Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh issue after token expires #198

Closed
bedaka opened this issue May 3, 2022 · 8 comments
Closed

Refresh issue after token expires #198

bedaka opened this issue May 3, 2022 · 8 comments

Comments

@bedaka
Copy link

bedaka commented May 3, 2022

We're using 2.1.1 with our own CloudFront Distribution. Similar to this issue once the refresh token is expired users are stuck with this "Refresh issue" page:

image

The SignOut Sign in Button does not work and the only way to resolve this is to delete the cookies manually.

Different to the linked issue I do not observe the "request loop" that is mentioned there which leads me to believe this might be a different problem.

With log level DEBUG on the RefreshHandler I noticed that the HTTP POST request towards https://our-address.auth.eu-central-1.amazoncognito.com/oauth2/token fails with 400. Unfortunately there are no more details in the log. After 5 retries the Handler redirects to the error page.
If https://our-address.service.prisma-capacity.cloud/signout is called directly we get redirected to /refreshauth and are stuck again.

This is our cookie related configuration:

AdditionalCookies: -
CookieCompatibility: amplify
CookieSettings: { "idToken": null, "accessToken": null, "refreshToken": null, "nonce": null }

@ottokruse
Copy link
Collaborator

ottokruse commented May 4, 2022

Hi @bedaka

Those cookie settings you pasted look good (the vanilla values, should work).

Is the app client allowed to refresh tokens? (Does it allow ALLOW_REFRESH_TOKEN_AUTH)

400 I've see reported here before had to do with some conflicting set up in Cognito. Such as:

  • Using a client with a secret but running the deployment with EnableSpaMode = true
  • Federating to another IDP, but not having proper attribute mappings

See this search: https://github.com/aws-samples/cloudfront-authorization-at-edge/issues?q=is%3Aissue+400

Looking at the code again, I see there's not much logging to help you in this case:

throw new Error(`Status is ${res.status}, expected 200`);

It would be great to add e.g. console.log(res.data?.toString()) there, then we'd see the response value from Cognito (not always helpful either but who knows).

If you're deploying using SAM CLI, you can add that logging yourself in the code and redeploy (increase the value for parameter VERSION then, to ensure CloudFormation really redeploys the Lambda functions)

@ottokruse
Copy link
Collaborator

It would be helpful to know:

  1. How did you deploy? (Serverless Application Repository, or SAM CLI, or ...)
  2. What parameter values did you use? (e.g. is CreateCloudFrontDistribution set to true, are you bringing you own user pool and client? Etc)

@bedaka
Copy link
Author

bedaka commented May 5, 2022

Thanks for your reply.

To answer your follow up questions:

  • There is no "App client secret" set in the Cognito App client
  • we do federate to a seperate IDP (Azure AD) via SAML but since the login worked I assumed that our attribute mappings are correct. "Enable IdP sign out flow" is enabled.
  • We use our own User Pool and Cloudfront Distribution. The UserPool is located in a different region as the Cloudfront.
  • We deploy with CDK (using the CfnApplication Construct) with these parameters:
parameters: {
        CreateCloudFrontDistribution: 'false',
        AlternateDomainNames: `example.com`,
        EnableSPAMode: 'true',
        OAuthScopes: ['email', 'openid'].join(','),
        UserPoolArn: outputs.get('UserPoolArn'),
        UserPoolClientId: 'name',
        RewritePathWithTrailingSlashToIndex: 'false',
}

this is the header of my request and the decoded JWTs in case this is of interest:

amplitude_id_5b66b1a209cXXXXX_our_domain.com=XXXX;
amp_5b66b1=f6d857dd-8db9-4523-af3XXXXXXXXXXXXXXXX;
amplitude_id_dba089949XXXXXXX_our_domain.com=XXXX;
amp_dba089=iZx_lSj4bItoXmfxE_VP71.ZjBjZThlYzXXXXXXXXXX;
amp_326633=Mq67UonvBjDnfLJi99MZlK.MDdkZjhXXXXXXXXXXXXXX;
CognitoIdentityServiceProvider.qwertzuiopqwertzuiop.LastAuthUser=azuread_name@our-domain.com;
CognitoIdentityServiceProvider.qwertzuiopqwertzuiop.azuread_name@our-domain.com.tokenScopesString=email openid;
CognitoIdentityServiceProvider.qwertzuiopqwertzuiop.azuread_name@our-domain.com.userData=%7B%22UserAttributes%22%3A%5B%7B%22Name%22%3A%22sub%22%2C%22Value%22%3A%220cc3284d-3017-46b2-b227-bc380b433f0e%22%7D%2C%7B%22Name%22%3A%22email%22%2C%22Value%22%3A%22name%40our-domain.com%22%7D%5D%2C%22Username%22%3A%22azuread_name%40our-domain.com%22%7D;
amplify-signin-with-hostedUI=true;
CognitoIdentityServiceProvider.qwertzuiopqwertzuiop.azuread_name@our-domain.com.idToken=;
CognitoIdentityServiceProvider.qwertzuiopqwertzuiop.azuread_name@our-domain.com.accessToken=;
CognitoIdentityServiceProvider.qwertzuiopqwertzuiop.azuread_name@our-domain.com.refreshToken=;
spa-auth-edge-nonce=1234567890;
spa-auth-edge-nonce-hmac=asdfgh-asdfg

idToken: {
  "at_hash": "LtXtcxxxxxxx",
  "sub": "0cc3284d-xxxxxxxxx",
  "cognito:groups": [
    "eu-central-1_XXXXXX_AzureAd"
  ],
  "email_verified": false,
  "iss": "https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXXXXXX",
  "cognito:username": "azuread_name@our-domain.com",
  "nonce": "1TCTBEud5e8geAO_XXXXXXXXXXX",
  "origin_jti": "38c6c8f1-XXXXXXXXXXXX",
  "aud": "7kq22gd6xxxxxxxxxx",
  "identities": [
    {
      "userId": "name@our-domain.com",
      "providerName": "AzureAd",
      "providerType": "SAML",
      "issuer": "https://sts.windows.net/3124141-xxxxxxxxxxxx/",
      "primary": "true",
      "dateCreated": "1647502726514"
    }
  ],
  "token_use": "id",
  "auth_time": 1651579835,
  "exp": 1651582835,
  "iat": 1651579835,
  "jti": "dd4bca55-xxxxxxxxxxxxxxxx",
  "email": "name@our-domain.com"
}

accessToken: {
  "sub": "xxxxx",
  "cognito:groups": [
    "eu-central-1_XXXXXX_AzureAd"
  ],
  "iss": "https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXXXX",
  "version": 2,
  "client_id": "7kq22gd6erxxxxxxxxx",
  "origin_jti": "38c6c8f1-2046xxxxxxx",
  "token_use": "access",
  "scope": "openid email",
  "auth_time": 1651579835,
  "exp": 1651582835,
  "iat": 1651579835,
  "jti": "b957c5da-0d90-xxxxxxxxxxx",
  "username": "azuread_name@our-domain.com"
}

I'll try to add the advanced logging as you recommended and see if it generated new insights.

@bedaka
Copy link
Author

bedaka commented May 5, 2022

The advanced logging as you suggested provided this message:

Error: Failed to refresh tokens: Error: Status is 400, expected 200. 
Data: {"error":"invalid_grant"} [log region: eu-central-1] 

@ottokruse
Copy link
Collaborator

Thanks for the extra info.
Just to make sure, can you confirm that the app client has ALLOW_REFRESH_TOKEN_AUTH = true ?

Also, you mentioned the signin button does not work. Do you have more info on that?

@bedaka
Copy link
Author

bedaka commented May 6, 2022

Yes sorry I forgot to mention that ALLOW_REFRESH_TOKEN_AUTH is set to true.

But actually I might have figured out the issue. The "Sign in" Button redirects to /signout but the Cloudfront behaviour rule is named "signOut" (uppercase O). Therefore the the Sign in Button currently does not work, because it just gets redirected to the /refreshauth page again. I'll try to rename the behaviour rule.

Unrelated to that I wonder why the refreshAuth lambda does not realize that my token is expired but instead tries to query cognito. The "invalid_grant" error seems to be a reasonable reponse in case the refresh token is expired but woudn't the correct response be to clear the client cookies so that it can log in again instead of throwing an error?

@ottokruse
Copy link
Collaborator

Ahh so the token was expired. Great that you found the reason and know now why the button didn’t work.

You can’t tell if a refresh token is expired or not, the only way is to try to use it. (It’s an encrypted JWT that only Cognito can decrypt)

Since the “invalid grant” is an error that can happen in other cases as well, you don’t know it refresh token expired is the reason for it. But it would probably be good to add to the error message a text like “this is likely because your refresh token is expired. Please sign-in again”

@bedaka
Copy link
Author

bedaka commented May 6, 2022

Fixing the Behavior rules name did work.

Thank you very much for your support 🙏🏽

@bedaka bedaka closed this as completed May 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants