Amazon Cognito
开发人员指南

经过开发人员验证的身份 (身份池)

除了通过 Facebook (身份池)Google (身份池)Login with Amazon (身份池) 的 Web 联合身份验证之外,Amazon Cognito 支持已经过开发人员验证的身份。借助已经过开发人员验证的身份,您可以注册并通过自己的现有身份验证流程对用户进行身份验证,同时仍然使用 Amazon Cognito 同步用户数据并访问 AWS 资源。使用已经过开发人员验证的身份涉及最终用户设备、身份验证后端和 Amazon Cognito 之间的交互。有关更多详细信息,请阅读我们的博客

了解身份验证流程

有关已经过开发人员验证的身份的身份验证流程以及它与外部提供商身份验证流程的不同之处的信息,请参阅身份池 (联合身份) 身份验证流程

定义开发人员提供商名称并将其与身份池关联

要使用已经过开发人员验证的身份,您需要与开发人员提供商关联的身份池。为此,请按照以下步骤操作:

  1. 登录 Amazon Cognito 控制台

  2. 创建新的身份池,并在该过程中,在身份验证提供商自定义选项卡中定义开发人员提供商名称。

  3. 或者,编辑现有身份池,并在身份验证提供商自定义选项卡中定义开发人员提供商名称。

注意:一旦设置提供商名称,便无法进行更改。

有关使用 Amazon Cognito 控制台的更多说明,请参阅 使用 Amazon Cognito 控制台

实施身份提供商

Android

要使用已经过开发人员验证的身份,请实施自己的身份提供商类,该类可扩展 AWSAbstractCognitoIdentityProvider。您的身份提供商类应返回包含令牌作为属性的响应对象。

下面是我们的示例应用程序中使用的身份提供商的简单示例:

public class DeveloperAuthenticationProvider extends AWSAbstractCognitoDeveloperIdentityProvider { private static final String developerProvider = "<Developer_provider_name>"; public DeveloperAuthenticationProvider(String accountId, String identityPoolId, Regions region) { super(accountId, identityPoolId, region); // Initialize any other objects needed here. } // Return the developer provider name which you choose while setting up the // identity pool in the &COG; Console @Override public String getProviderName() { return developerProvider; } // Use the refresh method to communicate with your backend to get an // identityId and token. @Override public String refresh() { // Override the existing token setToken(null); // Get the identityId and token by making a call to your backend // (Call to your backend) // Call the update method with updated identityId and token to make sure // these are ready to be used from Credentials Provider. update(identityId, token); return token; } // If the app has a valid identityId return it, otherwise get a valid // identityId from your backend. @Override public String getIdentityId() { // Load the identityId from the cache identityId = cachedIdentityId; if (identityId == null) { // Call to your backend } else { return identityId; } } }

要使用此身份提供商,您必须将其传递到 CognitoCachingCredentialsProvider。下面是一个示例:

DeveloperAuthenticationProvider developerProvider = new DeveloperAuthenticationProvider( null, "IDENTITYPOOLID", context, Regions.USEAST1); CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider( context, developerProvider, Regions.USEAST1);

iOS - Objective-C

要使用已经过开发人员验证的身份,请实施自己的身份提供商类,该类可扩展 AWSCognitoCredentialsProviderHelper。您的身份提供商类应返回包含令牌作为属性的响应对象。

@implementation DeveloperAuthenticatedIdentityProvider /* * Use the token method to communicate with your backend to get an * identityId and token. */ - (AWSTask <NSString*> *) token { //Write code to call your backend: //Pass username/password to backend or some sort of token to authenticate user //If successful, from backend call getOpenIdTokenForDeveloperIdentity with logins map //containing "your.provider.name":"enduser.username" //Return the identity id and token to client //You can use AWSTaskCompletionSource to do this asynchronously // Set the identity id and return the token self.identityId = response.identityId; return [AWSTask taskWithResult:response.token]; } @end

要使用此身份提供商,请将其传递到 AWSCognitoCredentialsProvider,如下例所示:

DeveloperAuthenticatedIdentityProvider * devAuth = [[DeveloperAuthenticatedIdentityProvider alloc] initWithRegionType:AWSRegionYOUR_IDENTITY_POOL_REGION identityPoolId:@"YOUR_IDENTITY_POOL_ID" useEnhancedFlow:YES identityProviderManager:nil]; AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionYOUR_IDENTITY_POOL_REGION identityProvider:devAuth];

如果您想同时支持未经身份验证的身份和已经过开发人员验证的身份,请在 logins 实施中覆盖 AWSCognitoCredentialsProviderHelper 方法。

- (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins { if(/*logic to determine if user is unauthenticated*/) { return [AWSTask taskWithResult:nil]; }else{ return [super logins]; } }

如果您想支持已经过开发人员验证的身份和社交提供商,您必须管理在 loginsAWSCognitoCredentialsProviderHelper 实施中谁是当前的提供商。

- (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins { if(/*logic to determine if user is unauthenticated*/) { return [AWSTask taskWithResult:nil]; }else if (/*logic to determine if user is Facebook*/){ return [AWSTask taskWithResult: @{ AWSIdentityProviderFacebook : [FBSDKAccessToken currentAccessToken] }]; }else { return [super logins]; } }

iOS - Swift

要使用已经过开发人员验证的身份,请实施自己的身份提供商类,该类可扩展 AWSCognitoCredentialsProviderHelper。您的身份提供商类应返回包含令牌作为属性的响应对象。

import AWSCore /* * Use the token method to communicate with your backend to get an * identityId and token. */ class DeveloperAuthenticatedIdentityProvider : AWSCognitoCredentialsProviderHelper { override func token() -> AWSTask<NSString> { //Write code to call your backend: //pass username/password to backend or some sort of token to authenticate user, if successful, //from backend call getOpenIdTokenForDeveloperIdentity with logins map containing "your.provider.name":"enduser.username" //return the identity id and token to client //You can use AWSTaskCompletionSource to do this asynchronously // Set the identity id and return the token self.identityId = resultFromAbove.identityId return AWSTask(result: resultFromAbove.token) }

要使用此身份提供商,请将其传递到 AWSCognitoCredentialsProvider,如下例所示:

let devAuth = DeveloperAuthenticatedIdentityProvider(regionType: .YOUR_IDENTITY_POOL_REGION, identityPoolId: "YOUR_IDENTITY_POOL_ID", useEnhancedFlow: true, identityProviderManager:nil) let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .YOUR_IDENTITY_POOL_REGION, identityProvider:devAuth) let configuration = AWSServiceConfiguration(region: .YOUR_IDENTITY_POOL_REGION, credentialsProvider:credentialsProvider) AWSServiceManager.default().defaultServiceConfiguration = configuration

如果您想同时支持未经身份验证的身份和已经过开发人员验证的身份,请在 logins 实施中覆盖 AWSCognitoCredentialsProviderHelper 方法。

override func logins () -> AWSTask<NSDictionary> { if(/*logic to determine if user is unauthenticated*/) { return AWSTask(result:nil) }else { return super.logins() } }

如果您想支持已经过开发人员验证的身份和社交提供商,您必须管理在 loginsAWSCognitoCredentialsProviderHelper 实施中谁是当前的提供商。

override func logins () -> AWSTask<NSDictionary> { if(/*logic to determine if user is unauthenticated*/) { return AWSTask(result:nil) }else if (/*logic to determine if user is Facebook*/){ if let token = AccessToken.current?.authenticationToken { return AWSTask(result: [AWSIdentityProviderFacebook:token]) } return AWSTask(error:NSError(domain: "Facebook Login", code: -1 , userInfo: ["Facebook" : "No current Facebook access token"])) }else { return super.logins() } }

JavaScript

从后端获取身份 ID 和会话令牌后,您要将它们传递到 AWS.CognitoIdentityCredentials 提供商。下面是一个示例:

AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: 'IDENTITY_POOL_ID', IdentityId: 'IDENTITY_ID_RETURNED_FROM_YOUR_PROVIDER', Logins: { 'cognito-identity.amazonaws.com': 'TOKEN_RETURNED_FROM_YOUR_PROVIDER' } });

Unity

要使用已经过开发人员验证的身份,您需要扩展 CognitoAWSCredentials 并覆盖 RefreshIdentity 方法,以从后端检索用户身份 ID 和令牌,并将它们返回。下面是可通过“example.com”联系假想后端的身份提供商的简单示例:

using UnityEngine; using System.Collections; using Amazon.CognitoIdentity; using System.Collections.Generic; using ThirdParty.Json.LitJson; using System; using System.Threading; public class DeveloperAuthenticatedCredentials : CognitoAWSCredentials { const string PROVIDER_NAME = "example.com"; const string IDENTITY_POOL = "IDENTITY_POOL_ID"; static readonly RegionEndpoint REGION = RegionEndpoint.USEast1; private string login = null; public DeveloperAuthenticatedCredentials(string loginAlias) : base(IDENTITY_POOL, REGION) { login = loginAlias; } protected override IdentityState RefreshIdentity() { IdentityState state = null; ManualResetEvent waitLock = new ManualResetEvent(false); MainThreadDispatcher.ExecuteCoroutineOnMainThread(ContactProvider((s) => { state = s; waitLock.Set(); })); waitLock.WaitOne(); return state; } IEnumerator ContactProvider(Action<IdentityState> callback) { WWW www = new WWW("http://example.com/?username="+login); yield return www; string response = www.text; JsonData json = JsonMapper.ToObject(response); //The backend has to send us back an Identity and a OpenID token string identityId = json["IdentityId"].ToString(); string token = json["Token"].ToString(); IdentityState state = new IdentityState(identityId, PROVIDER_NAME, token, false); callback(state); } }

上面的代码使用线程调度程序对象调用协同程序。如果您在项目中无法执行上述操作,您可以在场景中使用以下脚本:

using System; using UnityEngine; using System.Collections; using System.Collections.Generic; public class MainThreadDispatcher : MonoBehaviour { static Queue<IEnumerator> _coroutineQueue = new Queue<IEnumerator>(); static object _lock = new object(); public void Update() { while (_coroutineQueue.Count > 0) { StartCoroutine(_coroutineQueue.Dequeue()); } } public static void ExecuteCoroutineOnMainThread(IEnumerator coroutine) { lock (_lock) { _coroutineQueue.Enqueue(coroutine); } } }

Xamarin

要使用已经过开发人员验证的身份,您需要扩展 CognitoAWSCredentials 并覆盖 RefreshIdentity 方法,以从后端检索用户身份 ID 和令牌,并将它们返回。下面是可通过“example.com”联系假想后端的身份提供商的简单示例:

public class DeveloperAuthenticatedCredentials : CognitoAWSCredentials { const string PROVIDER_NAME = "example.com"; const string IDENTITY_POOL = "IDENTITY_POOL_ID"; static readonly RegionEndpoint REGION = RegionEndpoint.USEast1; private string login = null; public DeveloperAuthenticatedCredentials(string loginAlias) : base(IDENTITY_POOL, REGION) { login = loginAlias; } protected override async Task<IdentityState> RefreshIdentityAsync() { IdentityState state = null; //get your identity and set the state return state; } }

更新登录映射 (仅限 Android 和 iOS)

Android

使用身份验证系统成功对用户进行身份验证后,请使用开发人员提供商名称和开发人员用户标识符 (一个字母数字字符串,可在身份验证系统中唯一标识用户) 更新登录映射。请确保在更新登录映射后调用 refresh 方法,因为 identityId 可能已更改:

HashMap<String, String> loginsMap = new HashMap<String, String>(); loginsMap.put(developerAuthenticationProvider.getProviderName(), developerUserIdentifier); credentialsProvider.setLogins(loginsMap); credentialsProvider.refresh();

iOS - Objective-C

如果没有凭证或者凭证已过期,则 iOS 开发工具包仅调用 logins 方法,以获取最新登录映射。如果您要强制开发工具包获取新的凭证 (例如,最终用户从未经身份验证变为经过身份验证并且您想要经过身份验证的用户的凭证),则在 clearCredentials 上调用 credentialsProvider

[credentialsProvider clearCredentials];

iOS - Swift

如果没有凭证或者凭证已过期,则 iOS 开发工具包仅调用 logins 方法,以获取最新登录映射。如果您要强制开发工具包获取新的凭证 (例如,最终用户从未经身份验证变为经过身份验证并且您想要经过身份验证的用户的凭证),则在 clearCredentials 上调用 credentialsProvider

credentialsProvider.clearCredentials()

获取令牌 (服务器端)

您可通过调用 GetOpenIdTokenForDeveloperIdentity 获取令牌。您必须使用 AWS 开发人员凭证从后端调用此 API。不得从客户端开发工具包调用它。API 接收 Cognito 身份池 ID;包含身份提供商名称作为密钥及标识符作为值的登录映射;以及可选 Cognito 身份 ID (即,您让一个未经过身份验证的用户变成了经过身份验证的用户)。标识符可以是用户的用户名、电子邮件地址或数值。API 通过为用户提供唯一 Cognito ID 及为最终用户提供 OpenID Connect 令牌来响应您的调用。

对于由 GetOpenIdTokenForDeveloperIdentity 返回的令牌,您需要注意以下事项:

  • 您可以指定令牌的自定义过期时间,以便缓存。如果您不提供任何自定义过期时间,则令牌的有效期为 15 分钟。

  • 您可以设置的最大令牌持续时间为 24 小时。

  • 请留意延长令牌持续时间所带来的安全方面的问题。如果攻击者获取了此令牌,则他们可以将令牌换成 AWS 凭证,供最终用户在令牌持续时间使用。

以下 Java 代码段显示了如何初始化 Amazon Cognito 客户端,以及如何检索已经过开发人员验证的身份的令牌。

// authenticate your end user as appropriate // .... // if authenticated, initialize a cognito client with your AWS developer credentials AmazonCognitoIdentity identityClient = new AmazonCognitoIdentityClient( new BasicAWSCredentials("access_key_id", "secret_access_key") ); // create a new request to retrieve the token for your end user GetOpenIdTokenForDeveloperIdentityRequest request = new GetOpenIdTokenForDeveloperIdentityRequest(); request.setIdentityPoolId("YOUR_COGNITO_IDENTITY_POOL_ID"); request.setIdentityId("YOUR_COGNITO_IDENTITY_ID"); //optional, set this if your client has an //identity ID that you want to link to this //developer account // set up your logins map with the username of your end user HashMap<String,String> logins = new HashMap<>(); logins.put("YOUR_IDENTITY_PROVIDER_NAME","YOUR_END_USER_IDENTIFIER"); request.setLogins(logins); // optionally set token duration (in seconds) request.setTokenDuration(60 * 15l); GetOpenIdTokenForDeveloperIdentityResult response = identityClient.getOpenIdTokenForDeveloperIdentity(request); // obtain identity id and token to return to your client String identityId = response.getIdentityId(); String token = response.getToken(); //code to return identity id and token to client //...

按照上述步骤操作,您应该能够将已经过开发人员验证的身份集成到应用程序中。如有任何问题或疑问,请随时在我们的论坛上发帖。

连接到现有社交身份

当您使用已经过开发人员验证的身份时,您必须从后端链接所有提供商。要将自定义身份连接到用户的社交身份(Facebook、Google 或 Amazon),请在调用 GetOpenIdTokenForDeveloperIdentity 时将身份提供商令牌添加到登录映射。要实现上述目标,当您从客户端开发工具包调用后端来对最终用户进行身份验证时,您还需要传递最终用户的社交提供商令牌。

例如,如果您想将自定义身份链接到 Facebook,在调用 GetOpenIdTokenForDeveloperIdentity 时,除了身份提供商标识符之外,您还需要将 Facebook 令牌添加到登录映射。

logins.put("YOUR_IDENTITY_PROVIDER_NAME","YOUR_END_USER_IDENTIFIER"); logins.put("graph.facebook.com","END_USERS_FACEBOOK_ACCESSTOKEN");

支持在提供商之间转换

Android

您的应用程序可能需要支持未经身份验证的身份或使用公有提供商(Login with Amazon、Facebook 或 Google 登录)的经过身份验证的身份,以及已经过开发人员验证的身份。已经过开发人员验证的身份与其他身份 (未经身份验证的身份和使用公共提供商的经过身份验证的身份) 的主要区别在于 identityId 和令牌的获取方式。对于其他身份,移动应用程序将直接与 Amazon Cognito 进行交互,而不是与身份验证系统联系。因此,移动应用程序应该能够支持两个不同的流程,具体取决于应用程序用户的选择。对此,您必须对自定义身份提供商做出一些更改。

如果映射不为空且包含带开发人员提供商名称的密钥,则 refresh 方法应该检查登录映射,然后,您应该调用后端;否则,请调用 getIdentityId 方法并返回 null。

public String refresh() { setToken(null); // If the logins map is not empty make a call to your backend // to get the token and identityId if (getProviderName() != null && !this.loginsMap.isEmpty() && this.loginsMap.containsKey(getProviderName())) { /** * This is where you would call your backend **/ // now set the returned identity id and token in the provider update(identityId, token); return token; } else { // Call getIdentityId method and return null this.getIdentityId(); return null; } }

同样,getIdentityId 方法也有两个流程,具体取决于登录映射的内容:

public String getIdentityId() { // Load the identityId from the cache identityId = cachedIdentityId; if (identityId == null) { // If the logins map is not empty make a call to your backend // to get the token and identityId if (getProviderName() != null && !this.loginsMap.isEmpty() && this.loginsMap.containsKey(getProviderName())) { /** * This is where you would call your backend **/ // now set the returned identity id and token in the provider update(identityId, token); return token; } else { // Otherwise call &COG; using getIdentityId of super class return super.getIdentityId(); } } else { return identityId; } }

iOS - Objective-C

您的应用程序可能需要支持未经身份验证的身份或使用公有提供商(Login with Amazon、Facebook 或 Google 登录)的经过身份验证的身份,以及已经过开发人员验证的身份。为此,请覆盖 AWSCognitoCredentialsProviderHelper logins 方法,以便根据当前的身份提供商返回正确的登录映射。此示例说明如何能够在未经身份验证的身份、Facebook 和已经过开发人员验证的身份之间切换。

- (AWSTask<NSDictionary<NSString *, NSString *> *> *)logins { if(/*logic to determine if user is unauthenticated*/) { return [AWSTask taskWithResult:nil]; }else if (/*logic to determine if user is Facebook*/){ return [AWSTask taskWithResult: @{ AWSIdentityProviderFacebook : [FBSDKAccessToken currentAccessToken] }]; }else { return [super logins]; } }

当您从未经身份验证转换为经过身份验证时,您应该调用 [credentialsProvider clearCredentials]; 以强制开发工具包获取经过身份验证的新凭证。当您在两个经过身份验证的提供商之间切换并且不想将这两个提供商链接起来时 (即,您没有在登录词典中为多个提供商提供令牌),您应该调用 [credentialsProvider clearKeychain];。上述操作将清除凭证和身份,并强制开发工具包获取新的。

iOS - Swift

您的应用程序可能需要支持未经身份验证的身份或使用公有提供商(Login with Amazon、Facebook 或 Google 登录)的经过身份验证的身份,以及已经过开发人员验证的身份。为此,请覆盖 AWSCognitoCredentialsProviderHelper logins 方法,以便根据当前的身份提供商返回正确的登录映射。此示例说明如何能够在未经身份验证的身份、Facebook 和已经过开发人员验证的身份之间切换。

override func logins () -> AWSTask<NSDictionary> { if(/*logic to determine if user is unauthenticated*/) { return AWSTask(result:nil) }else if (/*logic to determine if user is Facebook*/){ if let token = AccessToken.current?.authenticationToken { return AWSTask(result: [AWSIdentityProviderFacebook:token]) } return AWSTask(error:NSError(domain: "Facebook Login", code: -1 , userInfo: ["Facebook" : "No current Facebook access token"])) }else { return super.logins() } }

当您从未经身份验证转换为经过身份验证时,您应该调用 credentialsProvider.clearCredentials() 以强制开发工具包获取经过身份验证的新凭证。当您在两个经过身份验证的提供商之间切换并且不想将这两个提供商链接起来时 (即,您没有在登录词典中为多个提供商提供令牌),您应该调用 credentialsProvider.clearKeychain()。上述操作将清除凭证和身份,并强制开发工具包获取新的。

Unity

您的应用程序可能需要支持未经身份验证的身份或使用公有提供商(Login with Amazon、Facebook 或 Google 登录)的经过身份验证的身份,以及已经过开发人员验证的身份。已经过开发人员验证的身份与其他身份 (未经身份验证的身份和使用公共提供商的经过身份验证的身份) 的主要区别在于 identityId 和令牌的获取方式。对于其他身份,移动应用程序将直接与 Amazon Cognito 进行交互,而不是与身份验证系统联系。因此,移动应用程序应该能够支持两个不同的流程,具体取决于应用程序用户的选择。对此,您必须对自定义身份提供商做出一些更改。

在 Unity 中,我们建议您从 AmazonCognitoEnhancedIdentityProvide 而不是 AbstractCognitoIdentityProvider 扩展身份提供商,并调用父 RefreshAsync 方法而不是您自己的方法,以防用户未通过您自己的后端进行身份验证。如果用户已经过身份验证,则您可以使用之前介绍的相同的流程。

Xamarin

您的应用程序可能需要支持未经身份验证的身份或使用公有提供商(Login with Amazon、Facebook 或 Google 登录)的经过身份验证的身份,以及已经过开发人员验证的身份。已经过开发人员验证的身份与其他身份 (未经身份验证的身份和使用公共提供商的经过身份验证的身份) 的主要区别在于 identityId 和令牌的获取方式。对于其他身份,移动应用程序将直接与 Amazon Cognito 进行交互,而不是与身份验证系统联系。因此,移动应用程序应该能够支持两个不同的流程,具体取决于应用程序用户的选择。对此,您必须对自定义身份提供商做出一些更改。