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 的参考文档可以在此处找到。