Cognito 用户池:如何使用刷新令牌刷新访问令牌
Cognito User Pool: How to refresh Access Token using Refresh Token
我正在使用 Cognito 用户池对系统中的用户进行身份验证。成功的身份验证会提供 ID 令牌 (JWT)、访问令牌 (JWT) 和 刷新令牌 . documentation here, 明确提到刷新令牌可以用来刷新访问令牌,但没有提到如何。
我的问题是,一旦我的 Access Token 过期,我该如何使用存储的刷新令牌再次刷新我的访问令牌?
我搜索了 JavaScript SDK,但找不到任何方法来执行相同的操作。我肯定错过了什么。
我还想通过 Lambda 函数来执行此操作,该函数接收访问令牌和刷新令牌并使用刷新的访问令牌进行响应。如果有人能对此有所了解,那就太好了。
JavaScript SDK 在内部处理令牌的刷新。当您调用 getSession
获取令牌时,在没有任何有效的缓存访问和 ID 令牌的情况下,SDK 使用刷新令牌来获取新的访问和 ID 令牌。它调用用户身份验证,要求用户提供用户名和密码,仅当刷新令牌也已过期时。
如果您处于 Cognito Javascript SDK 无法满足您的目的的情况,您仍然可以在 SDK source 中查看它如何处理刷新过程:
您可以在 refreshSession
中看到调用 Cognito InitiateAuth 端点时 REFRESH_TOKEN_AUTH
设置为 AuthFlow
值,并且传入一个对象作为 AuthParameters
值。
需要配置该对象以满足您的用户池的需要。具体来说,如果您的目标 App 客户端 ID 具有关联的 App 客户端机密,您可能必须传入 SECRET_HASH
。为与 Javascript SDK 一起使用而创建的用户池客户端应用目前不能包含客户端密码,因此不需要 SECRET_HASH
来连接它们。
另一个可能会让您陷入困境的警告是,如果您的用户池设置为记住设备,并且您没有将 DEVICE_KEY
与 REFRESH_TOKEN
一起传递。 Cognito API 当前 returns 如果您传入 RefreshToken
而未同时传入 DeviceKey
,则会出现 "Invalid Refresh Token" 错误。即使您传入有效的 RefreshToken
,也会返回此错误。上面链接的线程说明了这一点,尽管我确实希望 AWS 更新他们的错误处理以在未来不那么神秘。
如该线程中所述,如果您将 AdminInitiateAuth 与 ADMIN_NO_SRP_AUTH
一起使用,您的成功身份验证响应负载目前不包含 NewDeviceMetadata
;这意味着您在尝试刷新令牌时不会有任何 DeviceKey
传入。
我的应用程序要求在 Python 中实现,所以这里有一个对我有用的示例:
def refresh_token(self, username, refresh_token):
try:
return client.initiate_auth(
ClientId=self.client_id,
AuthFlow='REFRESH_TOKEN_AUTH',
AuthParameters={
'REFRESH_TOKEN': refresh_token,
'SECRET_HASH': self.get_secret_hash(username)
# Note that SECRET_HASH is missing from JSDK
# Note also that DEVICE_KEY is missing from my example
}
)
except botocore.exceptions.ClientError as e:
return e.response
我在 Javascript 中也一直在努力解决这个问题。这是我的解决方案,它基于 https://github.com/aws/amazon-cognito-identity-js 但它不依赖于存储,因此您可以根据需要在 lambda 函数中使用它。
编辑:固定代码,感谢 Crayons
const userPool = new AWSCognito.CognitoUserPool({
UserPoolId: <COGNITO_USER_POOL>,
ClientId: <COGNITO_APP_ID>
})
userPool.client.makeUnauthenticatedRequest('initiateAuth', {
ClientId: <COGNITO_APP_ID>,
AuthFlow: 'REFRESH_TOKEN_AUTH',
AuthParameters: {
'REFRESH_TOKEN': <REFRESH_TOKEN> // client refresh JWT
}
}, (err, authResult) => {
if (err) {
throw err
}
console.log(authResult) // contains new session
})
正在使用 amazon-cognito-identity-js 浏览器 SDK 刷新会话;它主要为您完成,除非您正在做一些不寻常的事情,否则您不需要直接处理刷新令牌。以下是您需要了解的内容:
假设您已经像这样实例化了用户池:
const userPool = new AmazonCognitoIdentity.CognitoUserPool({
UserPoolId: USER_POOL_ID,
ClientId: USER_POOL_CLIENT_ID
});
要查找最后经过身份验证的用户名,您可以这样做:
const cognitoUser = cognitoUserPool.getCurrentUser();
如果找到一个,cognitoUser 将是非空的,您可以这样做,这将在需要时在幕后刷新您的令牌:
cognitoUser.getSession(function(err, data) {
if (err) {
// Prompt the user to reauthenticate by hand...
} else {
const cognitoUserSession = data;
const yourIdToken = cognitoUserSession.getIdToken().jwtToken;
const yourAccessToken = cognitoUserSession.getAccessToken().jwtToken;
}
});
如果您不希望这些令牌保留在本地存储中,您可以:
cognitoUser.signOut();
它的工作方式是,在成功验证后,浏览器将存储您的 JWT 令牌,包括刷新令牌。默认情况下,它会将这些存储在浏览器的本地存储中,但如果需要,您可以提供自己的存储对象。默认情况下,刷新令牌的有效期为 30 天,但它是 UserPoolClient 的 属性 (RefreshTokenValidity),您可以更改它。执行上述操作时,getSession() 会首先查看您存储的令牌是否存在并且是否仍然有效;如果没有,它将尝试使用在那里找到的任何 refreshToken 来验证您进入新会话的身份。
文档 http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html 指出 iOS 和 Android SDK 会为您完成此操作,但我没有使用过它们,所以不能保证这一点。
这是一个示例,说明如何在服务器端使用 Node.js 来使用 JavaScript。
const AccessToken = new CognitoAccessToken({ AccessToken: tokens.accessToken });
const IdToken = new CognitoIdToken({ IdToken: tokens.idToken });
const RefreshToken = new CognitoRefreshToken({ RefreshToken: tokens.refreshToken });
const sessionData = {
IdToken: IdToken,
AccessToken: AccessToken,
RefreshToken: RefreshToken
};
const userSession = new CognitoUserSession(sessionData);
const userData = {
Username: email,
Pool: this.userPool
};
const cognitoUser = new CognitoUser(userData);
cognitoUser.setSignInUserSession(userSession);
cognitoUser.getSession(function (err, session) { // You must run this to verify that session (internally)
if (session.isValid()) {
// Update attributes or whatever else you want to do
} else {
// TODO: What to do if session is invalid?
}
});
您可以在我的博客中看到完整的工作示例post How to authenticate users with Tokens using Cognito。
如果您有刷新令牌,那么只需向 Cognito 发出这个简单的 POST 请求即可获得新的访问和 ID 令牌:
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token >
Content-Type='application/x-www-form-urlencoded'
Authorization=Basic base64(client_id + ':' + client_secret)
grant_type=refresh_token&
client_id=YOUR_CLIENT_ID&
refresh_token=YOUR_REFRESH_TOKEN
您将收到以下回复:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token":"eyJz9sdfsdfsdfsd",
"id_token":"dmcxd329ujdmkemkd349r",
"token_type":"Bearer",
"expires_in":3600
}
请记住授权 header 应该包含上述计算的 base64。
使用 NodeJS aws-sdk
and a bit of Promise
you can await
authentication using Refresh Token with initiateAuth
如下:
const {CognitoIdentityServiceProvider} = require('aws-sdk');
const initiateAuth = (ClientId, REFRESH_TOKEN, DEVICE_KEY) =>
new Promise((resolve, reject) => {
const CISP = new CognitoIdentityServiceProvider();
CISP.initiateAuth(
{
ClientId, // Cognito App Client Id
AuthFlow: 'REFRESH_TOKEN_AUTH',
AuthParameters: {
REFRESH_TOKEN,
DEVICE_KEY
}
},
(err, data) => {
if (err) {
return reject(err);
}
resolve(data);
}
);
});
// ------ Usage ------ //
(async () => {
const tokens = await initiateAuth('mY4pps3cR3T', '<R3FR3SHT0K3N>');
console.log('Tokens', tokens);
const {AuthenticationResult: {AccessToken, IdToken, ExpiresIn, TokenType}} = tokens;
})()
Keep in mind that if Device tracking is enabled you should pass a device key otherwise you can receive Invalid refresh token error.
我正在使用 Cognito 用户池对系统中的用户进行身份验证。成功的身份验证会提供 ID 令牌 (JWT)、访问令牌 (JWT) 和 刷新令牌 . documentation here, 明确提到刷新令牌可以用来刷新访问令牌,但没有提到如何。 我的问题是,一旦我的 Access Token 过期,我该如何使用存储的刷新令牌再次刷新我的访问令牌?
我搜索了 JavaScript SDK,但找不到任何方法来执行相同的操作。我肯定错过了什么。
我还想通过 Lambda 函数来执行此操作,该函数接收访问令牌和刷新令牌并使用刷新的访问令牌进行响应。如果有人能对此有所了解,那就太好了。
JavaScript SDK 在内部处理令牌的刷新。当您调用 getSession
获取令牌时,在没有任何有效的缓存访问和 ID 令牌的情况下,SDK 使用刷新令牌来获取新的访问和 ID 令牌。它调用用户身份验证,要求用户提供用户名和密码,仅当刷新令牌也已过期时。
如果您处于 Cognito Javascript SDK 无法满足您的目的的情况,您仍然可以在 SDK source 中查看它如何处理刷新过程:
您可以在 refreshSession
中看到调用 Cognito InitiateAuth 端点时 REFRESH_TOKEN_AUTH
设置为 AuthFlow
值,并且传入一个对象作为 AuthParameters
值。
需要配置该对象以满足您的用户池的需要。具体来说,如果您的目标 App 客户端 ID 具有关联的 App 客户端机密,您可能必须传入 SECRET_HASH
。为与 Javascript SDK 一起使用而创建的用户池客户端应用目前不能包含客户端密码,因此不需要 SECRET_HASH
来连接它们。
另一个可能会让您陷入困境的警告是,如果您的用户池设置为记住设备,并且您没有将 DEVICE_KEY
与 REFRESH_TOKEN
一起传递。 Cognito API 当前 returns 如果您传入 RefreshToken
而未同时传入 DeviceKey
,则会出现 "Invalid Refresh Token" 错误。即使您传入有效的 RefreshToken
,也会返回此错误。上面链接的线程说明了这一点,尽管我确实希望 AWS 更新他们的错误处理以在未来不那么神秘。
如该线程中所述,如果您将 AdminInitiateAuth 与 ADMIN_NO_SRP_AUTH
一起使用,您的成功身份验证响应负载目前不包含 NewDeviceMetadata
;这意味着您在尝试刷新令牌时不会有任何 DeviceKey
传入。
我的应用程序要求在 Python 中实现,所以这里有一个对我有用的示例:
def refresh_token(self, username, refresh_token):
try:
return client.initiate_auth(
ClientId=self.client_id,
AuthFlow='REFRESH_TOKEN_AUTH',
AuthParameters={
'REFRESH_TOKEN': refresh_token,
'SECRET_HASH': self.get_secret_hash(username)
# Note that SECRET_HASH is missing from JSDK
# Note also that DEVICE_KEY is missing from my example
}
)
except botocore.exceptions.ClientError as e:
return e.response
我在 Javascript 中也一直在努力解决这个问题。这是我的解决方案,它基于 https://github.com/aws/amazon-cognito-identity-js 但它不依赖于存储,因此您可以根据需要在 lambda 函数中使用它。 编辑:固定代码,感谢 Crayons
const userPool = new AWSCognito.CognitoUserPool({
UserPoolId: <COGNITO_USER_POOL>,
ClientId: <COGNITO_APP_ID>
})
userPool.client.makeUnauthenticatedRequest('initiateAuth', {
ClientId: <COGNITO_APP_ID>,
AuthFlow: 'REFRESH_TOKEN_AUTH',
AuthParameters: {
'REFRESH_TOKEN': <REFRESH_TOKEN> // client refresh JWT
}
}, (err, authResult) => {
if (err) {
throw err
}
console.log(authResult) // contains new session
})
正在使用 amazon-cognito-identity-js 浏览器 SDK 刷新会话;它主要为您完成,除非您正在做一些不寻常的事情,否则您不需要直接处理刷新令牌。以下是您需要了解的内容:
假设您已经像这样实例化了用户池:
const userPool = new AmazonCognitoIdentity.CognitoUserPool({
UserPoolId: USER_POOL_ID,
ClientId: USER_POOL_CLIENT_ID
});
要查找最后经过身份验证的用户名,您可以这样做:
const cognitoUser = cognitoUserPool.getCurrentUser();
如果找到一个,cognitoUser 将是非空的,您可以这样做,这将在需要时在幕后刷新您的令牌:
cognitoUser.getSession(function(err, data) {
if (err) {
// Prompt the user to reauthenticate by hand...
} else {
const cognitoUserSession = data;
const yourIdToken = cognitoUserSession.getIdToken().jwtToken;
const yourAccessToken = cognitoUserSession.getAccessToken().jwtToken;
}
});
如果您不希望这些令牌保留在本地存储中,您可以:
cognitoUser.signOut();
它的工作方式是,在成功验证后,浏览器将存储您的 JWT 令牌,包括刷新令牌。默认情况下,它会将这些存储在浏览器的本地存储中,但如果需要,您可以提供自己的存储对象。默认情况下,刷新令牌的有效期为 30 天,但它是 UserPoolClient 的 属性 (RefreshTokenValidity),您可以更改它。执行上述操作时,getSession() 会首先查看您存储的令牌是否存在并且是否仍然有效;如果没有,它将尝试使用在那里找到的任何 refreshToken 来验证您进入新会话的身份。
文档 http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html 指出 iOS 和 Android SDK 会为您完成此操作,但我没有使用过它们,所以不能保证这一点。
这是一个示例,说明如何在服务器端使用 Node.js 来使用 JavaScript。
const AccessToken = new CognitoAccessToken({ AccessToken: tokens.accessToken });
const IdToken = new CognitoIdToken({ IdToken: tokens.idToken });
const RefreshToken = new CognitoRefreshToken({ RefreshToken: tokens.refreshToken });
const sessionData = {
IdToken: IdToken,
AccessToken: AccessToken,
RefreshToken: RefreshToken
};
const userSession = new CognitoUserSession(sessionData);
const userData = {
Username: email,
Pool: this.userPool
};
const cognitoUser = new CognitoUser(userData);
cognitoUser.setSignInUserSession(userSession);
cognitoUser.getSession(function (err, session) { // You must run this to verify that session (internally)
if (session.isValid()) {
// Update attributes or whatever else you want to do
} else {
// TODO: What to do if session is invalid?
}
});
您可以在我的博客中看到完整的工作示例post How to authenticate users with Tokens using Cognito。
如果您有刷新令牌,那么只需向 Cognito 发出这个简单的 POST 请求即可获得新的访问和 ID 令牌:
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token >
Content-Type='application/x-www-form-urlencoded'
Authorization=Basic base64(client_id + ':' + client_secret)
grant_type=refresh_token&
client_id=YOUR_CLIENT_ID&
refresh_token=YOUR_REFRESH_TOKEN
您将收到以下回复:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token":"eyJz9sdfsdfsdfsd",
"id_token":"dmcxd329ujdmkemkd349r",
"token_type":"Bearer",
"expires_in":3600
}
请记住授权 header 应该包含上述计算的 base64。
使用 NodeJS aws-sdk
and a bit of Promise
you can await
authentication using Refresh Token with initiateAuth
如下:
const {CognitoIdentityServiceProvider} = require('aws-sdk');
const initiateAuth = (ClientId, REFRESH_TOKEN, DEVICE_KEY) =>
new Promise((resolve, reject) => {
const CISP = new CognitoIdentityServiceProvider();
CISP.initiateAuth(
{
ClientId, // Cognito App Client Id
AuthFlow: 'REFRESH_TOKEN_AUTH',
AuthParameters: {
REFRESH_TOKEN,
DEVICE_KEY
}
},
(err, data) => {
if (err) {
return reject(err);
}
resolve(data);
}
);
});
// ------ Usage ------ //
(async () => {
const tokens = await initiateAuth('mY4pps3cR3T', '<R3FR3SHT0K3N>');
console.log('Tokens', tokens);
const {AuthenticationResult: {AccessToken, IdToken, ExpiresIn, TokenType}} = tokens;
})()
Keep in mind that if Device tracking is enabled you should pass a device key otherwise you can receive Invalid refresh token error.