I am developing an application that uses AWS Cognito as the Identity Provider. So the user authenticate on AWS Cognito Pool and get the Access Token, Access ID and Refresh token.
Then the user can make backend requests to my app. I get the Access Token validate it, get the user profile on Cognito AWS and authorize the request.
The problem is that after the Access token has expired, and the client send the expired token to the backend, the backend app get an error (token experied or not authorized).
How can I make this workflow works?
I was thinking in send to the client a message that the token has expired, and the the cliente refresh it against the Cognito Pool. Is it the correct approach?
When you get the Access Token, ID and Refresh token from Cognito User Pools, you must cache it locally. The Access and the ID token are valid for 1 hour and should be reused as much as possible within that time period.
These tokens are JWT tokens and hold the expiry time within themselves. You can decode the JWT token and also cache this expiry along with the token. Every time the cache for the tokens is accessed, also check the current time against the cached expiry time. If expired, use the Refresh token to obtain the latest Access and ID token and cache the tokens and expiry again.
If you use one of our high level SDKs for Android, iOS or JavaScript, the SDK manages all of this for you.
you can find more information How-to use them on this link.http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html.
To use the refresh token to get new tokens, use
the AdminInitiateAuth API, passing REFRESH_TOKEN_AUTH for
theAuthFlow parameter and the refresh token for
the AuthParametersparameter with key "REFRESH_TOKEN". This initiates
the token refresh process with the Amazon Cognito server and returns
new ID and access tokens.
In short, call the AdminInitiateAuth action with the refresh token. Take a look at the SDK of your development language you prefer.
In my projects I use AWS Amplify library and I found this approach to work:
Configuration:
import Amplify, { Auth } from "aws-amplify";
Amplify.configure({
Auth: {
userPoolId: <USER_POOL_ID>,
userPoolWebClientId: <USER_POOL_WEB_CLIENT_ID>
}
});
Refresh tokens
try {
const currentUser = await Auth.currentAuthenticatedUser();
const currentSession = currentUser.signInUserSession;
currentUser.refreshSession(currentSession.refreshToken, (err, session) => {
// do something with the new session
});
} catch (e) {
// whatever
}
};
More discussion here: https://github.com/aws-amplify/amplify-js/issues/2560.
Related
Execute compute engine
api(GET https://compute.googleapis.com/compute/v1/projects/{project}/zones/{zone}/instances/{resourceId}) with oauth 2.0 client id.
I created an OAuth2.0 client ID and got access_token and refresh_token based on the steps on this site.
Obtaining OAuth 2.0 access tokens
Refreshing an access token (offline access)
I can execute api with access_token which was refreshed.
after 3days, run this step again,
https://developers.google.com/identity/protocols/oauth2/web-server#offline
response was
json
{ "error": "invalid_grant", "error_description": "Token has been expired or revoked." }
why expired refresh_token?
refresh_token
A token that you can use to obtain a new access token. Refresh tokens are valid until the user revokes access. Again, this field is only present in this response if you set the access_type parameter to offline in the initial request to Google's authorization server.
There are a lot of things which can cause a refresh token to expire.
you are using a gmail scope and the user changed their password.
it has not been used in six months.
the user has revoked your access in their google account.
If the user runs your app you get a refresh token, if they run it again you get a different refresh token, you can do this up to 50 times and get new refresh tokens and they will all work after number 50 the first one will expire. Make sure you are always saving the most resent refresh token.
your app is currently in testing and has not been set to published and has not been though the verification process.
Documentation link for expiration
I have a userpool in cognito which uses Google as the identity provider.
Now, using Amplify, we do a FederatedSign with provider as 'Google' as shown below.
Auth.federatedSignIn({ provider: "Google" });.
This gives me back the access token, id token. But the refresh token is empty.
This is for the oauth responseType:'token' configuration.
I have seen elsewhere that we need to change the grant type to 'code' i.e responseType: 'code' in order to get the refresh token.
But in this scenario, I am getting 'code = some-value' in the callback url and not the access token and refresh token.
What am I missing here?
My aim is to be able to get the refresh token - and using this Amplify would refresh the session once the access token in invalid.
You need to change oauth.responseType in your config to 'code' instead of 'token'. I'm getting an error when I do that and I'm not sure why, but this is what I found you need to do.
I am using parseCognitoWebResponse and had the same problem.
Within your User Pool go to App Clients. Check your Cognito App Client and make sure no client secret is generated. If it is filled in recreate an App Client without generating a Client Secret
Change the response_type to code
window.location.href = `https://${yourCognitoDomain}?response_type=code&client_id=${yourClientId}&redirect_uri=${cognitoRedirectUrl}`
I'm dealing with the issue of users not explicitly logging out of a web application after use, which is not secure enough for the use case. It is a React app with AWS Amplify and Cognito.
I plan to do this by tracking sessions in a database (I can capture the start or refresh of a session using a Cognito Lambda trigger written in Go on PostAuthentication_Authentication or TokenGeneration_RefreshTokens events), and expiring sessions using GlobalSignOut after a period of inactivity, but in order to invalidate the user refresh tokens on session abandonment, I need the Access Token, which appears to only be available to the client.
I can get this explicitly on login from the web client, and post it back to the database using GraphQL to record it, but I was surprised to see that it's not available from the Cognito payload sent to the Lambda event triggers. I'm also not sure of how to grab the refreshed token on the client if it refreshes after an hour of continued application use, without adding overhead to every change in the application.
Is there a way to request the current access token for a Cognito user from a server side process like a Lambda function if you're using Amplify on the client for the authentication flows? I cannot see anything in cognitoidentityprovider that allows me to retrieve the access token, but it's clearly needed to use GlobalSignOut.
You could use the Authorisation Code Flow with PKCE instead so the client is only exposed to the code which is then exchanged server-side using the token endpoint for cognito user pool / id token, access token and refresh token. You can return the user pool token to the client as that will expire after an hour while keeping the refresh token in your session manager on the server-side allowing you to fetch fresh tokens as needed or invalidate the session based on your requirements.
https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html
I've found a workable solution that does not involve fetching the Access Token server side. We use AppSync for GraphQL - the access token is passed with each GraphQL request as an authorization header, and it can be accessed in the request template as follows:
#set( $myMap = {
"field": "${field}",
"user_id": $context.identity.sub,
"source_ip": $context.identity.sourceIp,
"arguments": $context.arguments,
"access_token": $context.request.headers.authorization
} )
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson($myMap)
}
In each Lambda resolver, I simply cache the access token when encountered, so that I always have the latest access token for the current user. My scheduled session manager function then retrieves the access tokens from the cache for any users it detects with an abandoned session and can use it as an input to GlobalSignOut.
I'm trying to create a personal blog using s3 and lambda. I already have the API setup but I'm trying to figure out how to make the blog post requests more secure by requiring an authorization token in order to access the API Gateway.
I believe this can be done with cognito user pools but is usually used with many users not a single admin user. However, if there's another way I should go about this then I'm all ears.
You can implement this by:
Creating a User Pool in Cognito
If you are using the Hosted UI login pages, I recommend having the pages send a code response rather than a token response because you can call the token endpoint to get all the appropriate tokens.
Call your token endpoint with the code you receive in Step 2 (it'll be in the URL when you are redirected back to your site) to retrieve the ID, Access, and Refresh Tokens.
Once you have your cognitoUser tokens, you can wrap your blog publish function with a token check function to ensure that your token is up-to-date and send the updated token to your publish blog callback.
Send the user token in your headers: { Authorization: token } API Call.
In API Gateway, choose the Method Request in your Blog Post API and select your Cognito User Pool name under authorizers.
As long as the token you send is valid, the Method Request is all you need to update in order to secure the ability to post.
I've set up an application where users can register to my site/web application. Where
A.) A User can either join the site via registering their email/password combination and these users will be registered inside a Cognito User Pool.
B.) A User can join the site via logging in with Google/Facebook.
Bullet point A works as as expected. The user will submit their username and password, they will be able to login and they will receive the ID Token, AccessToken, and other information necessary as a response(I'm using aws amplify where it supeseded cognito javascript libraries). Now, when these users access a protected resource on API Gateway that has a cognito_authorizer enabled they will simply pass in
"Bearer " And they will be able to access
Where the is ID Token.
Now, for case B.
I am now able to login via social provider. I was able to configure all the necessary configurations in both google developer console, and registered google as federated entities.
Now, Thanks to aws amplify, I can perform a federated sign in by passing in the id token, and expires_at value that I have received from the google login.
However the only values I am able to received as a response from cognito is the CognitoIdentityCredentials
(https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityCredentials.html)
There is no access token, refresh token, and other necessary information.
Question is. Is it possible for federated sign in to retrieve an access token, and idtoken that is generated by cognito, and use that as token to be passed in as a header whenever I perform a request where a resource has a cognito_authorizer for users who joined my site via social login? Or am Missing certain steps to perform federated login that will return idtoken,and access token which is generated by cognito?
Here's the sample code I'm using
const profile = res.getBasicProfile();
const { id_token, expires_at } = res.getAuthResponse();
const user = {
email: profile.getEmail(),
name: profile.getName()
};
console.log(id_token);
Auth.federatedSignIn(
// Initiate federated sign-in with Google identity provider
'google',
{
// the JWT token
token: id_token,
// the expiration time
expires_at
},
// a user object
user
).then((a) => {
// ...location.reload();
console.log(a);
console.log(Auth.currentUserPoolUser());
});
And this is only the value I receive.
const getIdToken = async() => {return (await Auth.currentSession()).getIdToken().getJwtToken()}