Amazon Cognito 为您的 Web 应用程序提供身份验证、授权和用户管理。Soto Cognito 身份验证工具包是 Cognito 的 Swift 接口。
首先,您需要创建一个 CognitoConfiguration 实例来存储所有配置信息,并创建您的 CognitoAuthenticatable 实例。
let awsClient = AWSClient(httpClientProvider: .createNew)
let cognitoIdentityProvider = CognitoIdentityProvider(client: awsClient, region: .euwest1)
let configuration = CognitoConfiguration(
userPoolId: "eu-west-1_userpoolid",
clientId: "23432clientId234234",
clientSecret: "1q9ln4m892j2secreta0dalh9a3aakmpeugiaag8k3cacijlbkrp",
cognitoIDP: cognitoIdentityProvider,
adminClient: true
)
let authenticatable = CognitoAuthenticatable(configuration: configuration)
userPoolId、clientId 和 clientSecret 的值都可以在 Amazon Cognito 用户池 控制台中找到。AWSClient 是用于与 Amazon Web Services 通信的客户端,CognitoIdentityProvider 提供了 Cognito Identity Provider 用户池 API。这两个对象都由 Soto 库提供。在继续之前,建议您先阅读一下关于它们的文档,此处 和 此处。
adminClient 参数决定将使用哪一组 CognitoIdentityProvider 命令。如果设置为 true,则将使用命令的管理版本。这些版本需要带有 AWS 凭证的 AWSClient。您可以在此处找到有关提供凭证的更多详细信息。此外,像 createUser 这样的一些命令仅在 adminClient 设置为 true 时才可用。是否需要使用 adminClient 将由您在 AWS 控制台中为您的应用程序客户端设置的 authFlow 定义。如果您要将 adminClient 设置为 false,则可以按如下方式创建您的 AWSClient,因为您不需要 AWS 凭证。
let awsClient = AWSClient(
credentialProvider: .empty,
httpClientProvider: .createNew
)
通常,命令的管理版本由服务器使用,而非管理版本由客户端软件使用。
假设我们有上面创建的 CognitoAuthenticatable 实例,则可以使用以下代码创建用户。如上所述,此函数需要配置 adminClient 设置为 true。
let username = "johndoe"
let attributes: [String: String] = ["email": "user@email.com", "name": "John Doe", "gender": "male"]
return authenticatable.createUser(username: username, attributes: attributes)
您提供的属性应与您在 AWS Cognito 控制台中创建用户池时选择的属性相匹配。创建用户后,系统会向他们发送一封电子邮件,详细说明他们的用户名和随机生成的密码。
作为替代方案,您可以使用 signUp 函数,该函数接受 username 和 password。这将向用户发送一封包含确认码的确认电子邮件。然后,您需要使用此确认码调用 confirmSignUp。要使此路径可用,您需要在用户池中设置“允许用户自行注册”标志。
一旦您的用户在 signUp 情况下创建并确认。以下代码将从用户名和密码生成 JWT 身份验证令牌。此函数需要配置带有 AWS 凭证的 CognitoIdentityProvider,除非您传递设置为 false 的 requireAuthenticatedClient 参数。
let response = try await authenticatable.authenticate(
username: username,
password: password,
context: request
)
if case .authenticated(let authenticated) = response {
let accessToken = authenticated.accessToken
let idToken = authenticated.idToken
let refreshToken = authenticated.refreshToken
...
访问令牌仅用于指示用户已被授予访问权限。它包含验证信息、用户名和一个主题 uuid,如果您不想使用用户名,可以使用该 uuid 来标识用户。令牌有效期为 60 分钟。idToken 包含有关用户身份的声明。它应包含附加到用户的所有属性。同样,此令牌仅在 60 分钟内有效。如果您收到 challenged 情况,则表示您遇到了登录挑战,必须先响应挑战才能收到身份验证令牌。请参阅下文。
以下代码将验证令牌是否提供访问权限。
let response = try await authenticatable.authenticate(accessToken: token)
let username = response.username
let subject = response.subject
...
如果访问令牌已过期、不是由用户池颁发的或不是为应用程序客户端创建的,则此调用将返回失败的 Future 并出现未经授权的错误。
ID 令牌包含用户的属性。由于这在不同项目之间有所不同,您必须提供一个自定义类来填充这些属性。该类需要继承自 Codable,并且 CodingKeys 需要反映 Amazon Web Services 提供的键。这些键在 OIDC 标准声明中定义。如果您有附加到用户的自定义属性,这些属性将以 "custom:" 为前缀。以下代码将从 ID 令牌中提取用户名、电子邮件、姓名和性别。
struct IdResponse: Codable {
let email: String
let username: String
let name: String
let gender: String
private enum CodingKeys: String, CodingKey {
case email = "email"
case username = "cognito:username"
case name = "name"
case gender = "gender"
}
}
let response = authenticatable.authenticate(idToken: token)
.map { (response: IdResponse)->IdResponse in
let email = response.email
let username = response.username
let name = response.name
let gender = response.gender
...
return response
}
注意:ID 令牌中的用户名标签是 "cognito:username"。
为了避免每 60 分钟就要求用户提供用户名和密码,还提供了刷新令牌。您可以使用它来生成新的 ID 令牌和访问令牌,无论它们何时过期或即将过期。刷新令牌有效期为 30 天。不过,您可以在 Cognito 控制台中编辑此时长。
let response = try await authenticatable.refresh(
username: username,
refreshToken: refreshToken,
context: request
)
let accessToken = response.authenticated?.accessToken
let idToken = response.authenticated?.idToken
...
有时,当您尝试验证用户名和密码或刷新令牌时,系统会返回挑战而不是身份验证令牌。例如,当有人首次登录时,他们需要先更改密码才能继续。在这种情况下,AWS Cognito 会返回一个新的密码挑战。当您使用新密码响应此挑战时,它会为您提供身份验证令牌。其他情况包括多因素身份验证。以下是响应更改密码请求的代码
let challengeName: CognitoChallengeName = .newPasswordRequired
let challengeResponse: [String: String] = ["NEW_PASSWORD":"MyNewPassword1"]
let response = try await authenticatable.respondToChallenge(
username: username,
name: challengeName,
responses: challengeResponse,
session: session,
context: request
)
let accessToken = response.authenticated?.accessToken
let idToken = response.authenticated?.idToken
let refreshToken = response.authenticated?.refreshToken
...
name 参数是一个枚举,包含所有挑战。responses 参数是一个字典,包含对挑战的输入。session 参数包含在身份验证请求返回给您的挑战中。如果挑战成功,您将收到 response.authenticated 作为响应。如果需要另一个挑战,您将在 response.challenged 中获得该挑战的详细信息。respondToChallenge 函数有针对新密码的自定义版本:respondToNewPasswordChallenge 和针对多因素身份验证的自定义版本:respondToMFAChallenge。
如果您想将 Cognito 用户池与 Soto Cognito 身份验证库一起使用,则在创建 Cognito 用户池时需要进行一些设置。由于该库使用管理员级别的服务调用,因此设备跟踪不可用,请确保将设备记住功能设置为关闭。否则,您的刷新令牌将无法工作。
在为您的用户池创建应用程序客户端时,请确保启用“生成客户端密钥”。Soto Cognito 身份验证库会自动创建具有客户端密钥的用户池所需的密钥哈希。利用这一点是明智的。由于该库旨在在安全的后端服务器上工作,因此它使用 Admin no SRP 授权流程来验证用户身份。您还需要勾选“为身份验证启用管理员 API 的用户名密码身份验证 (ALLOW_ADMIN_USER_PASSWORD_AUTH)”,以确保身份验证正常工作。
有关 AWS Cognito 用户池的更多详细信息,您可以在此处找到 Amazon 的文档。
Soto Cognito 身份验证可以用于与 Amazon Cognito 联合身份进行交互,从而允许您创建用于访问 AWS 服务的临时凭证。
首先,您需要创建一个 CognitoIdentityConfiguration 实例,用于存储与 Amazon Cognito 联合身份进行交互的所有配置信息,并创建一个 CognitoIdentifiable 实例。
let cognitoIdentity = CognitoIdentity(client: awsClient, region: .euwest1)
let configuration = CognitoIdentityConfiguration(
identityPoolId: "eu-west-1_identitypoolid"
identityProvider: "provider"
cognitoIdentity: cognitoIdentity
)
let identifiable = CognitoIdentifiable(configuration: configuration)
identityPoolId 可以从 AWS 控制台的“编辑身份池”部分获取。cognitoIdentity 是用于与 Amazon Web Services 通信的客户端。它由 Soto 库提供。identityProvider 是您在 AWS Cognito 身份池中设置的任何内容,用于提供身份验证详细信息。
访问 AWS 凭证需要两个步骤。首先,您需要获取一个身份 ID,然后使用该身份 ID,您可以获取您的 AWS 凭证。这可以通过以下方式完成。
let identity = identifiable.getIdentityId(idToken: idToken)
return identifiable.getCredentialForIdentity(identityId: identity, idToken: token)
在您使用 Cognito 用户池的情况下,idToken 是您验证用户身份时返回的 idToken。
如果您从客户端使用用户名/密码身份验证,则最好使用安全远程密码 (SRP) 进行身份验证。SRP 是一种安全的基于密码的身份验证和密钥交换协议。它要求客户端向服务器表明它知道用户的密码,而无需实际将密码传递给服务器。此外,服务器不存储密码副本,而是存储一个验证器,该验证器可用于验证密码是否正确。AWS Cognito 中实现了一个版本,您可以按如下方式使用它
import SotoCognitoAuthenticationSRP
let response = authenticatable.authenticateSRP(
username: username,
password: password,
requireAuthenticatedClient: false
)
Soto Cognito 身份验证工具包提供了一个凭证提供程序,该提供程序结合了 Cognito 用户池身份验证和 Cognito Identity 来生成凭证。它将在需要时使用返回的刷新令牌刷新凭证。它的设置方式如下
let credentialProvider: CredentialProviderFactory = .cognitoUserPool(
userName: username,
authentication: .password(password),
userPoolId: userPoolId,
clientId: clientId,
clientSecret: clientSecret,
identityPoolId: identityPoolId,
region: region,
respondToChallenge: { challenge, parameters, error in
// Respond to any challenges returned by userpool authentication
// function parameters are
// challenge: Challange type
// parameters: Challenge parameters
// error: Error returned from a previous respondToChallenge response
switch challenge {
case .newPasswordRequired:
return try await respondToNewPassword()
default:
return nil
}
}
)
let client = AWSClient(credentialProvider: credentialProvider, httpClientProvider: .createNew)
authentication 参数允许您定义如何使用用户池进行身份验证。可能的选项包括 .password(需要密码)、.refreshToken(需要您已生成的刷新令牌),如果您已导入 SotoAuthenticationKitSRP,则 .srp 为您提供安全远程密码身份验证。
SotoCognitoAuthenticationKit 的参考文档可以在此处找到。