awslabs / cognito-at-edge

Serverless authentication solution to protect your website or Amplify application
Apache License 2.0
168 stars 54 forks source link

logoutUri does not log out user completely? #80

Open BredoGen opened 8 months ago

BredoGen commented 8 months ago

What happened:

I'm facing an "auth loop" while using logount endpoint with redirect to the main page. What is a proper logoutConfiguration configuration?

What did you expect to have happen:

logoutUri should log out the user completely.

How to reproduce this (as precisely and succinctly as possible):

Cognito domain: mypool.auth.us-east-1.amazoncognito.com Protected URL: https://privatesite.com

My cognito-at-edge configuration:

const authenticator = new Authenticator({
  logLevel: 'debug',
  region: 'us-east-1', // user pool region
  userPoolId: 'us-east-1_', // user pool ID
  userPoolAppId: 'appid', // user pool app client ID
  userPoolDomain: 'mypool.auth.us-east-1.amazoncognito.com', // user pool domain
  cookiePath: '/',
  logoutConfiguration: {
    logoutUri: '/logout',
    logoutRedirectUri: 'https://privatesite.com'
  }
});

Current requests flow:

Standard login flow, everything is OK here:

  1. GET https://privatesite.com = 302 ->
  2. GET https://mypool.auth.us-east-1.amazoncognito.com/authorize?... = 302 ->
  3. POST https://mypool.auth.us-east-1.amazoncognito.com/login?... = 302 ->
  4. GET https://privatesite.com?code=... = (Set-Cookie)

Now trying to logout:

  1. GET https://privatesite.com/logout (clears the cookies with Set-Cookie empty response) = 302 ->
  2. GET https://privatesite.com = 302 ->
  3. GET https://mypool.auth.us-east-1.amazoncognito.com/authorize?... (cognito domain still remembers the user!) = 302 ->
  4. GET https://privatesite.com?code=... (authorized again)

What am I missing here? The cognito domain (mypool.auth.us-east-1.amazoncognito.com) stores it own state about user in cookies and restores the auth.

The only way I found to make it work is setting logoutRedirectUri to "https://mypool.auth.us-east-1.amazoncognito.com/logout?..." to force cognito domain logout.

Is it supposed way to do this?

Anything else you think we should know?

Environment:

aalexiev42 commented 7 months ago

Hi. I had the same issue and after a lot of troubleshooting I got to the following conclusion (and fix).

When you log out, the "handle" class goes through all if and exception cases and eventually ends up in this._getRedirectToCognitoUserPoolResponse

That thing is supposed to serve you a cognito /authorize page with specific parameters, however if there is a user session still alive in cognito, e.g. you logged in 10 minutes ago, this page auto-generates a new code and redirects you back to the base url, the lambda gets the new ?code=... and issues new tokens for you.

Maybe I'm not familiar enough with cognito and oauth in general and there is a better way to configure the auth protocol which will make the /authorize endpoint work as I need it to, but in my case changing the /authorize link to /login worked exactly as I intended. Now, the user has the option to log back in with his existing session or sign in as another user.

git diff: src/index.ts row 547

-    const userPoolUrl = `https://${this._userPoolDomain}/authorize?redirect_uri=${oauthRedirectUri}&response_type=code&client_id=${this._userPoolAppId}&state=${state}`;
+    const userPoolUrl = `https://${this._userPoolDomain}/login?redirect_uri=${oauthRedirectUri}&response_type=code&client_id=${this._userPoolAppId}&state=${state}`;
+    //const userPoolUrl = `https://${this._userPoolDomain}/authorize?redirect_uri=${oauthRedirectUri}&response_type=code&client_id=${this._userPoolAppId}&state=${state}`;

I also have userPoolAppSecret included, but I don't think it's necessary to have it. My Lambda@Edge index.js:

const { Authenticator } = require('cognito-at-edge');

const authenticator = new Authenticator({
  region: 'eu-central-1', // user pool region
  userPoolId: 'eu-central-1_someid', // user pool ID
  userPoolAppId: 'user-pool-appid', // user pool app client ID
  userPoolAppSecret: 'user-pool-secret', // user pool app client secret
  userPoolDomain: 'my-cloudfront.auth.eu-central-1.amazoncognito.com', // user pool domain
  logoutConfiguration: {
    logoutUri: "/logout",
    logoutRedirectUri: "/index.html"
  },
  cookieDomain: "my.cloudfront.domain.com",
  cookiePath: "/",
  cookieSettingsOverrides: {
    idToken: {
      expirationDays: 1
    },
    accessToken: {
      expirationDays: 1
    },
    refreshToken: {
      expirationDays: 7
    }
  },
  logLevel: 'trace'
});

exports.handler = async (request) => authenticator.handle(request);
BredoGen commented 7 months ago

@aalexiev42 Thanks for sharing your solution.

In my case, I needed to log out the user and reset the Cognito session fully, so I ended up with a similar approach, but redirecting to /logout user pool domain url.

mosheka commented 7 months ago

+1

manu-remsense commented 2 months ago

@aalexiev42 Thanks for sharing your solution.

In my case, I needed to log out the user and reset the Cognito session fully, so I ended up with a similar approach, but redirecting to /logout user pool domain url.

Thanks for pointing that out @BredoGen , with /logout it works perfectly, and thanks to the original @aalexiev42 solution too