Cloud Run으로 로그인 페이지 호스팅

IAP(Identity-Aware Proxy)로 외부 ID를 사용하려면 앱에 로그인 페이지가 필요합니다. IAP는 사용자를 보안 리소스에 액세스하기 전에 이 페이지로 리디렉션하여 인증합니다.

이 문서에서는 Cloud Run을 사용하여 사전 빌드된 로그인 페이지를 배포하고 맞춤설정하는 방법을 보여줍니다. 외부 ID를 시작하는 가장 빠른 방법이며 코드를 작성할 필요가 없습니다.

로그인 페이지를 직접 만들 수도 있습니다. 자체 페이지를 빌드하는 것이 더 복잡하지만 인증 흐름 및 사용자 환경을 보다 세밀하게 제어할 수 있습니다. 자세한 내용은 FirebaseUI로 로그인 페이지 만들기커스텀 로그인 페이지 만들기를 참조하세요.

로그인 페이지 제한사항

프로젝트에 이메일 열거 보호가 사용 설정된 경우 사전 빌드된 로그인 페이지를 사용할 수 없습니다.

프로젝트에 이메일 열거 보호가 사용 설정된 경우 이 문서의 절차를 계속하기 전에 email-enumeration-protection을 사용 중지합니다.

시작하기 전에

  • Compute Engine API를 사용 설정합니다.

    Compute Engine API 사용 설정

  • 외부 ID를 사용 설정하고 설정 중에 자동으로 로그인 페이지 만들기 옵션을 선택합니다. 그러면 Cloud Run 및 FirebaseUI에서 로그인 페이지를 자동으로 만들어 줍니다.

  • Cloud Run에 사용된 서비스 계정인 PROJECT_NUMBER-compute@developer.gserviceaccount.com에 다음 사전 정의된 역할이 있는지 확인합니다.

    • roles/identitytoolkit.viewer
    • roles/iap.settingsAdmin
    • roles/compute.networkViewer

호스팅된 페이지의 리디렉션 URL 설정

호스팅된 로그인 페이지는 Firebase 인증 도메인으로 자체 도메인을 사용하여 모든 환경에서 리디렉션을 통한 로그인이 올바르게 작동하도록 합니다. 호스팅된 페이지의 URL을 제공업체 구성에서 승인된 리디렉션 URL로 추가해야 합니다.

호스팅된 로그인 페이지의 URL을 승인된 리디렉션 URL로 추가하려면 다음을 수행합니다.

  1. 애플리케이션을 선택한 후 로그인 URL을 복사합니다.

  2. Google Cloud 콘솔에서 사용자 인증 정보 페이지로 이동합니다.

    사용자 인증 정보로 이동

  3. LOGIN_URL/__/auth/handler를 앱의 OAuth 2.0 클라이언트에 대해 승인된 리디렉션 URI 중 하나로 추가합니다. 공급업체 구성 시 사용한 것과 동일한 OAuth 클라이언트 ID 및 보안 비밀 키를 선택합니다.

  4. 앱에서 다른 SAML 및 OIDC 제공업체를 사용하는 경우 LOGIN_URL/__/auth/handler를 승인된 리디렉션 URI 또는 ACS URL로 추가합니다.

또는 팝업 로그인 과정에서 로그인 페이지를 맞춤설정하여 LOGIN_URL/__/auth/handler 대신 PROJECT_ID.firebaseapp.com을 인증 도메인으로 사용할 수 있습니다.

페이지의 팝업 로그인 과정을 구현하려면 다음 단계를 따르세요.

  1. 페이지 맞춤설정을 클릭합니다.

  2. JSON 형식의 구성 파일에서 authDomainPROJECT_ID.firebaseapp.com로, signInFlowpopup로 설정합니다.

  3. 구성을 저장하려면 저장을 클릭합니다.

다음을 바꿉니다.

  • LOGIN_URL: 호스팅된 로그인 페이지의 도메인입니다.
  • PROJECT_ID: Firebase 프로젝트 ID입니다.

다음은 팝업 로그인 과정 구성의 예시입니다.

{
  "AIzaSyC5DtmRUR...": {
    "authDomain": "example.firebaseapp.com",
    "displayMode": "optionFirst",
    ...
    ...
    "tenants": {
      "tenant-a-id": {
        "fullLabel": "Company A Portal",
        "displayName": "Company A",
        ...
        ...
        "signInFlow": "popup",
        ...
        ...
      }
    }
  }
}

리디렉션 권장사항 및 스토리지 파티셔닝에 대한 자세한 내용은 서드 파티 스토리지 액세스를 차단하는 브라우저에서 signInWithRedirect를 사용하기 위한 권장사항을 참조하세요.

로그인 페이지 테스트

IAP에서 생성되는 초기 로그인 페이지는 정상적으로 작동합니다. 테스트하려면 다음 안내를 따르세요.

  1. IAP로 보호되는 리소스로 이동합니다. 로그인 페이지로 자동 리디렉션되어야 합니다.

  2. 로그인할 테넌트 및 공급업체를 선택합니다. 테넌트 또는 공급업체가 나열되지 않으면 Identity Platform을 사용하여 구성했는지 확인하세요.

  3. 사용자 인증 정보로 로그인합니다.

보호된 리소스로 리디렉션되어야 합니다.

로그인 페이지 맞춤설정

JSON 구성 파일을 사용하여 로그인 페이지를 맞춤설정할 수 있습니다. 다음은 몇 가지 옵션입니다.

  • 로그인 페이지에 헤더와 로고 추가
  • 사용 가능한 테넌트 및 공급업체 지정
  • 각 테넌트 및 공급업체 버튼의 아이콘 및 스타일 맞춤설정
  • 앱의 개인정보처리방침 및 서비스 약관에 대한 링크 추가

다음 섹션에서는 JSON 구성 파일에 액세스하고 업데이트하는 방법을 설명합니다.

액세스 토큰 가져오기

로그인 페이지를 관리하려면 Google 액세스 토큰이 필요합니다. Google 액세스 토큰을 얻는 가장 쉬운 방법은 Google을 Identity Platform의 제공업체로 사용 설정하는 것입니다. 앱에서 이미 Google을 ID 공급업체로 사용하고 있다면 이 섹션을 건너뛸 수 있습니다.

  1. Google Cloud 콘솔에서 Identity Platform 공급업체 페이지로 이동합니다.

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

  2. 제공자 추가를 클릭합니다.

  3. 공급업체 목록에서 Google을 선택합니다.

  4. 웹 클라이언트 ID웹 클라이언트 보안 비밀번호를 구성합니다.

    1. 별도의 탭에서 IAP 페이지를 엽니다.

      IAP 페이지로 이동

    2. 리소스의 더보기 메뉴를 확장한 후 OAuth 클라이언트 수정을 클릭합니다.

    3. 클라이언트 ID클라이언트 보안 비밀번호 필드를 Identity Platform의 Google 공급업체 구성에 복사합니다.

    4. OAuth 클라이언트의 승인된 리디렉션 URI 목록에 Identity Platform 리디렉션 URL을 추가합니다. URL의 형식은 https://PROJECT_ID.firebaseapp.com/__/auth/handler입니다.

  5. 두 페이지 모두 저장을 클릭합니다.

관리자 패널에 로그인

Cloud Run에서 호스팅하는 로그인 페이지의 JSON 구성입니다. 다음 단계는 패널에 액세스하는 방법을 보여줍니다. 스토리지 관리자(roles/storage.admin) 역할이 필요합니다.

  1. Google Cloud 콘솔에서 IAP 페이지로 이동합니다.

    IAP 페이지로 이동

  2. 목록에서 리소스를 선택합니다.

  3. 정보 패널의 페이지 맞춤설정 아래에 나열된 URL을 실행합니다. 예를 들면 다음과 같습니다. https://servicename-xyz-uc.a.run.app/admin

  4. IAP를 구성할 때 사용한 것과 동일한 Google 계정으로 로그인합니다. JSON 구성 파일이 포함된 텍스트 편집기가 표시됩니다.

구성 수정

로그인 페이지의 구성 스키마는 FirebaseUI를 기반으로 하며 많은 속성을 상속합니다. 다음 코드는 세 개의 테넌트가 있는 구성 예시를 보여줍니다.

{
  "AIzaSyC5DtmRUR...": {
    "authDomain": "awesomeco.firebaseapp.com",
    "displayMode": "optionFirst",
    "selectTenantUiTitle": "Awesome Company Portal",
    "selectTenantUiLogo": "https://awesome.com/abcd/logo.png",
    "styleUrl": "https://awesome.com/abcd/overrides/stylesheet.css",
    "tosUrl": "https://awesome.com/abcd/tos.html",
    "privacyPolicyUrl": "https://awesome.com/abcd/privacypolicy.html",
    "tenants": {
      "tenant-a-id": {
        "fullLabel": "Company A Portal",
        "displayName": "Company A",
        "iconUrl": "https://companya.com/img/icon.png",
        "logoUrl": "https://companya.com/img/logo.png",
        "buttonColor": "#007bff",
        "signInOptions": [
          {
            "provider": "password",
            "requireDisplayName": false,
            "disableSignUp": {
              "status": true,
              "adminEmail": "admin@example.com",
              "helpLink": "https://www.example.com/trouble_signing_in"
            }
          },
          "facebook.com",
          "google.com",
          "microsoft.com",
          {
            "provider": "saml.okta-cicp-app",
            "providerName": "Corp Account",
            "fullLabel": "Employee Corporate Login",
            "buttonColor": "#ff0000",
            "iconUrl": "https://companya.com/abcd/icon-1.png"
          },
          {
            "provider": "oidc.okta-oidc",
            "providerName": "Contractor Account",
            "fullLabel": "Contractor Account Portal",
            "buttonColor": "#00ff00",
            "iconUrl": "https://companya.com/abcd/icon-2.png"
          }
        ],
        "tosUrl": "https://companya.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companya.com/abcd/privacypolicy.html"
      },
      "tenant-b-id": {
        "fullLabel": "Company B Portal",
        "displayName": "Company B",
        "iconUrl": "https://companyb.com/img/icon.png",
        "logoUrl": "https://companyb.com/img/logo.png",
        "buttonColor": "#007bff",
        "immediateFederatedRedirect": true,
        "signInOptions": [
          {
            "provider": "saml.okta-bla-app",
            "providerName": "Corp Account",
            "buttonColor": "#0000ff",
            "iconUrl": "https://companyb.com/abcd/icon.png"
          }
        ],
        "tosUrl": "https://companyb.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companyb.com/abcd/privacypolicy.html"
      },
      "tenant-c-id": {
        "fullLabel": "Company C Portal",
        "displayName": "Company C",
        "iconUrl": "https://companyc.com/img/icon.png",
        "logoUrl": "https://companyc.com/img/logo.png",
        "buttonColor": "#007bff",
        "immediateFederatedRedirect": true,
        "signInOptions": [
          {
            "provider": "password",
            "requireDisplayName": false
          },
          {
            "provider": "google.com",
            "scopes": ["scope1", "scope2", "https://example.com/scope3"],
            "loginHintKey": "login_hint",
            "customParameters": {
              "prompt": "consent",
            },
          }
        ],
        "tosUrl": "https://companyc.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companyc.com/abcd/privacypolicy.html",
        "adminRestrictedOperation": {
          "status": true,
          "adminEmail": "admin@example.com",
          "helpLink": "https://www.example.com/trouble_signing_in"
        }
      },
    }
  }
}

사용 가능한 속성의 전체 목록은 참조 문서를 확인하세요.

CSS 재정의

styleUrl 속성을 사용하여 커스텀 CSS 파일을 지정할 수 있습니다. 이 파일의 스타일은 기본 CSS를 재정의합니다. HTTPS를 사용하여 이 파일에 공개적으로 액세스할 수 있어야 합니다(예: Cloud Storage 버킷에서 호스팅하는 경우).

다음 예시는 기본 CSS를 재정의하는 방법을 보여줍니다.

/** Change header title style. */
.heading-center {
  color: #7181a5;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 20px;
  font-weight: bold;
}

/** Use round edged borders for container. */
.main-container {
  border-radius: 5px;
}

/** Change page background color. */
body {
  background-color: #f8f9fa;
}

Cloud Run 인스턴스 재배포

경우에 따라 로그인 페이지를 호스팅하는 Cloud Run 인스턴스를 다시 배포할 수 있습니다. 시나리오의 예시는 다음과 같습니다.

  • ID 공급업체 추가, 수정 또는 삭제
  • 테넌트 구성 수정
  • 환경 변수 설정
  • 컨테이너 이미지를 최신 버전으로 업데이트

컨테이너 이미지를 정기적으로 업데이트하고 다시 배포하면 최신 버그 수정 및 보안 패치가 적용됩니다. GitHub에서 버전 간의 변경사항 목록을 확인할 수 있습니다.

/versionz 엔드포인트를 사용하여 배포된 컨테이너의 현재 버전을 가져올 수 있습니다. 예:

curl 'https://servicename-xyz-uc.a.run.app/versionz'

Cloud Run 인스턴스를 다시 배포하려면 다음 안내를 따르세요.

  1. Google Cloud 콘솔에서 Cloud Run 페이지로 이동합니다.

    Cloud Run 페이지로 이동

  2. 로그인 페이지를 호스팅하는 인스턴스를 선택합니다.

  3. 새 버전 수정 및 배포를 클릭합니다.

  4. 선택적으로 버전에 고급 설정을 지정하거나 변수 및 보안 비밀 탭을 클릭하여 환경 변수를 추가합니다.

  5. 배포를 클릭합니다.

고급 옵션

프로그래매틱 방식으로 로그인 페이지 맞춤설정

/admin 콘솔을 사용하는 것 외에도 JSON 구성을 프로그래매틱 방식으로 업데이트할 수 있습니다.

현재 구성을 가져오려면 /get_admin_config 엔드포인트를 사용합니다. 예:

curl -H 'Authorization: Bearer [TOKEN]'
  'https://servicename-xyz-uc.a.run.app/get_admin_config'

구성을 업데이트하려면 /set_admin_config를 사용합니다. 예:

curl -XPOST -H 'Authorization: Bearer [TOKEN]' -H "Content-type: application/json"
  -d '[UPDATED-CONFIG]' 'https://servicename-xyz-uc.a.run.app/set_admin_config'

두 REST 호출 모두 https://www.googleapis.com/auth/devstorage.read_write 범위가 필요하며 유효한 OAuth 토큰을 Authorization 헤더에 연결해야 합니다.

환경 변수 설정

Cloud Run 인스턴스에서 환경 변수를 설정하여 고급 설정을 맞춤설정할 수 있습니다. 사용 가능한 변수는 다음 표에 나와 있습니다.

변수 설명
DEBUG_CONSOLE 모든 네트워크 요청 오류 및 세부정보를 로깅할지를 나타내는 부울 값 (0 또는 1)입니다. 민감한 정보는 로깅되지 않습니다. 기본적으로 사용 중지됩니다(0).
UI_CONFIG 로그인 페이지의 JSON 구성이 포함된 문자열입니다. /admin 패널 대신 이 변수를 사용하면 구성에 액세스할 때 Cloud Storage를 읽고 쓸 수 없습니다. 잘못된 구성은 무시됩니다. 이 변수를 설정하기 전에 /admin 패널을 사용하여 JSON을 검증하면 구문 오류를 최소화할 수 있습니다.
GCS_BUCKET_NAME JSON 구성을 저장하는 데 사용되는 기본 Cloud Storage 버킷을 재정의하는 문자열입니다. 이 파일의 이름은 config.json이고 기본 위치는 gcip-iap-bucket-[CLOUD-RUN-SERVICE-NAME]-[PROJECT-NUMBER]입니다.
ALLOW_ADMIN /admin 구성 패널에 대한 액세스 허용 여부를 나타내는 부울 값(0 또는 1)입니다. 기본적으로 사용 설정됩니다(1)

변수를 업데이트한 후 Cloud Run 인스턴스의 새 버전을 배포해야 변경사항이 적용됩니다. 환경 변수에 대해 자세히 알아보려면 Cloud Run 문서를 참조하세요.

도메인 맞춤설정

기본적으로 로그인할 때 Cloud Run 인스턴스의 URL이 표시됩니다. 대신 커스텀 도메인을 지정하려면 다음 안내를 따르세요.

  1. 커스텀 도메인 매핑의 단계를 따라 Cloud Run 인스턴스의 커스텀 도메인을 설정합니다.

  2. 새 인증 URL을 사용하도록 IAP를 구성합니다.

    1. Google Cloud 콘솔에서 IAP 페이지로 이동합니다.

      IAP 페이지로 이동

    2. IAP로 보호되는 리소스를 선택합니다.

    3. 측면 패널에서 로그인 URL 입력란 옆에 있는 수정 아이콘을 선택합니다.

    4. 기존 호스팅 로그인 페이지 사용을 선택하고 드롭다운 메뉴에서 도메인을 선택합니다.

    5. 저장을 클릭합니다.

여러 IAP 리소스에 단일 로그인 페이지 사용

동일한 로그인 페이지를 사용하여 여러 IAP 리소스를 보호할 수 있습니다. 그러면 여러 구성 관리와 관련된 작업이 줄어듭니다.

로그인 페이지를 다시 사용하려면 다음 단계를 따르세요.

  1. 이 문서의 단계에 따라 IAP로 보호되는 첫 번째 리소스의 인증 페이지를 배포합니다.

  2. 두 번째 리소스에 IAP를 사용 설정합니다. 로그인 페이지를 지정하라는 메시지가 표시되면 자체 페이지 제공을 선택하고 Cloud Run 서비스의 URL을 URL로 입력합니다.

  3. Cloud Run 서비스를 다시 배포합니다.

문제 해결

서드 파티 쿠키를 사용 중지하거나 스토리지 파티셔닝을 구현하는 브라우저에서는 로그인 페이지가 작동하지 않습니다.

이 문제를 해결하려면 다음 단계를 따르세요.

  1. 로그인 페이지를 재배포합니다. 최신 버전의 로그인 페이지는 로그인 페이지의 도메인을 authDomain으로 사용합니다.

  2. authDomain이 IAP로 보호되는 애플리케이션의 로그인 페이지 URL로 설정되었는지 확인하기 위해 로그인 페이지의 구성을 맞춤설정합니다.

    {
      "AIzaSyC5DtmRUR...": {
        "authDomain": "LOGIN_URL",
        ...
      }
    }
    

다음 단계