Google は、黒人コミュニティのための人種的公平の促進に取り組んでいます。詳細をご覧ください。

Appleを使用して認証する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Firebase SDK を使用してエンド ツー エンドの OAuth 2.0 サインイン フローを実行することにより、ユーザーが Apple ID を使用して Firebase で認証できるようにすることができます。

あなたが始める前に

Apple を使用してユーザーをサインインするには、まず Apple の開発者サイトで Sign In with Apple を構成してから、Firebase プロジェクトのサインイン プロバイダーとして Apple を有効にします。

Apple デベロッパ プログラムに参加する

Sign In with Apple は、 Apple Developer Programのメンバーのみが構成できます。

Apple でサインインを構成する

  1. Apple の開発者サイトの [ Certificates, Identifiers & Profiles]ページで、アプリの Sign In with Apple を有効にします。
  2. メール リンクのサインイン、メール アドレスの確認、アカウント変更の取り消しなど、ユーザーにメールを送信する Firebase Authentication の機能のいずれかを使用する場合は、Apple プライベート メール リレー サービスを構成し、 noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (またはカスタマイズしたメール テンプレート ドメイン)を使用して、Apple が Firebase Authentication から送信されたメールを匿名の Apple メール アドレスにリレーできるようにします。

Apple をサインイン プロバイダーとして有効にする

  1. Apple プロジェクトに Firebase を追加します。 Firebase コンソールでアプリをセットアップするときは、必ずアプリのバンドル ID を登録してください。
  2. Firebase コンソールで、 Authセクションを開きます。 [サインイン方法] タブで、 Appleプロバイダーを有効にします。アプリで Sign In with Apple のみを使用している場合は、サービス ID、Apple チーム ID、秘密鍵、および鍵 ID フィールドを空のままにしておくことができます。

Apple の匿名データ要件に準拠する

Sign In with Apple では、ユーザーはサインイン時にメール アドレスなどのデータを匿名化するオプションを利用できます。このオプションを選択したユーザーは、ドメインがprivaterelay.appleid.comのメール アドレスを持っています。アプリで Sign In with Apple を使用する場合、これらの匿名化された Apple ID に関して適用される開発者ポリシーまたは Apple の条件に従う必要があります。

これには、直接識別される個人情報を匿名化された Apple ID に関連付ける前に、必要なユーザーの同意を得ることも含まれます。 Firebase Authentication を使用する場合、これには次のアクションが含まれる場合があります。

  • メールアドレスを匿名化された Apple ID に、またはその逆にリンクします。
  • 電話番号を匿名化された Apple ID にリンクする、またはその逆を行う
  • 非匿名のソーシャル認証情報 (Facebook、Google など) を匿名化された Apple ID にリンクするか、その逆を行います。

上記のリストは網羅的なものではありません。アプリが Apple の要件を満たしていることを確認するには、開発者アカウントのメンバーシップ セクションにある Apple Developer Program License Agreement を参照してください。

Apple でサインインし、Firebase で認証する

Apple アカウントで認証するには、まず Apple のAuthenticationServicesフレームワークを使用してユーザーを Apple アカウントにサインインさせ、次に Apple の応答からの ID トークンを使用して Firebase AuthCredentialオブジェクトを作成します。

  1. サインイン リクエストごとに、ランダムな文字列 (「ナンス」) を生成します。これを使用して、アプリの認証リクエストに応じて取得した ID トークンが付与されたことを確認します。この手順は、リプレイ攻撃を防ぐために重要です。

    次の例のように、 SecRandomCopyBytes(_:_:_)を使用して暗号的に安全なノンスを生成できます。

    迅速

    // Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
    private func randomNonceString(length: Int = 32) -> String {
      precondition(length > 0)
      let charset: [Character] =
        Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
      var result = ""
      var remainingLength = length
    
      while remainingLength > 0 {
        let randoms: [UInt8] = (0 ..< 16).map { _ in
          var random: UInt8 = 0
          let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
          if errorCode != errSecSuccess {
            fatalError(
              "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
            )
          }
          return random
        }
    
        randoms.forEach { random in
          if remainingLength == 0 {
            return
          }
    
          if random < charset.count {
            result.append(charset[Int(random)])
            remainingLength -= 1
          }
        }
      }
    
      return result
    }
    
        

    Objective-C

    // Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
    - (NSString *)randomNonce:(NSInteger)length {
      NSAssert(length > 0, @"Expected nonce to have positive length");
      NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
      NSMutableString *result = [NSMutableString string];
      NSInteger remainingLength = length;
    
      while (remainingLength > 0) {
        NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16];
        for (NSInteger i = 0; i < 16; i++) {
          uint8_t random = 0;
          int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random);
          NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode);
    
          [randoms addObject:@(random)];
        }
    
        for (NSNumber *random in randoms) {
          if (remainingLength == 0) {
            break;
          }
    
          if (random.unsignedIntValue < characterSet.length) {
            unichar character = [characterSet characterAtIndex:random.unsignedIntValue];
            [result appendFormat:@"%C", character];
            remainingLength--;
          }
        }
      }
    
      return [result copy];
    }
        

    サインイン要求で nonce の SHA256 ハッシュを送信すると、Apple はそれを応答で変更せずに渡します。 Firebase は、元のノンスをハッシュし、それを Apple から渡された値と比較することで、応答を検証します。

    迅速

    @available(iOS 13, *)
    private func sha256(_ input: String) -> String {
      let inputData = Data(input.utf8)
      let hashedData = SHA256.hash(data: inputData)
      let hashString = hashedData.compactMap {
        String(format: "%02x", $0)
      }.joined()
    
      return hashString
    }
    
        

    Objective-C

    - (NSString *)stringBySha256HashingString:(NSString *)input {
      const char *string = [input UTF8String];
      unsigned char result[CC_SHA256_DIGEST_LENGTH];
      CC_SHA256(string, (CC_LONG)strlen(string), result);
    
      NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
      for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
        [hashed appendFormat:@"%02x", result[i]];
      }
      return hashed;
    }
        
  2. ノンスの SHA256 ハッシュと、Apple の応答を処理するデリゲート クラスを要求に含めて、Apple のサインイン フローを開始します (次の手順を参照してください)。

    迅速

    import CryptoKit
    
    // Unhashed nonce.
    fileprivate var currentNonce: String?
    
    @available(iOS 13, *)
    func startSignInWithAppleFlow() {
      let nonce = randomNonceString()
      currentNonce = nonce
      let appleIDProvider = ASAuthorizationAppleIDProvider()
      let request = appleIDProvider.createRequest()
      request.requestedScopes = [.fullName, .email]
      request.nonce = sha256(nonce)
    
      let authorizationController = ASAuthorizationController(authorizationRequests: [request])
      authorizationController.delegate = self
      authorizationController.presentationContextProvider = self
      authorizationController.performRequests()
    }
    

    Objective-C

    @import CommonCrypto;
    
    - (void)startSignInWithAppleFlow {
      NSString *nonce = [self randomNonce:32];
      self.currentNonce = nonce;
      ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
      ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest];
      request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
      request.nonce = [self stringBySha256HashingString:nonce];
    
      ASAuthorizationController *authorizationController =
          [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
      authorizationController.delegate = self;
      authorizationController.presentationContextProvider = self;
      [authorizationController performRequests];
    }
    
  3. ASAuthorizationControllerDelegateの実装で Apple の応答を処理します。サインインが成功した場合は、Apple の応答からの ID トークンとハッシュ化されていないナンスを使用して、Firebase で認証します。

    迅速

    @available(iOS 13.0, *)
    extension MainViewController: ASAuthorizationControllerDelegate {
    
      func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
          guard let nonce = currentNonce else {
            fatalError("Invalid state: A login callback was received, but no login request was sent.")
          }
          guard let appleIDToken = appleIDCredential.identityToken else {
            print("Unable to fetch identity token")
            return
          }
          guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
            print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
            return
          }
          // Initialize a Firebase credential.
          let credential = OAuthProvider.credential(withProviderID: "apple.com",
                                                    IDToken: idTokenString,
                                                    rawNonce: nonce)
          // Sign in with Firebase.
          Auth.auth().signIn(with: credential) { (authResult, error) in
            if error {
              // Error. If error.code == .MissingOrInvalidNonce, make sure
              // you're sending the SHA256-hashed nonce as a hex string with
              // your request to Apple.
              print(error.localizedDescription)
              return
            }
            // User is signed in to Firebase with Apple.
            // ...
          }
        }
      }
    
      func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
        print("Sign in with Apple errored: \(error)")
      }
    
    }
    

    Objective-C

    - (void)authorizationController:(ASAuthorizationController *)controller
       didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
      if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
        ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
        NSString *rawNonce = self.currentNonce;
        NSAssert(rawNonce != nil, @"Invalid state: A login callback was received, but no login request was sent.");
    
        if (appleIDCredential.identityToken == nil) {
          NSLog(@"Unable to fetch identity token.");
          return;
        }
    
        NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken
                                                  encoding:NSUTF8StringEncoding];
        if (idToken == nil) {
          NSLog(@"Unable to serialize id token from data: %@", appleIDCredential.identityToken);
        }
    
        // Initialize a Firebase credential.
        FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com"
                                                                            IDToken:idToken
                                                                           rawNonce:rawNonce];
    
        // Sign in with Firebase.
        [[FIRAuth auth] signInWithCredential:credential
                                  completion:^(FIRAuthDataResult * _Nullable authResult,
                                               NSError * _Nullable error) {
          if (error != nil) {
            // Error. If error.code == FIRAuthErrorCodeMissingOrInvalidNonce,
            // make sure you're sending the SHA256-hashed nonce as a hex string
            // with your request to Apple.
            return;
          }
          // Sign-in succeeded!
        }];
      }
    }
    
    - (void)authorizationController:(ASAuthorizationController *)controller
               didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) {
      NSLog(@"Sign in with Apple errored: %@", error);
    }
    

Firebase Auth でサポートされている他のプロバイダーとは異なり、Apple は写真の URL を提供していません。

また、ユーザーが自分の電子メールをアプリと共有しないことを選択した場合、Apple はそのユーザーに固有の電子メール アドレス ( xyz@privaterelay.appleid.comの形式) をプロビジョニングし、それをアプリと共有します。プライベート メール リレー サービスを設定した場合、Apple は匿名化されたアドレスに送信されたメールをユーザーの実際のメール アドレスに転送します。

Apple は、ユーザーが初めてサインインしたときにのみ、表示名などのユーザー情報をアプリと共有します。通常、Firebase は、ユーザーが初めて Apple にサインインしたときに、 Auth.auth().currentUser.displayName .ただし、以前に Apple を使用して Firebase を使用せずにユーザーをアプリにサインインさせた場合、Apple は Firebase にユーザーの表示名を提供しません。

再認証とアカウントのリンク

同じパターンをreauthenticateWithCredential()で使用できます。これを使用して、最近のサインインが必要な機密操作の新しい資格情報を取得できます。

迅速

// Initialize a fresh Apple credential with Firebase.
let credential = OAuthProvider.credential(
  withProviderID: "apple.com",
  IDToken: appleIdToken,
  rawNonce: rawNonce
)
// Reauthenticate current Apple user with fresh Apple credential.
Auth.auth().currentUser.reauthenticate(with: credential) { (authResult, error) in
  guard error != nil else { return }
  // Apple user successfully re-authenticated.
  // ...
}

Objective-C

FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com",
                                                                   IDToken:appleIdToken,
                                                                  rawNonce:rawNonce];
[[FIRAuth auth].currentUser
    reauthenticateWithCredential:credential
                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                   NSError * _Nullable error) {
  if (error) {
    // Handle error.
  }
  // Apple user successfully re-authenticated.
  // ...
}];

また、 linkWithCredential()を使用して、さまざまな ID プロバイダーを既存のアカウントにリンクできます。

Apple では、Apple アカウントを他のデータにリンクする前に、ユーザーから明示的な同意を得る必要があることに注意してください。

Sign in with Apple では、認証資格情報を再利用して既存のアカウントにリンクすることはできません。 Sign in with Apple クレデンシャルを別のアカウントにリンクする場合は、最初に古い Sign in with Apple クレデンシャルを使用してアカウントをリンクしてから、返されたエラーを調べて新しいクレデンシャルを見つける必要があります。新しい資格情報は、エラーのuserInfoディクショナリに配置され、 FIRAuthErrorUserInfoUpdatedCredentialKeyキーを介してアクセスできます。

たとえば、Facebook アカウントを現在の Firebase アカウントにリンクするには、ユーザーが Facebook にサインインして取得したアクセス トークンを使用します。

迅速

// Initialize a Facebook credential with Firebase.
let credential = FacebookAuthProvider.credential(
  withAccessToken: AccessToken.current!.tokenString
)
// Assuming the current user is an Apple user linking a Facebook provider.
Auth.auth().currentUser.link(with: credential) { (authResult, error) in
  // Facebook credential is linked to the current Apple user.
  // The user can now sign in with Facebook or Apple to the same Firebase
  // account.
  // ...
}

Objective-C

// Initialize a Facebook credential with Firebase.
FacebookAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:accessToken];
// Assuming the current user is an Apple user linking a Facebook provider.
[FIRAuth.auth linkWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
  // Facebook credential is linked to the current Apple user.
  // The user can now sign in with Facebook or Apple to the same Firebase
  // account.
  // ...
}];

次のステップ

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

  • アプリでは、ユーザーの基本的なプロファイル情報をFIRUserオブジェクトから取得できます。ユーザーの管理を参照してください。

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

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

ユーザーをサインアウトするには、 signOut:を呼び出します。

迅速

    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;
}

認証エラーの全範囲に対してエラー処理コードを追加することもできます。エラーの処理を参照してください。