Identity Platform 및 Google ID를 사용하여 Firestore에 사용자 인증하기


이 문서에서는 Identity Platform을 사용자 ID 및 액세스 관리 플랫폼으로 사용하여 Firestore 데이터베이스에 사용자 기반 액세스 제어를 설정하는 방법을 보여줍니다. Identity Platform을 사용하면 애플리케이션에 인증 레이어를 추가하여 고객의 사용자 인증 정보를 보호하고 관리할 수 있습니다. Firestore는 유연한 NoSQL 문서 중심의 데이터베이스입니다. Firestore 보안 규칙이라는 규칙 언어를 사용하여 데이터에 대한 액세스를 제어하므로 서버 측 승인 코드를 작성할 필요가 없습니다.

이 문서는 Firestore 보안 규칙과 함께 Firestore를 사용하고 Google과 같은 외부 로그인 제공업체를 통해 Identity Platform으로 사용자를 인증하려는 개발자와 보안 전문가를 대상으로 합니다. 이 문서의 코드에서는 Identity Platform 및 Firestore를 사용하는 두 가지 방법을 보여줍니다.

  • 자바스크립트를 사용하여 Identity Platform 및 Firestore API를 호출하는 REST API 호출. 이 접근 방식을 사용하면 웹 앱이 Identity Platform 요청을 생성하는 방법을 완전히 제어할 수 있습니다.
  • Identity Platform 클라이언트 SDK 및 Firestore SDK를 사용하여 Identity Platform에 대한 로그인 처리를 관리하고 Firestore를 쿼리하는 Identity Platform 클라이언트 SDK. SDK는 Identity Platform REST API를 통해 자바스크립트 래퍼 함수를 제공합니다. 이를 통해 HTTP 요청을 수동으로 생성하는 대신 자바스크립트 함수와 객체를 사용하여 Identity Platform을 호출할 수 있습니다.

Identity Platform 클라이언트 SDK와 Firebase 클라이언트 SDK는 동일한 SDK를 공유합니다. SDK는 Identity Platform의 모든 기능을 지원합니다. 이전 버전과의 호환성을 유지하기 위해 SDK는 Firebase 브랜드를 유지합니다.

아키텍처

다음 다이어그램은 이 문서에 설명된 논리 아키텍처를 보여줍니다.

논리 아키텍처 다이어그램

이 문서에서는 Identity Platform 및 Firestore 이외에 다음 구성요소를 사용하고 보여줍니다.

  • 웹 앱: 사용자가 Google ID를 사용하여 Identity Platform에 로그인할 수 있는 앱입니다. 그 다음 Firestore에 로그인한 사용자에 대한 정보를 쿼리합니다.
  • Google 로그인: 이 문서에서 사용되는 ID 공급업체입니다.
  • 인증 핸들러: Google에서 응답을 가져오고 Identity Platform으로 로그인을 수행한 후 결과를 다시 웹 앱으로 보내 로그인 프로세스를 완료하는 서비스 엔드포인트입니다.

목표

  • Google Cloud 프로젝트에 Identity Platform을 설정합니다.
  • Google을 Identity Platform의 로그인 공급업체로 추가합니다.
  • Firestore 보안 규칙을 사용하여 Firestore 데이터베이스에 대한 액세스를 제어합니다.
  • Identity Platform API 및 Identity Platform Client SDK를 사용하여 사용자를 웹 앱에 로그인하도록 합니다.
  • Firestore REST API와 Firestore 자바스크립트 클라이언트 SDK를 사용하여 클라이언트 측 웹 앱에서 Firestore에 안전하게 액세스합니다.

비용

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

시작하기 전에

  1. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

    기존 프로젝트를 선택할 경우 다음 조건을 충족하는 프로젝트를 선택해야 합니다.

    • Datastore가 사용 설정되지 않았습니다.
    • App Engine이 사용 설정되지 않았습니다.
    • 프로젝트에 기존의 보안 규칙이 있는 Firestore 데이터베이스를 포함하지 않습니다. 이 문서에 설명된 규칙을 사용하여 기존 규칙을 덮어씁니다.
  2. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  3. Identity Platform 사용 설정
    1. Google Cloud Console에서 Identity Platform Marketplace 페이지로 이동합니다.

      Identity Platform Marketplace 페이지로 이동

    2. Identity Platform 사용 설정을 클릭하고 작업이 완료될 때까지 기다립니다.
  4. Firestore 사용 설정:
    1. Google Cloud Console의 왼쪽에 있는 메뉴를 열고 Firestore를 선택합니다.
    2. Firestore 모드를 선택합니다. 기본 모드 선택을 클릭합니다.
    3. 데이터 저장 위치를 선택합니다. 사용자 위치와 가장 가까운 리전을 선택합니다. 데이터베이스 만들기를 클릭합니다.

이 문서에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

Identity Platform 구성하기

Identity Platform에서 사용자 인증을 사용 설정하려면 ID 공급업체를 추가해야 합니다.

Identity Platform에서 Google 공급업체를 구성하기 전에 Google OAuth 동의 화면을 구성해야 합니다. 사용자가 웹 앱에 처음 로그인하면 동의 화면이 표시됩니다. OAuth 동의 화면 구성에서 앱 이름, 앱 로고, 지원 이메일 주소와 같은 속성을 설정할 수 있습니다.

  1. Google Cloud Console에서 ID 공급업체 페이지로 이동합니다.

    ID 공급업체 페이지로 이동

  2. 공급업체 추가를 클릭합니다.
  3. Google을 선택하고 새 ID 공급업체 페이지에서 API 및 서비스 링크를 클릭합니다. 새 탭에서 사용자 인증 정보 페이지가 열립니다. 이전 탭을 닫지 마세요. 사용자 인증 정보 페이지에서 새 ID 공급업체 페이지에 복사해야 합니다.
  4. 사용자 인증 정보 페이지에서 사용자 인증 정보 웹 클라이언트(Google 서비스에서 자동 생성)를 클릭합니다.
  5. 클라이언트 ID 값을 복사하고 새 ID 공급업체 페이지를 표시하는 탭으로 이동하여 웹 클라이언트 ID 필드에 값을 붙여넣습니다. 이 단계를 반복하여 클라이언트 보안 비밀번호 값을 웹 클라이언트 보안 비밀번호 필드에 복사합니다.
  6. 새 ID 공급업체 페이지에서 저장을 클릭합니다.
  7. 사용자 인증 정보 페이지로 돌아가서 OAuth 동의 화면을 클릭합니다.
  8. 외부를 선택한 다음 만들기를 클릭합니다.
  9. OAuth 동의 화면 페이지에서 다음 정보를 추가합니다.
    • 애플리케이션 이름: Identity Platform Tutorial
    • 지원 이메일: 드롭다운 목록에서 이메일 주소를 선택합니다.
  10. 저장을 클릭합니다.
  11. 앱 등록 수정 페이지에서 다음 정보를 추가합니다.
    • 앱 이름: Identity Platform Tutorial
    • 사용자 지원 이메일: 드롭다운 목록에서 이메일 주소를 선택합니다.
    • 이메일 주소: 이메일 주소를 입력합니다.
  12. 저장 후 계속을 클릭합니다.
  13. 범위선택적 정보 페이지에서 저장 후 계속을 클릭합니다.
  14. 요약 페이지에서 대시보드로 돌아가기를 클릭합니다.

Firestore 구성하기

새로 만든 Firestore 데이터베이스는 현재 비어 있습니다. 또한 새로운 Firestore 데이터베이스에는 모든 사용자가 데이터베이스에서 읽기 작업을 수행할 수 있는 기본 보안 규칙 집합이 있습니다. 이러한 기본 규칙으로 인해 아무도 데이터베이스에 쓸 수 없습니다. 다음 단계에서는 데이터베이스에 데이터를 입력하고 보안 규칙을 업데이트하여 승인된 사용자의 읽기(쿼리) 요청을 제한합니다.

Firestore에서 테스트 데이터 만들기

이 절차에서는 웹 앱에서 Firestore 컬렉션을 쿼리하여 보안 규칙을 테스트합니다.

먼저 Firestore 보안 규칙 테스트에 도움이 되는 여러 문서를 만듭니다. 컬렉션 및 필드 이름은 대소문자를 구분합니다. 컬렉션과 필드에 소문자 이름을 사용하여 웹 앱이 Firestore에 쿼리를 보낼 때 실패하지 않도록 합니다.

  1. Google Cloud Console의 Firestore 페이지로 이동합니다.
  2. 컬렉션 시작을 클릭합니다.
  3. 컬렉션 ID 필드에 customers를 입력합니다.
  4. 다음 정보를 사용하여 문서를 만듭니다.

    문서 ID: bob@example.com

    필드 이름 필드 유형 필드 값
    name 문자열 Bob
    company 문자열
    
    ExampleOrganization
  5. 저장 후 다른 항목 추가를 클릭합니다.

  6. 필드 값 지우기를 클릭합니다.

  7. 다음 정보를 입력합니다.

  8. 문서 ID: 사용자 이메일 주소

    필드 이름 필드 유형 필드 값
    name 문자열 이름
    company 문자열 회사 이름
  9. 저장을 클릭합니다.

Firestore 보안 규칙 만들기

Firestore 보안 규칙은 사용자 인증 메타데이터, 수신 쿼리의 데이터, 데이터베이스의 기존 데이터를 사용하여 평가할 수 있습니다.

이 절차에서는 로그인한 사용자의 이메일 주소와 공급업체 이름을 기반으로 하는 Firestore의 보안 규칙을 만듭니다.

Firebase Console을 사용하여 Firestore 보안 규칙을 관리합니다.

  1. Firebase Console을 열고 프로젝트를 클릭합니다.
  2. 화면 왼쪽에서 Firestore를 클릭합니다.
  3. Firestore 페이지에서 규칙 탭을 클릭합니다.
  4. 보안 규칙이 적용된 Firestore 데이터베이스가 있는 기존 프로젝트를 사용하고 이 문서의 절차를 마친 후 프로젝트를 삭제하려는 경우 기존 보안 규칙을 기록해 둡니다.
  5. 기존 규칙을 다음 규칙으로 바꿉니다.

    rules_version = '2';
    service cloud.firestore {
     match /databases/{database}/documents {
      match /customers/{customerID} {
      allow read:
       if request.auth.uid != null
         && request.auth.token.firebase.sign_in_provider == "google.com"
         && request.auth.token.email == customerID
        }
      }
    }
    

    이 규칙은 customers 컬렉션에 대한 읽기 전용 권한을 부여합니다. 이 규칙은 Identity Platform에서 구성한 Google 로그인 제공업체를 통해 로그인했는지 확인합니다. 또한 이메일 주소와 일치하는 고객 ID가 있는 문서만 검색할 수 있도록 합니다.

  6. 게시를 클릭합니다.

테스트 환경 구성하기

Firestore 보안 규칙을 테스트하려면 사용자가 로그인해야 하는 웹 앱을 만드는 것부터 시작합니다. 웹 앱은 GitHub에서 사용할 수 있으며 Cloud Shell 환경으로 다운로드되어 애플리케이션을 테스트할 수 있습니다. 사용자가 로그인하면 웹 앱이 Firestore에서 문서를 읽고 콘텐츠를 표시합니다.

웹 앱 구성하기

  1. Identity Platform 공급업체 페이지로 이동

    Identity Platform 공급업체 페이지로 이동

  2. 페이지의 오른쪽에서 애플리케이션 설정 세부정보를 클릭합니다.
  3. apiKeyauthDomain 옆에 나열된 값을 클립보드에 복사한 다음 닫기를 클릭합니다.
  4. 페이지 상단의 Google Cloud를 클릭하고 프로젝트 정보 카드에서 프로젝트 ID를 복사합니다.
  5. Cloud Shell에서 열기를 클릭하여 Cloud Shell을 열고 GitHub 저장소를 클론한 다음 config.js 파일을 엽니다. Cloud Shell에서 열기 대화상자가 나타나면 확인을 클릭합니다.

    Cloud Shell에서 열기

  6. config.js 파일에서 자리표시자 [API_KEY], [AUTH_DOMAIN], [PROJECT_ID]를 이전 단계에서 복사한 값으로 바꿉니다. 코드는 이러한 값을 Identity Platform에 요청을 보낼 때 생성되는 메시지의 URL과 본문에 삽입합니다.

커스텀 인증 핸들러 등록하기

사용자가 웹 앱에 액세스하면 Google을 ID 공급업체로 로그인하도록 리디렉션됩니다. 사용자가 Google에 성공적으로 로그인하면 Google은 아키텍처 다이어그램에 표시된 것처럼 사용자 토큰을 사용한 리디렉션 (302) 응답을 인증 핸들러에 반환합니다. OAuth 2.0에서 제공업체가 토큰을 알 수 없는 대상으로 보내는 것을 방지하려면 제공업체의 구성에 모든 리디렉션 URL을 미리 등록해야 합니다. 이러한 제한이 발생하는 이유는 OAuth 2.0 웹사이트의 리디렉션 URL 등록 페이지에서 설명합니다.

이 단계에서는 승인된 리디렉션 URL 목록을 업데이트하여 이 문서에 사용된 인증 핸들러를 신뢰합니다.

URL 목록은 Google OAuth 2.0 클라이언트 구성의 일부이며, Identity Platform을 구성할 때 클라이언트 ID와 클라이언트 보안 비밀번호를 복사한 페이지입니다.

이 아키텍처에서는 다음과 같은 두 가지 다른 인증 핸들러를 사용합니다.

  • Identity Platform에서 호스팅하는 인증 핸들러
  • 웹 앱에서 호스팅하는 커스텀 인증 핸들러

Identity Platform에서 호스팅하는 인증 핸들러는 Google에서 관리하는 다음 엔드포인트를 통해 액세스할 수 있습니다. https://[YOUR_PROJECT_ID].firebaseapp.com/__/auth/handler

이 핸들러를 사용하기 위해 Google OAuth 2.0의 클라이언트 설정에서 승인된 URL 목록을 업데이트할 필요가 없습니다. 이 문서 앞부분에서 Identity Platform을 사용 설정하면 승인된 URL 목록에 URL이 자동으로 추가됩니다.

Identity Platform 클라이언트 SDK를 사용하는 경우 SDK에서 이 내장 인증 핸들러를 사용합니다. Identity Platform의 인증 핸들러에서는 SDK가 상태 객체를 핸들러와 교환하기 때문에 웹 앱에서 이 SDK를 사용해야 합니다. 예를 들어 SDK는 핸들러에 사용자가 Identity Platform에 성공적으로 로그인한 후 사용자를 리디렉션할 위치를 알려줍니다.

웹 앱에서 호스팅되는 커스텀 인증 핸들러의 경우 자바스크립트에서 직접 Identity Platform REST API를 사용할 때 SDK가 아닌 자체 인증 핸들러를 구현하고 호스팅하는 것이 좋습니다.

이 문서에서는 Google에서 사용자 토큰을 받으면 Identity Platform의 로그인 프로세스를 관리하는 샘플 인증 핸들러를 설명합니다. Google OAuth 2.0 클라이언트 설정에서 승인된 URL 목록에 커스텀 핸들러의 URL을 추가해야 합니다.

이 문서에서는 Cloud Shell에서 웹 앱을 실행합니다. 웹 앱을 시작한 후 Cloud Shell 인스턴스의 호스트 이름을 찾고 이에 따라 제공업체의 구성을 업데이트합니다.

  1. Cloud Shell에서 웹 앱을 실행합니다.

    npm install
    node app.js
    
  2. 다음 출력이 나타날 때까지 기다리세요. Example app listening on port 8080!

  3. 웹 미리보기 아이콘을 클릭한 다음 포트 8080에서 미리보기를 클릭합니다. 새 탭에 웹페이지가 나타날 때까지 기다린 후 인증 핸들러 URL(Google OAuth 2.0 클라이언트용) 아래에 값을 복사합니다.

  4. 사용자 인증 정보 페이지로 이동합니다.
    사용자 인증 정보 페이지로 이동

  5. 사용자 인증 정보 페이지에서 웹 클라이언트(Google 서비스에서 자동 생성)를 클릭합니다.

  6. 승인된 리디렉션 URI 페이지에서 URI 추가를 클릭하고 이전에 복사한 URL을 붙여넣습니다.

  7. 저장을 클릭하고 웹 앱을 계속 실행합니다.

웹 앱 도메인 승인하기

Identity Platform 인증 핸들러를 사용하면 핸들러가 사용자 정보 및 토큰과 함께 사용자를 다시 웹 앱으로 리디렉션합니다. 승인되지 않은 도메인으로 정보를 전송하지 않도록 하려면 웹 앱이 실행되는 도메인을 승인해야 합니다.

  1. 웹 앱의 기본 웹페이지로 돌아가서 호스트 이름(Identity Platform 승인 도메인) 아래에 값을 복사합니다.
  2. Identity Platform 설정 페이지로 이동합니다.

    Identity Platform 설정 페이지로 이동

  3. 보안 탭을 클릭한 다음 도메인 추가를 클릭합니다.
  4. 복사한 도메인을 붙여넣고 추가를 클릭한 다음 저장을 클릭합니다.
  5. 웹 앱은 Cloud Shell에서 계속 실행합니다. 다음 작업을 할 때 필요합니다.

Google ID를 사용하여 Identity Platform에 로그인하기

다음 다이어그램은 이 가이드의 시작 부분에서 대략적인 아키텍처 다이어그램을 확장합니다. 이 다이어그램은 이벤트의 시간순 흐름을 보여주기 위해 이 문서에서 설명하는 인증 프로세스의 세부정보를 다룹니다. 이벤트는 사용자가 로그인 버튼을 클릭하는 것으로 시작하고, 사용자 ID를 사용하여 Firestore에서 데이터를 검색하는 웹 앱으로 끝납니다.

대략적인 아키텍처

  1. 웹 앱 사용자는 웹 앱에서 Google 계정으로 로그인을 클릭합니다.
  2. 웹 앱은 선택된 ID 공급업체(이 경우 Google)의 로그인 URL에 대해 Identity Platform을 쿼리합니다.
  3. 웹 앱이 사용자를 인증 핸들러를 가리키는 콜백 URL과 함께 ID 공급업체의 로그인 페이지로 리디렉션합니다.
  4. 공급업체 로그인 페이지에서 사용자는 사용자 인증 정보를 입력하고 웹 앱에서 요청한 승인에 동의합니다.
  5. 사용자가 성공적으로 로그인하면 공급업체에서 토큰을 생성하고 이전에 제공된 콜백 URL로 리디렉션을 전송합니다.
  6. 인증 핸들러가 Google에서 발급한 토큰을 수신하고 Identity Platform으로 보내 사용자를 로그인 처리합니다. Identity Platform은 토큰을 검증하고 사용자를 로그인 처리하며 Identity Platform에서 발급한 ID 토큰과 갱신 토큰을 사용자 정보와 함께 반환합니다. Identity Platform이 처음으로 사용자를 로그인시키면 데이터베이스에 일치하는 사용자 프로필이 생성됩니다. Google에서 제공하는 계정 정보는 사용자의 프로필을 채우는 데 사용됩니다.
  7. Identity Platform에 로그인한 후 핸들러는 Identity Platform에서 받은 새 토큰과 함께 웹 앱으로 다시 리디렉션합니다.
  8. Firestore로 요청을 보내기 위해 웹 앱은 모든 Firestore 요청에 사용자의 ID 토큰을 연결합니다. Firestore 보안 규칙은 ID 토큰이 없는 요청을 익명 요청으로 취급하므로 거부됩니다.
  9. Identity Platform에서 발급한 ID 토큰은 1시간 후에 만료됩니다. ID 토큰이 만료되면 웹 앱이 캐시된 갱신 토큰을 사용하여 Identity Platform에서 새 ID 토큰을 검색합니다.

샘플 웹 앱은 두 가지 방법으로 Identity Platform 및 Firestore와 상호작용하는 방법을 보여줍니다.

첫 번째 방법은 Identity Platform REST API를 사용하는 것입니다.

  • 이 기법은 자바스크립트를 사용하여 Identity Platform REST API를 호출하는 커스텀 코드를 사용합니다.
  • API 호출은 site/identity-platform-auth-helper.js 파일에서 구현됩니다. 인증 핸들러는 views/auth-handler.ejs 파일에서 구현됩니다.
  • 도우미와 핸들러가 상태 객체를 교환하여 성공적으로 로그인한 후 웹 앱으로 다시 리디렉션하도록 사용 설정합니다.

두 번째 방법은 Identity Platform Client SDK를 사용하는 것입니다.

  • 이 기법을 사용하면 SDK가 로그인 프로세스를 처리합니다.
  • SDK는 모든 필수 API 호출을 구현하고 개발자가 시작할 로그인 과정을 제어하는 함수 집합을 노출합니다.

Identity Platform REST API를 사용하여 로그인하기

Google이 ID 공급업체로써 로그인 과정을 제어하는 두 가지 기본 API 호출이 있습니다.

Google이 공급업체로써 로그인 과정을 제어하는 두 가지 기본 API 호출

  • 공급업체 URL 및 식별자를 가져옵니다. accounts.createAuthUri 메서드는 지정된 ID 공급업체의 승인 URL을 반환합니다. 그러면 웹 앱이 반환된 승인 URL로 이동하여 선택된 ID 공급업체(예: Google)로 로그인 프로세스를 시작합니다.

    다음 코드 스니펫은 이 API를 호출하는 방법을 보여줍니다.

    IdentityPlatformAuthHelper.prototype.createAuthUri = function(providerId, tenantId) {
      // https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/createAuthUri
      const createAuthUriUrl = `${this.identityPlatformBaseUrl}/accounts:createAuthUri?key=${config.apiKey}`;
      const request = {
        'providerId' : providerId,
        'tenantId' : tenantId,
        'continueUri' : this.authHandlerUrl,
      };
    
      return fetch(
          createAuthUriUrl,
          {
            contentType: 'application/json',
            method: 'POST',
            body: JSON.stringify(request)
          }
        )
      .then(response => response.json())
      .then(data => {
        return {
          "authUri" : data.authUri,
          "sessionId" : data.sessionId
        };
      })
      .catch(error => {
        console.error(error);
      });
    };
  • Google에서 발급한 토큰을 사용하여 Identity Platform에 로그인합니다. accounts.signInWithIdp 메서드는 사용자가 ID 공급업체의 승인 응답을 사용하여 Identity Platform에 로그인합니다. API는 Identity Platform에서 발급한 새 토큰으로 이 요청에 응답합니다. 웹 앱은 ID 공급업체로부터 성공적인 승인 응답을 받은 후 이 API를 호출합니다. 다음 코드 스니펫은 이 API를 호출하는 방법을 보여줍니다.

    IdentityPlatformAuthHelper.prototype.signInWithIdp = function(data) {
      authState = this.getAuthState();
      this.authHandlerUrl = authState.authHandlerUrl;
    
      // https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/signInWithIdp
      const signInWithIdpUrl = `${this.identityPlatformBaseUrl}/accounts:signInWithIdp?key=${config.apiKey}`;
    
      const request = {
          'requestUri' : this.authHandlerUrl,
          'sessionId' : authState.sessionId,
          'returnRefreshToken' : true,
          'returnSecureToken' : true,
          'tenantId' : authState.tenantId
        };
    
      if (authState.providerId == 'google.com' || authState.providerId.startsWith('saml.')) {
        request.postBody = `${data}&providerId=${authState.providerId}`;
      } else {
        throw new Error('This sample script only supports the google.com and SAML providers for Identity Platform');
      }
    
      fetch(
          signInWithIdpUrl,
          {
            contentType: 'application/json',
            method: 'POST',
            body: JSON.stringify(request)
          }
        )
      .then(response => response.json())
      .then(data => {
        this.user = data;
        this.signedInHandler(this.user);
      })
      .catch(error => {
        console.error(error);
      });
    }

    postBody 필드 값의 형식은 선택한 ID 공급업체 및 여기에서 사용하는 승인 프로토콜에 따라 다릅니다. 코드는 OpenID Connect (OIDC) 토큰으로 Google ID 공급업체를 처리하고 제공된 SAML 응답으로 SAML 기반 ID 공급업체를 처리합니다. OAuth 2.0 또는 OAuth 1.0 액세스 토큰과 같은 다른 유형의 승인 토큰을 사용하는 경우 제공업체의 API 문서를 참조하세요.

사용자가 웹 앱에 로그인하면 웹 앱에서 Firestore로 쿼리를 보낼 수 있습니다.

웹 앱에서 Firestore로 쿼리를 전송할 때의 이벤트 흐름

코드가 Firestore REST API에 대한 요청을 트리거하기 전에 Identity Platform에서 발급하고 서명한 ID 토큰을 요청에 추가합니다. 다음 코드 스니펫은 요청을 만드는 방법을 보여줍니다.

function showCustomerInformation(userEmail) {
  $('#customer-information').show();
  $('#output').empty();

  const idTokenPromise = authHelper.getIdToken();
  const firestoreEndpoint = 'https://firestore.googleapis.com/v1';
  const defaultDbPath = `projects/${config.projectId}/databases/(default)/documents`;
  const collectionId = 'customers';

  // Call Firestore via its REST API and authenticate with the user's ID token
  idTokenPromise
  .then(idToken => {
    console.log(`JWT Token: ${idToken}`);
    return fetch(
      `${firestoreEndpoint}/${defaultDbPath}/${collectionId}/${userEmail}`,
      {
        headers: {
          'Authorization': 'Bearer ' + idToken
        },
        contentType: 'application/json',
        method: 'GET'
      })
  })
  .then(response => response.json())
  .then(data => {
      if (data.error) {
        throw data.error.message;
      }
      var fields = data.fields;
      $('#output').append($('<p>').text(`Id: ${userEmail}`));
      $('#output').append($('<p>').text(`Name: ${fields.name.stringValue}`));
      $('#output').append($('<p>').text(`Company: ${fields.company.stringValue}`));
      $('#output').append($('<p>').text(`Doc path: ${data.name}`));
      $('#output').append($('<p>').text(`Doc URL: ${firestoreEndpoint}/${data.name}`));
  })
  .catch(error => {
    console.error(error);
    $('#output').text("Error: " + JSON.stringify(error));
  });
}

IdentityPlatformAuthHelper.getIdToken() 함수는 브라우저에서 캐시된 토큰을 검색하여 JSON 웹 토큰(JWT) 형식으로 유효한 ID 토큰을 반환합니다. 토큰이 이미 만료된 경우 함수는 Identity Platform API를 호출하여 새 ID 토큰에 대한 갱신 토큰을 교환하여 토큰을 갱신합니다.

다음 스니펫은 기존 ID 토큰이 유효한지 만료되었는지 확인하는 방법과 Identity Platform을 호출하여 필요한 경우 갱신하는 방법을 보여줍니다.

IdentityPlatformAuthHelper.prototype.getIdToken = function() {
  const token = this.jwtDecode(this.user.idToken);

  // If exp has passed, refresh the token
  if (Date.now() > token.payload.exp * 1000) {
    return this.refreshToken(this.user.refreshToken);
  }
  return Promise.resolve(this.user.idToken);
}

IdentityPlatformAuthHelper.prototype.jwtDecode = function(t) {
  const token = {};
  token.raw = t;
  token.header = JSON.parse(window.atob(t.split('.')[0]));
  token.payload = JSON.parse(window.atob(t.split('.')[1]));
  return token;
}

IdentityPlatformAuthHelper.prototype.refreshToken = function(refreshToken) {
  // https://cloud.google.com/identity-platform/docs/reference/rest/client#section-refresh-token
  const tokenUrl = `https://securetoken.googleapis.com/v1/token?key=${config.apiKey}`;
  const requestBody = new URLSearchParams(`grant_type=refresh_token&refresh_token=${refreshToken}`);

  return fetch(
      tokenUrl,
      {
        contentType: 'application/x-www-form-urlencoded',
        method: 'POST',
        body: requestBody
      }
    )
  .then(response => response.json())
  .then(data => {
    this.user.idToken = data.id_token;
    this.user.refreshToken = data.refresh_token;
    return this.user.idToken;
  })
  .catch(error => {
    console.error(error);
  });
}

Google ID로 Identity Platform에 로그인하려면 다음 단계를 따르세요.

  1. 웹 앱의 기본 페이지가 표시된 탭으로 돌아갑니다. 이미 이 탭을 닫았다면 Cloud Shell 페이지로 돌아가서 웹 미리보기를 클릭한 다음 포트 8080에서 미리보기를 클릭합니다. 새 탭에 웹페이지가 표시될 때까지 기다립니다.
  2. customer-info-with-api.html 페이지를 표시하도록 브라우저에서 주소를 변경합니다. 새 URL의 형식은 https://random_prefix-devshell.appspot.com/customer-info-with-api.html입니다.
  3. Google 계정으로 로그인을 클릭하고 사용자 인증 정보로 로그인합니다. 로그인하면 이메일 주소가 포함된 텍스트 상자가 나타납니다.

    JWT를 디코딩하여 Identity Platform과 Google에서 제공하는 사용자 정보를 확인하려면 다음을 수행합니다. 또는 다음 단계로 이동합니다.

    사용자 정보는 JWT의 페이로드(두 번째) 부분에 있으며 base64로 인코딩됩니다. JWT의 두 번째 부분을 디코딩하고 jq를 사용하여 정보를 JSON 파일에 인쇄하려면 Cloud Shell에서 다음 명령어를 실행합니다.

    token=[PASTE_JWT_STRING_HERE]
    echo $token | awk '{split($0, a, "."); print a[2]; }' | base64 -d | jq
    

    다른 문서의 Firestore 쿼리를 계속 진행합니다.

  4. 고객 정보 받기를 클릭합니다. 이름과 회사 이름은 Firestore 데이터베이스에 입력할 때 표시됩니다.

  5. 이메일 주소를 bob@example.com으로 변경한 다음 Google 계정으로 로그인을 클릭합니다. 이번 응답에는 다음과 같은 오류 메시지가 표시됩니다.

    Error: "Missing or insufficient permissions." The security rule you added to Firestore limits your access to documents with an ID that matches your email address, as it appears in the token that Identity Platform created.

  6. 웹페이지를 닫지 마세요. 다음 절차에서 사용합니다.

Identity Platform Client SDK를 사용하여 로그인

Identity Platform에 수동으로 요청을 작성하는 대신 Identity Platform Client SDK를 사용할 수 있습니다. SDK는 로그인 프로세스를 관리하며, 어떤 공급업체를 사용할지, 리디렉션 또는 팝업을 사용할지와 같은 로그인 과정을 제어하는 기능을 제공합니다.

Identity Platform 클라이언트 SDK를 사용하려면 HTML 페이지에 여러 스크립트 파일을 포함해야 합니다. 다음 스니펫은 필요한 스크립트를 보여줍니다.

<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-firestore.js"></script>

다음 스니펫은 SDK를 사용하여 Google 공급업체에 로그인하는 방법을 보여줍니다.

$('#sign-in').click((event) => {
  provider = new firebase.auth.GoogleAuthProvider();
  //firebase.auth().signInWithPopup(provider)
  firebase.auth().signInWithRedirect(provider)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });
});

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    const welcomeName = user.displayName ? user.displayName : user.email;
    console.log(firebase.auth().currentUser);
    $('#user').text(welcomeName);
    $('#logged-in').show();
    $('#email').val(firebase.auth().currentUser.email);
  } else {
    $('#logged-in').hide();
    $('#logged-out').show();
    $('#email').val('');
  }
  $('#customer-information').hide();
});

$('#sign-out').click(function(event) {
  firebase.auth().signOut().then(function() {
    console.log('Sign out successful');
  }, function(error) {
    console.error(error);
  });
});

firebase.auth().signInWithRedirect() 함수는 사용자를 공급업체의 로그인 페이지로 리디렉션하여 동일한 브라우저 창에서 로그인 프로세스를 시작합니다. GoogleAuthProvider를 사용하면 함수가 Google 로그인 과정을 시작하도록 지시합니다.

대신 signInWithPopup 함수를 호출하여 리디렉션 동작을 팝업 동작으로 대체할 수 있습니다.

다른 인증 공급업체를 사용하려면 firebase.auth.AuthProvider 인터페이스를 구현하는 모든 유형을 추가합니다. 모든 필수 매개변수가 포함되어 있는지 확인하려면 선택한 공급업체의 문서를 따르세요.

firebase.auth().onAuthStateChanged 함수는 로그인 및 로그아웃 시 트리거되는 관찰자입니다. 로그인하면 웹 앱 코드가 사용자 객체에서 가져온 정보로 웹페이지를 채우고 로그인 버튼을 숨깁니다. 로그아웃 시 코드는 웹페이지를 지우고 로그인 버튼을 다시 표시합니다.

Identity Platform Client SDK는 Firestore SDK와 통합됩니다. 모든 쿼리에서 Firestore SDK는 유효한 ID 토큰을 Identity Platform Client SDK에서 가져와서 연결합니다. Identity Platform Client SDK는 ID 토큰이 만료될 때 ID 토큰 새로고침을 담당합니다.

다음 코드 스니펫은 Firestore SDK를 사용하여 Firestore를 쿼리하는 방법을 보여줍니다.

function showCustomerInformation(userEmail) {
  $('#customer-information').show();
  $('#output').empty();

  const db = firebase.firestore();
  const collectionId = 'customers';

  query = db.collection(collectionId).doc(userEmail).get();
  query.then((doc) => {
    var fields = doc.data();
    $('#output').append($('<p>').text(`Id: ${doc.id}`));
    $('#output').append($('<p>').text(`Name: ${fields.name}`));
    $('#output').append($('<p>').text(`Company: ${fields.company}`));
  }).catch((error) => {
    console.error(error);
    $('#output').text("Error: " + error.toString());
  });
}

쿼리에 ID 토큰을 추가하기 위해 코드를 작성할 필요가 없습니다. Firestore SDK 및 Identity Platform 클라이언트 SDK는 인증 프로세스를 처리합니다.

Google ID를 사용하여 Identity Platform에 로그인하고 Firestore를 쿼리하려면 다음 단계를 따르세요.

  1. 해당하는 웹 앱 탭을 이미 닫은 경우 Cloud Shell 페이지로 돌아가서 웹 미리보기를 클릭한 다음 포트 8080에서 미리보기를 클릭합니다. 새 탭에 웹페이지가 표시될 때까지 기다립니다.
  2. customer-info-with-sdk.html 페이지를 표시하도록 브라우저의 주소를 변경합니다. 새 URL의 형식은 https://random_prefix-devshell.appspot.com/customer-info-with-sdk.html입니다.
  3. Google 계정으로 로그인을 클릭하고 사용자 인증 정보로 로그인합니다. 로그인하면 이메일 주소가 포함된 텍스트 상자가 나타납니다.
  4. 고객 정보 받기를 클릭합니다. 이름과 회사 이름은 Firestore 데이터베이스에 입력할 때 표시됩니다.
  5. 이메일 주소를 bob@example.com으로 변경한 다음 Google 계정으로 로그인을 클릭합니다. 이번 응답은 오류 메시지입니다.

    Error: FirebaseError: [code=permission-denied]: Missing or insufficient Permissions.

    자바스크립트를 사용하여 Firestore를 쿼리하는 방법에 관한 자세한 내용은 Firestore 문서를 참조하세요.

웹 앱 문제 해결

웹 앱을 실행할 때 다음 문제가 발생할 경우 유용하게 활용할 수 있는 문제 해결 단계에 대해 알아봅니다.

웹 앱 탐색 오류

오류 참고
다음 오류 메시지 중 하나가 표시됩니다.

Error:Could not connect to Cloud Shell on port 8080

Error:No active Cloud Shell
커스텀 인증 핸들러 등록하기에 설명된 대로 Cloud Shell이 열려 있고 웹 앱이 실행되는지 확인합니다. 새 Cloud Shell 세션을 열고 웹 앱을 실행하기 전에 작업 디렉터리를 웹 앱의 디렉터리로 변경합니다.

cd "$HOME"/cloudshell_open/securing-cloud-firestore-with-identity-platform

웹 앱을 실행한 후 다음 출력이 표시되는지 확인합니다.

Example app listening on port 8080!

Google 로그인 오류

오류 참고
Google 계정으로 로그인 을 클릭해도 아무 반응이 없고, 다음 오류 코드가 표시됩니다.

Cannot GET /undefined
웹 애플리케이션 구성하기에 설명된 대로 config.js 파일에 apiKeyauthDomain 변수를 설정했는지 확인합니다.
Google 로그인 페이지에 다음 오류 메시지가 표시됩니다.

Authorization Error - Error 400: redirect_uri_mismatch
Google로 전송된 리디렉션 URL이 OAuth 클라이언트에 승인된 URL 목록과 일치하지 않습니다. 커스텀 인증 핸들러 등록하기에 설명된 대로 승인된 리디렉션 URI를 구성했는지 확인합니다.
Identity Platform SDK로 Google에 로그인하면 다음과 같은 오류 메시지가 표시됩니다.

This domain (***) is not authorized to run this operation. Add it to the OAuth redirect domains list in the Firebase console -> Auth section -> Sign-in method tab
이 오류는 Cloud Shell에서 사용하는 도메인이 Identity Platform의 허용된 도메인 목록에 없을 때 나타날 수 있습니다. 도메인 이름이 도메인 목록에 추가되었는지 확인하려면 웹 앱 도메인 승인하기의 단계를 따릅니다.

고객 정보를 가져오는 중에 오류 발생

오류 참고
다음 오류 메시지 중 하나가 표시됩니다.

Error: FirebaseError: [code=permission-denied]: Missing or insufficient permissions

Error: Missing or insufficient permissions
Firestore 보안 규칙에서 Firestore로 전송된 토큰의 유효성을 검사하지 못했을 수 있습니다. Firestore 보안 규칙 만들기에 설명된 대로 Firestore 보안 규칙이 올바르게 구성되었는지 확인합니다.
다음 오류 메시지 중 하나가 표시됩니다.

Error: FirebaseError: [code=unavailable]: Failed to get document because the client is offline

Error: "The project *** does not exist or it does not contain an active Datastore or Firestore database

Error: "Project id [PROJECT_ID] is malformed: it either contains invalid characters or is too long
웹 애플리케이션 구성하기에 설명된 대로 config.js 파일에 projectId 속성을 설정했는지 확인합니다.

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

  1. Google Cloud 콘솔에서 리소스 관리 페이지로 이동합니다.

    리소스 관리로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 후 종료를 클릭하여 프로젝트를 삭제합니다.

개별 리소스 삭제

  1. Cloud Shell에서 Ctrl+C를 눌러 웹 앱을 중지합니다.
  2. 클론된 저장소 디렉터리를 삭제합니다.

    rm -rf "$HOME"/cloudshell_open/securing-cloud-firestore-with-identity-platform
    
  3. Identity Platform 설정 페이지로 이동합니다.

    Identity Platform 설정 페이지로 이동

  4. 보안 탭을 클릭하고 이전에 추가한 도메인을 삭제한 다음 저장을 클릭합니다.

  5. ID 공급업체 페이지로 이동

    ID 공급업체 페이지로 이동

  6. 이전에 추가한 Google 공급업체를 삭제합니다.

  7. API 및 서비스로 이동한 다음 사용자 인증 정보 페이지로 이동합니다.

    사용자 인증 정보 페이지로 이동

  8. 사용자 인증 정보 페이지에서 웹 클라이언트(Google 서비스에서 자동 생성)를 클릭합니다.

  9. 승인된 리디렉션 URI 페이지에서 이전에 붙여넣은 URL을 삭제한 후 저장을 클릭합니다.

  10. Firebase Console을 열고 Console 탐색창에서 프로젝트를 클릭합니다.

  11. 왼쪽의 개발 메뉴에서 데이터베이스 옵션을 선택합니다.

  12. 데이터베이스 페이지에서 규칙 탭을 클릭합니다.

  13. 이 문서를 시작하기 전에 사용했던 보안 규칙으로 현재 보안 규칙을 덮어쓰고 게시를 클릭합니다.

  14. Google Cloud Console의 Firestore 페이지로 이동합니다.

  15. 고객 컬렉션 왼쪽의 메뉴를 클릭한 후 컬렉션 삭제를 클릭합니다.

  16. 삭제 확인 팝업의 컬렉션 ID 필드에 customers를 입력한 다음 삭제를 클릭합니다.

다음 단계

Google Cloud에 대한 참조 아키텍처, 다이어그램, 권장사항 살펴보기 Cloud 아키텍처 센터 살펴보기