要让您的用户能够通过 OAuth 提供方(如 Yahoo)进行 Firebase 身份验证,您可以使用 Firebase SDK 执行端到端登录流程,将通用 OAuth 登录机制集成到您的应用中。
准备工作
如需让用户能够通过 Yahoo 账号登录,您必须先启用 Yahoo 作为您的 Firebase 项目的登录服务提供方:
- 将 Firebase 添加到您的 Apple 项目。
- 在 Firebase 控制台中,打开 Auth 部分。
- 在登录方法标签页中,启用 Yahoo 提供方。
- 将该提供方的开发者控制台中的客户端 ID 和客户端密钥添加至提供方配置:
-
如需注册 Yahoo OAuth 客户端,请按照有关如何通过 Yahoo 注册 Web 应用的 Yahoo 开发者文档进行操作。
请务必选择以下两种 OpenID Connect API 权限:
profile
和email
。 - 向这些提供方注册应用时,请务必将项目的
*.firebaseapp.com
网域注册为应用的重定向网域。
-
- 点击保存。
使用 Firebase SDK 处理登录流程
如需使用 Firebase Apple 平台 SDK 处理登录流程,请按以下步骤操作:
将自定义网址方案 (URL scheme) 添加至 Xcode 项目中:
- 打开项目配置:在左侧的树状视图中双击项目名称。在目标部分中选择您的应用,然后选择信息标签页,并展开网址类型部分。
- 点击 + 按钮,然后将经过编码的应用 ID 添加为网址方案。您可以打开 Firebase 控制台的常规设置页面,在您的 iOS 应用部分找到经过编码的应用 ID。请将其他字段留空。
完成上述操作后,您的配置应显示如下(但其中的值应替换为您的应用的值):
使用提供方 ID OAuthProvider 创建一个 OAuthProvider 实例。
var provider = OAuthProvider(providerID: "yahoo.com")
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:@"yahoo.com"];
可选:指定您希望通过 OAuth 请求发送的其他自定义 OAuth 参数。
provider.customParameters = [ "prompt": "login", "language": "fr" ]
[provider setCustomParameters:@{@"prompt": @"login", @"language": @"fr"}];
如需查看 Yahoo 支持的参数,请参阅 Yahoo OAuth 文档。 请注意,您不能使用
setCustomParameters
传递 Firebase 必需的参数。这些参数包括 client_id、redirect_uri、response_type、scope 和 state。可选:指定您希望向身份验证提供方申请的
profile
和email
之外的 OAuth 2.0 范围。如果您的应用需要通过 Yahoo API 访问用户私人数据,您需要在 Yahoo 开发者控制台的 API 权限下申请 Yahoo API 权限。申请的 OAuth 范围必须与应用的 API 权限中预配置的范围完全匹配。例如,如果您申请了对用户通讯录的读写权限,并已在应用的 API 权限中预配置相应权限,则必须传递sdct-w
而不是只读 OAuth 范围sdct-r
。否则将导致流程失败,并向最终用户显示错误消息。// Request access to Yahoo Mail API. // Request read/write access to user contacts. // This must be preconfigured in the app's API permissions. provider.scopes = ["mail-r", "sdct-w"]
// Request access to Yahoo Mail API. // Request read/write access to user contacts. // This must be preconfigured in the app's API permissions. [provider setScopes:@[@"mail-r", @"sdct-w"]];
如需了解详情,请参阅 Yahoo 范围文档。
可选:如果您希望自定义应用在向用户显示 reCAPTCHA 时如何呈现
SFSafariViewController
或UIWebView
,请创建一个符合AuthUIDelegate
协议的自定义类,并将其传递给credentialWithUIDelegate
。使用 OAuth 提供方对象进行 Firebase 身份验证。
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. // Yahoo OAuth access token can also be retrieved by: // (authResult.credential as? OAuthCredential)?.accessToken // Yahoo OAuth ID token can be retrieved by calling: // (authResult.credential as? OAuthCredential)?.idToken } } }
[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. // Yahoo OAuth access token can also be retrieved by: // ((FIROAuthCredential *)authResult.credential).accessToken // Yahoo OAuth ID token can be retrieved by calling: // ((FIROAuthCredential *)authResult.credential).idToken }]; } }];
使用 OAuth 访问令牌,您可以调用 Yahoo API。
例如,如需获取基本个人资料信息,您可以调用 REST API 并在
Authorization
标头中传递访问令牌:https://social.yahooapis.com/v1/user/YAHOO_USER_UID/profile?format=json
其中,
YAHOO_USER_UID
是 Yahoo 用户的 ID,可以从Auth.auth.currentUser.providerData[0].uid
字段或authResult.additionalUserInfo.profile
检索。以上示例侧重的是登录流程。除此之外,您也可以使用
linkWithPopup
将 Yahoo 提供方与现有用户相关联。例如,您可以将多个提供方关联至同一个用户,让用户可以使用任意一个提供方进行登录。Auth().currentUser.link(withCredential: credential) { authResult, error in if error != nil { // Handle error. } // Yahoo credential is linked to the current user. // IdP data available in authResult.additionalUserInfo.profile. // Yahoo OAuth access token can also be retrieved by: // (authResult.credential as? OAuthCredential)?.accessToken // Yahoo OAuth ID token can be retrieved by calling: // (authResult.credential as? OAuthCredential)?.idToken }
[[FIRAuth auth].currentUser linkWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error) { // Handle error. } // Yahoo credential is linked to the current user. // IdP data available in authResult.additionalUserInfo.profile. // Yahoo OAuth access token is can also be retrieved by: // ((FIROAuthCredential *)authResult.credential).accessToken // Yahoo OAuth ID token can be retrieved by calling: // ((FIROAuthCredential *)authResult.credential).idToken }];
上述模式同样适用于
reauthenticateWithPopup
/reauthenticateWithRedirect
。对于要求用户必须在近期内登录过才能执行的敏感操作,可使用它来检索新的凭据。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 is can also be retrieved by: // (authResult.credential as? OAuthCredential)?.accessToken // Yahoo OAuth ID token can be retrieved by calling: // (authResult.credential as? OAuthCredential)?.idToken }
[[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 is can also be retrieved by: // ((FIROAuthCredential *)authResult.credential).accessToken // Yahoo OAuth ID token can be retrieved by calling: // ((FIROAuthCredential *)authResult.credential).idToken }];
处理“account-exists-with-different-credential”错误
如果您在 Firebase 控制台中启用了每个电子邮件地址一个账号设置,当用户尝试使用一个 Firebase 用户的提供方(例如 Google)中已存在的电子邮件地址登录另一个提供方(例如 Yahoo)时,系统会抛出 FIRAuthErrorCodeAccountExistsWithDifferentCredential
错误及临时 FIRAuthCredential
对象(Yahoo 凭据)。如需登录所需的提供方,用户必须首先登录现有提供方 (Google),然后再关联到之前的 FIRAuthCredential
(Yahoo 凭据)。 如下所示:
// Sign-in with an OAuth credential. provider.getCredentialWith(nil) { credential, error in // An account with the same email already exists. if (error as NSError?)?.code == AuthErrorCode.accountExistsWithDifferentCredential.rawValue { // Get pending credential and email of existing account. let existingAcctEmail = (error! as NSError).userInfo[AuthErrorUserInfoEmailKey] as! String let pendingCred = (error! as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as! AuthCredential // Lookup existing account identifier by the email. Auth.auth().fetchProviders(forEmail:existingAcctEmail) { providers, error in // Existing email/password account. if (providers?.contains(EmailAuthProviderID))! { // Existing password account for email. Ask user to provide the password of the // existing account. // Sign in with existing account. Auth.auth().signIn(withEmail:existingAcctEmail, password:password) { user, error in // Successfully signed in. if user != nil { // Link pending credential to account. Auth.auth().currentUser?.linkAndRetrieveData(with: pendingCred) { result, error in // ... } } } } } return } // Other errors. if error != nil { // handle the error. return } // Sign in with the credential. if credential != nil { Auth.auth().signInAndRetrieveData(with: credential!) { result, error in if error != nil { // handle the error. return } } } }
// Sign-in with an OAuth credential. [provider getCredentialWithUIDelegate:nil completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { // An account with the same email already exists. if (error.code == FIRAuthErrorCodeAccountExistsWithDifferentCredential) { // Get pending credential and email of existing account. NSString *existingAcctEmail = error.userInfo[FIRAuthErrorUserInfoEmailKey]; FIRAuthCredential *pendingCred = error.userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey]; // Lookup existing account identifier by the email. [[FIRAuth auth] fetchProvidersForEmail:existingAcctEmail completion:^(NSArray<NSString *> *_Nullable providers, NSError *_Nullable error) { // Existing email/password account. if ( [providers containsObject:FIREmailAuthProviderID] ) { // Existing password account for email. Ask user to provide the password of the // existing account. // Sign in with existing account. [[FIRAuth auth] signInWithEmail:existingAcctEmail password:password completion:^(FIRUser *user, NSError *error) { // Successfully signed in. if (user) { // Link pending credential to account. [[FIRAuth auth].currentUser linkWithCredential:pendingCred completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { // ... }]; } }]; } }]; return; } // Other errors. if (error) { // handle the error. return; } // Sign in with the credential. if (credential) { [[FIRAuth auth] signInAndRetrieveDataWithCredential:credential completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { if (error) { // handle the error. return; } }]; } }];
高级:手动处理登录流程
Firebase 所支持的其他 OAuth 提供方(如 Google、Facebook 和 Twitter)可以通过基于 OAuth 访问令牌的凭据直接实现登录,Firebase Auth 则不同。由于 Firebase Auth 服务器无法验证 Yahoo 等提供方的 OAuth 访问令牌的目标设备,因此 Firebase Auth 不支持通过这些提供方直接登录。 这是一项关键的安全要求,不满足该要求的应用和网站可能会受到重放攻击的威胁。在这种情况下,为某个项目(攻击者)获取的 Yahoo OAuth 访问令牌可能被用来登录另一个项目(受害者)。 因此,Firebase Auth 改为提供另一种功能,即使用在 Firebase 控制台中配置的 OAuth 客户端 ID 和密钥来处理整个 OAuth 流程和授权代码交换。由于授权代码只能与特定客户端 ID/密钥结合使用,因此为某个项目获取的授权代码不能用于另一个项目。
如果需要在不受支持的环境中使用这些提供方,则需使用第三方 OAuth 库和 Firebase 自定义身份验证。前者用于向提供方进行身份验证,后者则用于将提供方的凭据交换成自定义令牌。
后续步骤
在用户首次登录后,系统会创建一个新的用户账号,并将其与该用户登录时使用的凭据(即用户名和密码、电话号码或者身份验证提供方信息)相关联。此新账号存储在您的 Firebase 项目中,无论用户采用何种方式登录,您项目中的每个应用都可以使用此账号来识别用户。
在您的 Firebase Realtime Database 和 Cloud Storage 安全规则中,您可以从
auth
变量获取已登录用户的唯一用户 ID,然后利用此 ID 来控制用户可以访问哪些数据。
您可以通过将身份验证提供方凭据关联至现有用户账号,让用户可以使用多个身份验证提供方登录您的应用。
如需将用户退出登录,请调用 signOut:
。
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
您可能还需要为所有身份验证错误添加错误处理代码。请参阅处理错误。