I'm using AWS Cognito for authentication and authorisation in backend API's. If user sign in using Cognito, I get access token,id token and refresh token.
In this scenario i will use id token for authentication and authorisation purpose. If the id token expires I will use refresh token to generate new tokens.
But, if I use Google as Identity Provider. After successful user creation, I'm getting only the access token and the id token. Its expiry is 1 hour also. After 1 hour how can i maintain user authentication in this scenario ?
Is it possible to generate refresh token after Google authentication from Cognito ?
This is the url I'm using for Google signup/signin :
https://{DOMAIN_NAME}.auth.{REGION}.amazoncognito.com/login?response_type=token&client_id={CLIENT_ID}&redirect_uri=http://localhost:5000/
According to your sign in URL, you are trying to use Implicit grant flow here (because response_type=token).
Cognito should not return a refresh token for Implicit grant flow. That is the intended behaviour according to the specification:
The authorization server MUST NOT issue a refresh token.
Also using the implicit flow is highly discouraged due to vulnerabilities.
Please consider using Authorization code grant flow along with PKCE. With that you can get the refresh token.
The reason you get the refresh token along with Cognito, UserName/Password is because it should be using a different Auth flow (example: USER_PASSWORD_AUTH).
Related
We are working on switching to Cognito as the 3rd party OAuth 2.0 provider for our backend services.
Our legacy system had its own Spring Authorization Server (deprecated) for generating and verifying access tokens which enabled us to add custom claims to Access Token itself, there was no need to carry around Id Token to extract information about the authorised user.
But Cognito pushes us to use OAuth 2.0 standards naturally, and there is no possibility to add custom claims to Access Tokens. Id Tokens are used for extracting custom claims and user information throughout an authorised session.
This brings us to a situation where we need to have Id tokens in the backend service sessions for the endpoints that needs some user info in order to process requests. But to fetch the Id token from the Cognito provided /userInfo endpoint, Access Tokens need to have openid scope, but to have the openid scope in the Access token, we need to use Cognito Hosted UI which is not applicable for the mobile app and our legacy Web app.
So either we need to use Identity Token as the Bearer token(not advised by standards), or we need to expect ID token with a custom HTTP header along with the Access token. Is there a huge security risk if ID tokens are used also for enabling access to REST API's ?
I am using an AWS Lambda function (Node.js 14.x) to call Cognito revokeToken function to revoke a refresh token.
According to the official document, "revokeToken" will:
Revokes all of the access tokens generated by the specified refresh token. After the token is revoked, you can not use the revoked token to access Cognito authenticated APIs.
While I can successfully revoke the refresh token & associated access tokens, previously created access tokens can still be used to access API Gateway resources (with Cognito User Pool authentication enabled).
Why can I still authorise requests to API Gateway after using Cognito's RevokeToken method to revoke access tokens?
Your refresh token & access tokens created using that refresh token are being revoked however, API Gateway will not pick up on this & still allow access.
The reason for this is that API Gateway uses the identity token to authorize API calls, not the access token.
The documentation (very unclearly I must admit) mentions:
After the token is revoked, you can not use the revoked token to access Cognito authenticated APIs.
In this case, what is meant by the above is that the token is revoked for API actions that actually belong to Cognito, not services which use Cognito for authentication.
This is why you can still authenticate with API Gateway as it will internally use the not-revoked ID token.
To demonstrate this, the typical Cognito authentication result will look like this:
{
"AuthenticationResult":{
"AccessToken":"eyJraWQiOiJvZ2JFM2xXN0FWeEpPZjJWRU50MW9RNnRrY3ZOdVRJUUNTZkJpczlBWDFBPSIsImFsZyI6IlJTMjU2In0.eyJvcmlnaW5fanRpIjoiOThjM2QyNTgtYWU1NC00MzBiLWEwNjktYjg4OGZhNWQ5YzkwIiwic3ViIjoiZWE5MGQ2MjktMWI3NS00Y2ZiLThkOWQtZGRmZWJhZGZlZGYzIiwiZXZlbnRfaWQiOiJlODMxZWI1MS1mMjM3LTRhNDctYTM3YS1jNTk5MzBkNzFkZDgiLCJ0b2tlbl91c2UiOiJhY2Nlc3MiLCJzY29wZSI6ImF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluIiwiYXV0aF90aW1lIjoxNjM0NDcxOTA1jsda56dsf8sd7f7sdf9sdf6hdf8gfdf8dg89dfg8dfgIiwiZXhwIjoxNjM0NTU4MzA1LCJpYXQiOjE2MzQ0NzE5MDUsImp0aSI6IjNlMWQ1ZmJjLTUwNjMtNGZmNS1iZDIxLWFlZTk1ZjAyZTVjZCIsImNsaWVudF9pZCI6IjNiZHBsZzNxYmwyNWJhczM1aWRuOWU4YWdqIiwidXNlcm5hbWUiOiJxbDAzIn0.FU8fv7bXDFLhUku_A11bLiw2kCdLCIepZ0l4E5t8okC_KgABGE4G_VFZ5E34VYAokuy-npWQaP84PKksnPR-S17phEQ-CWyoL5OM7t5sqkJseikqrgxzMoAgnSn34RUY4FJDhmuM9F5ejNhaKp-uDhDnvYaWe8Qcuz1TfBlgLUwARE1eBMaxqusmPOyJpZOvKcaeiOfqduv_rnN36UjIRaOeeDkht54n0066H9vBYnE1kwkVLlLagCI7kF2agHV6Kkl-cTVZTZjqCYzhOuAba_ZhdedsLn9xrQcY14-qgxfYiBxc-m1CVSZ-ZUTlmRShFrG6aHZDYSlWP38bRgQD8g",
"ExpiresIn":86400,
"TokenType":"Bearer",
"RefreshToken":"eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.ODcmlgBQ4TWaUudE5edD5Y0Fx4LQQ6LnmNjfBBcfS14s4Q9MmiD9YQZZSyKTu9UoeR3SpQkGEVvkI6t7rhtyheMqwtJiCxmitCcKVm8RhLrjgEIq2wbBTegINEOKFQKpf3IHsolssm2UuebYpaXxxB68swMwDdBC-By51aTdAaZpJWGiviqZiCNnuzvuKNfhnrZVk482ctrBdu0AgGj-YxKsnVEQozXvCHiojnQE7YfJW048ctYUBgti0wpKvNI2_PbavT11W8cD0x093uQhARZtSBazv4mkqtbPpdv6GSzIE6PHETfKIxJIMaDzLJKAnbOHCEquHfYD1KouZO93Cw.IopkTsaLXan0zWOW.jWxoHQPORSNVHQKfypBQ23BWtq3hzwkDDasd65asd7a8sfg8sd8gsd78gs9gdf8asdc9asdfdsfFXw-hfLA1uhCIeZebRNcTzmVcR_Kd0g75tCzH8FJw4TXMPvQ9Qg9NcJI1JsA_DLC1B9m0hwPXtUib0Pz7k5Z2l7fwCUUfFfT4VNiPrhsuz35XlbNJc1fq1kfN88f5sGZ0UocYwl_CtPf-0FwMeGJkhyyKIWAguV0z5FsfaVWojDPcGkw5JqILUwYxKZUW3mSORI5tXrgVloLoF48xaFoXpK5T2xPHfSaUZMJBsFMK24MdDRgLIfy6XS-21upJsi30O6yyc96A1vYYpk-eD871WC9156AlB3BskCsmgPKRSjPaDQ6Dfuc_xDR4ZLYb5XSaFtEC8q5eGeq-N2DjS0eDQbsUyMFY8ddY7BVNWIv0X1_HRKz6Nilrveimmc4OfaQ3aTHj32VDkxJb5BZgylEgLtaO_HRnqjnPD1Ic-XlxH0oXLgVm2aN3SSdXuEr3BdtCoRtnGfAkLAlK686L-3Ryo2Xg1oR61gNJXBljeFQVeTeUSNuYBKyc_swv4pwBUW_Ff_iOiR_ddhMuzqattTEtGxyXDKIVls3tDoyerJsD5e-_igpKkw5cks9Il6XI1I0Mq2jnso7xFeZEBztt3qcXJ5w8OdV1Qsc79SnAIA8yF9K_8zwZvMfFU5ODJSkvY56lReBiHDImQfDiImkgShDSKu-4y8IP10Ba3jr55b7rebgk3fO9yV9JcZOx9C2JtAHKFaTVCYz2YRA3fChUXlRHcqJfc2cYYAx5wD9fJLR4FiVkgkapaNadYT0pc4LPnylyijtUXgxL7tpDG4i9yJiQ1hT3kHV4o9NZeXyPV-VDN2XWeCOEYhpXASnj7nyRpzH4wPmD9xa6N-4mDzqDVXel7c527eecN7ZfU3MPXJ2fHnTGTzDjPCCPG1Ur0LE-9CJMWMMxbFDV9RR6paKMU8fno-cgczR-HZsWIgGzXudYEyb03OVDmRgdKxW_oWL8yWx0KEMtibHH.5jIZnrXB7RFLn3LS1bJqGA",
"IdToken":"eyJraWQiOiJlZFRQVFU0cXNOdFQyOEd2TWlXakg1aUExZjZFOVwvekw3OEZLYzdWU1VoWT0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJlYTkwZDYyOS0xYjc1LTRjZmItOGQ5ZC1kZGZlYmFkZmVkZjMiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2dadfgsdf6asdf87sd6fsd8f89fjpITrZUOH58mhfm-ToGQznHcWxd5I48W-uHckz1F9dLP9YLZpI0e3BGPPPeEOf2lAIa0dSfzKhl-SZgSLRBd5qX066jx1_6SvnTMpDgRE9JZmFM_n3cI2_jGoShxUwr7NpGaUCo2r2LPXx9Rs-KqmU33mqTFD8F-CezracE9xuAuDiTNCwlBA9_LB1FPQhvzkkgSR5vouIyUYzg"
},
"..."
}
This contains 3 parts:
AccessToken - used for checking access to Cognito authenticated APIs
RefreshToken - used for refreshing the access token
IdToken - a bearer JWT token generally used by services outside of CUP (Congito User Pools)
AccessToken & RefreshToken are being revoked but the JWT ID token (that API GW uses) is not being revoked.
This is confirmed by an AWS employee in this GitHub issue:
Because IdToken is represented as a JSON Web Key Token, it's signed with a secret or private/public key pairs, which means even if you revoke the IdToken, there is no way to revoke the distributed public key.
Others also have indicated this problem, here, here & here for example.
This is not really Amazon's fault as the nature of JWT tokens means that they are stateless. The authorizing server doesn't need any state stored to be able to validate the JWT token - the token itself is all that is needed to verify a token bearer's authorization.
This is perhaps highlighted a bit clearer in another section of the docs:
Revoked tokens will still be valid if they are verified using any JWT library that verifies the signature and expiration of the token.
Essentially, the bad news is that you've hit an Amazon Cognito limitation.
The slightly good news is that if your solutions permits, you can set the expiry of the ID token to a minimum of 1 hour for Cognito ID tokens. While the Cognito Quotas page mentions a minimum of 5 minutes for ID tokens, the console will actually throw a Cannot be greater than refresh token expiration. error for 5 minutes. You will have to set it to the minimum refresh token expiration duration which currently sits at 60 minutes.
A better way to say this is how the documentation for [AdminUserGlobalSignOut][9] has worded it:
Signs out users from all devices, as an administrator. It also invalidates all refresh tokens issued to a user. The user's current access and Id tokens remain valid until their expiry. Access and Id tokens expire one hour after they are issued.
The good news is that an update was given by an AWS Amplify contributor in February that they were working on it (the same "fix" would apply to API Gateway hopefully as a service external to Congito).
The "best" news is that it is possible to implement "real" token revocation yourself to prevent the ID tokens from still being used. You will need to have a custom API Gateway Lambda authoriser (also this guide may be useful) to validate the token with Cognito but then to also check the token to see if it's been revoked by e.g. checking a blocklist DynamoDB table. Read more about the methodology in my answer here.
Until Amazon allows you to "revoke" the ID token too i.e. manages adding of ID tokens associated with the revoked refresh token to a block list on their side which other services can use for verification, you will have to implement this yourself.
I have configured Facebook as federated identity provider with authorization code. After successful authentication I receive the authorization code but can't find a way to get the access and refresh token in AWS .net SDK. Only option that I found is https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html but is there any existing method in .net SDK for this?
Assuming you are using the Cognito Authentication Extension Library: refreshing a session with a refresh token is documented here. The refresh token for a signed in user can be access through user.SessionTokens attribute which is an instance of CognitoUserSession
I'm using implicit grant for one particular client (web-based admin portal) for an application that uses Cognito-integrated auth for other parts of the application (API Gateway, etc).
The problem is, the id token I get from Cognito Oauth expires after one hour, but I still need that token to send to API Gateway to signify that I have an authenticated user.
How do I get a new id token (remember, I'm using implicit grant, so I don't have a refresh token)?
Do I have to re-send the user's credentials to Cognito's auth endpoint every hour? That seems extravagantly un-secure for several reasons.
What am I missing?
Thanks in advance!
If you use implicit grant there is no way of refreshing the tokens (Storing credentials and using it to get new id tokens doesn't count here since its takes away the purpose of using OpenID Connect).
This is why the code grant is there. Unless you use code grant, refreshing the tokens are not possible.
You can use AWS Amplify library to simplify the login where it takes cares of the refreshing and other heavy liftings if you use the code grant including using federated identities to access AWS resources such as S3.
When I signed in using google from aws cognito login page, it return back aws access token. Is it possible to retrieve google access token and refresh token using aws token.
The url used to login -
https://example.auth.ap-southeast-1.amazoncognito.com//login?redirect_uri=redirect_uri&response_type=token&client_id=client_id.
How aws cognito handles refresh token when signed using google?
In short, no.
When you use the above URL to sign in using Cognito Userpool's OAuth endpoints, the response from google (i.e. tokens) is sent to the OAuth response endpoint for your userpool's domain ( https://example.auth.ap-southeast-1.amazoncognito.com/oauth2/idpresponse ). Cognito checks the response from Google and generates id, access & refresh tokens and returns these to you depending on the scope and auth flows used. The response from Google i.e the google tokens is not stored somewhere and there are no Cognito API calls to retrieve the same.
As for token refresh when signed in using Google, that depends on your refresh token (returned by Cognito, and not Google's refresh token). As long as the refresh token returned from Cognito is valid, you can use it to get new id/access tokens. Again, this process does not involve Google at all.
Not a fully working solution, but you can create custom attributes in cognito, and map them in the attribute mapping screen :
Then when the user logged in, I saw that access_token and expired_in are captured. But I do not see refresh_token. Without refresh_token we wont be able to access the api from the backend. This seems so close alas !
I'm gonna build off of Sourav Sarkar's answer with an idea that you can try. If you setup Google as an OIDC provider (not the one built in Cognito) you may be able to try adding either one of these scopes:
offline
offline_access
The reason why we have to include these is because by default, Google only returns the Access Token and not the Refresh Token. You have to specify that you would need the refresh token to retrieve it.
Fair warning though, I am not sure which one will work nor if adding any scope will work either since I have not tried it myself.
If you really need the refresh token for some reason e.g. Google API calls, you can use Auth0 as this is built into their product. I have not tried that either and this is also a problem I am facing right now since I don't want to use Auth0.
Here are my references:
AWS OpenID Connect Oath 2.0 Scopes Google OAuth 2.0