Apple プラットフォームで OpenID Connect を使用して認証する

Identity Platform を使用する Firebase Authentication にアップグレードした場合は、OpenID Connect(OIDC)準拠の任意のプロバイダを使用して Firebase でユーザーを認証できます。これにより、Firebase でネイティブにサポートされていない ID プロバイダを使用できるようになります。

始める前に

OIDC プロバイダを使用してユーザーをログインさせるには、まずプロバイダから次のような情報を収集する必要があります。

  • クライアント ID: アプリを識別するプロバイダに固有の文字列。プロバイダによっては、サポートするプラットフォームごとに異なるクライアント ID を割り当てている場合があります。これは、プロバイダが発行する ID トークン内の aud クレームの値の一つです。

  • クライアント シークレット: プロバイダがクライアント ID の所有権を確認するために使用するシークレット文字列。クライアント ID ごとに、一致するクライアント シークレットが必要です(この値は、認証コードフローを使用する場合にのみ必要です。認証コードフローの使用は強く推奨されています)。

  • 発行元: プロバイダを識別する文字列。この値は、/.well-known/openid-configuration が付加される際に、プロバイダの OIDC ディスカバリ ドキュメントがある URL にする必要があります。たとえば、発行元が https://auth.example.com の場合、ディスカバリ ドキュメントは https://auth.example.com/.well-known/openid-configuration で入手できる必要があります。

上記の情報を取得したら、Firebase プロジェクトのログイン プロバイダとして OpenID Connect を有効にします。

  1. Firebase を iOS プロジェクトに追加します

  2. Identity Platform を使用する Firebase Authentication にアップグレードしていない場合は、アップグレードしてください。OpenID Connect 認証は、アップグレードされたプロジェクトでのみ使用できます。

  3. Firebase コンソールの [ログイン プロバイダ] ページで [新しいプロバイダを追加] をクリックし、続いて [OpenID Connect] をクリックします。

  4. 認証コードフローを使用するか、暗黙的な認可フローを使用するかを選択します。

    プロバイダでサポートされているコードフローを常に使用する必要があります。暗黙的フローは安全性が低いため、使用しないことを強くおすすめします。

  5. このプロバイダに名前を付けます。生成されたプロバイダ ID(たとえば、oidc.example-provider など)をメモします。この ID は、ログインコードをアプリに追加するときに必要になります。

  6. クライアント ID とクライアント シークレット、プロバイダの発行元文字列を指定します。これらの値は、プロバイダから割り当てられた値と完全に一致している必要があります。

  7. 変更を保存します。

Firebase SDK を使用したログインフローの処理

OIDC プロバイダを使用して Firebase でユーザーを認証する最も簡単な方法は、Firebase SDK でログインフロー全体を処理することです。

Firebase Apple プラットフォーム SDK でログインフローを処理する手順は次のとおりです。

  1. Xcode プロジェクトにカスタム URL スキームを追加します。

    1. プロジェクト構成を開きます(左側のツリービューでプロジェクト名をダブルクリックします)。[TARGETS] セクションでアプリを選択し、[Info] タブを開いて [URL Types] セクションを展開します。
    2. [+] ボタンをクリックし、エンコードされたアプリ ID を URL スキームとして追加します。エンコードされたアプリ ID は、Firebase コンソールの [全般設定] ページで、iOS アプリのセクションで確認できます。その他のフィールドは空白にしておきます。

      完了すると、構成は次のようになります(ただし、値はアプリケーションによって異なります)。

      Xcode のカスタム URL スキーム設定インターフェースのスクリーンショット
  2. Firebase コンソールで取得したプロバイダ ID を使用して OAuthProvider のインスタンスを作成します。

    Swift

    var provider = OAuthProvider(providerID: "oidc.example-provider")
    

    Objective-C

    FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:@"oidc.example-provider"];
    
  3. 省略可: OAuth リクエストと一緒に送信したい追加のカスタム OAuth パラメータを指定します。

    Swift

    provider.customParameters = [
      "login_hint": "user@example.com"
    ]
    

    Objective-C

    [provider setCustomParameters:@{@"login_hint": @"user@example.com"}];
    

    サポートされているパラメータについては、プロバイダにお問い合わせください。setCustomParameters で Firebase の必須パラメータを渡すことはできません。該当するパラメータは client_idresponse_typeredirect_uristatescoperesponse_mode です。

  4. 省略可: 認証プロバイダにリクエストする、基本的なプロフィール以外の追加の OAuth 2.0 スコープを指定します。

    Swift

    provider.scopes = ["mail.read", "calendars.read"]
    

    Objective-C

    [provider setScopes:@[@"mail.read", @"calendars.read"]];
    

    サポートされているスコープについては、プロバイダにお問い合わせください。

  5. 省略可: アプリがユーザーに reCAPTCHA を表示する際の SFSafariViewController または UIWebView の提示方法をカスタマイズするには、AuthUIDelegate プロトコルに従ったカスタムクラスを作成します。

  6. OAuth プロバイダ オブジェクトを使用して Firebase での認証を行います。

    Swift

    // If you created a custom class that conforms to AuthUIDelegate,
    // pass it instead of nil:
    provider.getCredentialWith(nil) { credential, error in
      if error != nil {
        // Handle error.
      }
      if credential != nil {
        Auth().signIn(with: credential) { authResult, error in
          if error != nil {
            // Handle error.
          }
          // User is signed in.
          // IdP data available in authResult.additionalUserInfo.profile.
          // OAuth access token can also be retrieved:
          // (authResult.credential as? OAuthCredential)?.accessToken
          // OAuth ID token can also be retrieved:
          // (authResult.credential as? OAuthCredential)?.idToken
        }
      }
    }
    

    Objective-C

    // If you created a custom class that conforms to AuthUIDelegate,
    // pass it instead of nil:
    [provider getCredentialWithUIDelegate:nil
                                completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) {
      if (error) {
        // Handle error.
      }
      if (credential) {
        [[FIRAuth auth] signInWithCredential:credential
                                  completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
          if (error) {
            // Handle error.
          }
          // User is signed in.
          // IdP data available in authResult.additionalUserInfo.profile.
          // OAuth access token can also be retrieved:
          // ((FIROAuthCredential *)authResult.credential).accessToken
          // OAuth ID token can also be retrieved:
          // ((FIROAuthCredential *)authResult.credential).idToken
        }];
      }
    }];
    
  7. 上記の例ではログインフローを中心に説明していますが、linkWithCredential を使用して OIDC プロバイダを既存のユーザーにリンクすることもできます。たとえば、複数のプロバイダを同じユーザーにリンクして、どれでもログインできるようにすることができます。

    Swift

    Auth().currentUser.link(withCredential: credential) { authResult, error in
      if error != nil {
        // Handle error.
      }
      // OIDC credential is linked to the current user.
      // IdP data available in authResult.additionalUserInfo.profile.
      // OAuth access token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.accessToken
      // OAuth ID token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.idToken
    }
    

    Objective-C

    [[FIRAuth auth].currentUser
        linkWithCredential:credential
                completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      if (error) {
        // Handle error.
      }
      // OIDC credential is linked to the current user.
      // IdP data available in authResult.additionalUserInfo.profile.
      // OAuth access token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).accessToken
      // OAuth ID token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).idToken
    }];
    
  8. 同じパターンを reauthenticateWithCredential でも使用できます。これは、ログインしてから短時間のうちに行うべき機密性の高い操作のために、最新の認証情報を取得するのに使われます。

    Swift

    Auth().currentUser.reauthenticateWithCredential(withCredential: credential) { authResult, error in
      if error != nil {
        // Handle error.
      }
      // User is re-authenticated with fresh tokens minted and
      // should be able to perform sensitive operations like account
      // deletion and email or password update.
      // IdP data available in result.additionalUserInfo.profile.
      // Additional OAuth access token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.accessToken
      // OAuth ID token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.idToken
    }
    

    Objective-C

    [[FIRAuth auth].currentUser
        reauthenticateWithCredential:credential
                          completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      if (error) {
        // Handle error.
      }
      // User is re-authenticated with fresh tokens minted and
      // should be able to perform sensitive operations like account
      // deletion and email or password update.
      // IdP data available in result.additionalUserInfo.profile.
      // Additional OAuth access token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).accessToken
      // OAuth ID token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).idToken
    }];
    

手動でログインフローを処理する

OpenID Connect ログインフローをアプリにすでに実装している場合は、ID トークンを直接使用して Firebase で認証できます。

Swift

let credential = OAuthProvider.credential(
    withProviderID: "oidc.example-provider",  // As registered in Firebase console.
    idToken: idToken,  // ID token from OpenID Connect flow.
    rawNonce: nil
)
Auth.auth().signIn(with: credential) { authResult, error in
    if error {
        // Handle error.
        return
    }
    // User is signed in.
    // IdP data available in authResult?.additionalUserInfo?.profile
}

Objective-C

FIROAuthCredential *credential =
    [FIROAuthProvider credentialWithProviderID:@"oidc.example-provider"  // As registered in Firebase console.
                                       IDToken:idToken  // ID token from OpenID Connect flow.
                                      rawNonce:nil];
[[FIRAuth auth] signInWithCredential:credential
                          completion:^(FIRAuthDataResult * _Nullable authResult,
                                      NSError * _Nullable error) {
    if (error != nil) {
        // Handle error.
        return;
    }
    // User is signed in.
    // IdP data available in authResult.additionalUserInfo.profile
}];

次のステップ

ユーザーが初めてログインすると、新しいユーザー アカウントが作成され、ユーザーがログイン時に使用した認証情報(ユーザー名とパスワード、電話番号、または認証プロバイダ情報)にアカウントがリンクされます。この新しいアカウントは Firebase プロジェクトの一部として保存され、ユーザーのログイン方法にかかわらず、プロジェクトのすべてのアプリでユーザーを識別するために使用できます。

  • アプリでは、User オブジェクトからユーザーの基本的なプロフィール情報を取得できます。ユーザーを管理するをご覧ください。

  • Firebase Realtime Database と Cloud Storage のセキュリティ ルールでは、ログイン済みユーザーの一意のユーザー ID を auth 変数から取得し、それを使用して、ユーザーがアクセスできるデータを管理できます。

既存のユーザー アカウントに認証プロバイダの認証情報をリンクすることで、ユーザーは複数の認証プロバイダを使用してアプリにログインできるようになります。

ユーザーのログアウトを行うには、signOut: を呼び出します。

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

さまざまな認証エラーに対応できるようにエラー処理コードを追加することもできます。エラーの処理をご覧ください。