认知用户池: 如何使用刷新令牌刷新访问令牌

我正在使用 Cognoto 用户池来验证我系统中的用户。成功的身份验证提供了 ID 令牌(JWT)访问令牌(JWT)刷新令牌文件清楚地提到刷新令牌可以用来刷新访问令牌,但没有提到如何使用。 我的问题是,一旦 访问令牌过期,如何使用存储的刷新令牌再次刷新访问令牌?

我在 JavaScriptSDK 中搜索了一下,没有找到任何方法来做同样的事情。

我还考虑通过一个 Lambda 函数来实现这一点,该函数接受访问令牌和刷新令牌,并使用刷新的访问令牌进行响应。如果有人能解释一下就好了。

68049 次浏览

JavaScriptSDK 在内部处理令牌的刷新。当调用 getSession获取令牌时,如果没有任何有效的缓存访问和 id 令牌,SDK 将使用刷新令牌获取新的访问和 id 令牌。它调用用户身份验证,只有当刷新令牌也过期时,才需要用户提供用户名和密码。

如果你正处于 Cognito Javascript SDK 不能为你所用的情况下,你仍然可以看到它是如何在 SDK 源码中处理刷新过程的:

您可以在 refreshSession中看到,通过为 AuthFlow值设置 REFRESH_TOKEN_AUTH,以及作为 AuthParameters值传入的对象,可以调用 Cognito启动认证端点。

该对象需要进行配置,以满足用户池的需要。具体来说,如果您的目标 App 客户机 id 具有相关的 App 客户机机密,则可能必须传入 SECRET_HASH。用于 Javascript SDK 的用户池客户端应用程序目前不能包含客户端机密,因此不需要 SECRET_HASH来连接它们。

另一个可能使您陷入循环的警告是,如果您的用户池被设置为记住设备,并且您没有将 DEVICE_KEYREFRESH_TOKEN一起传递。如果传入的是 RefreshToken而没有传入的是 DeviceKey,那么 CognitoAPI 当前将返回一个 “无效刷新令牌”错误。即使传入有效的 RefreshToken,也会返回此错误。上面链接的线程说明了这一点,尽管我确实希望 AWS 将来更新它们的错误处理时不要那么神秘。

正如在该线程中讨论的,如果使用 AdminInitiateAuthADMIN_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 函数,如果你愿意。 编辑: 固定代码,感谢蜡笔

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-conito-Identity-js 浏览器 SDK 刷新会话; 它主要是为您做这件事,除非您正在做一些不寻常的事情,否则您不需要直接处理刷新令牌。以下是你需要知道的:

假设您已经像下面这样实例化了用户池:

const userPool = new AmazonCognitoIdentity.CognitoUserPool({
UserPoolId: USER_POOL_ID,
ClientId: USER_POOL_CLIENT_ID
});

要查找已验证的最后一个用户名,可以这样做:

const cognitoUser = cognitoUserPool.getCurrentUser();

如果它找到了一个,conitoUser 将是非空的,你可以这样做,如果需要的话,它将在后台刷新你的令牌:

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 令牌,包括刷新令牌。默认情况下,它将这些内容存储在浏览器的本地存储中,但如果需要,您可以提供自己的存储对象。默认情况下,刷新令牌对30d 有效,但它是 UserPoolClient 的一个属性(RefreshTokenVality) ,您可以更改它。当您执行上述操作时,getSession ()将首先查看存储中的令牌是否存在并且仍然有效; 如果没有,它将尝试使用在那里找到的任何 refshToken 对您进行身份验证,以进入一个新的会话。

文档 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?
}
});

您可以在我的博客文章 如何认证使用令牌的用户使用认知中看到一个完整的工作示例。

如果您有一个刷新令牌,那么您可以通过向 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
}

请记住,Authorization 头应该包含前面提到的计算的 base64。

使用 NodeJS aws-sdk和一点 Promise,你可以使用 刷新令牌initiateAuth进行 await身份验证,如下所示:

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;
})()

请记住,如果启用了 设备跟踪 ,则 应该传递一个设备密钥可能会收到 无效的刷新令牌错误。