oidc-client-ts icon indicating copy to clipboard operation
oidc-client-ts copied to clipboard

AWS Cognito logout endpoint

Open njlr opened this issue 1 year ago • 5 comments

I am trying to implement sign-out against an AWS Cognito user pool.

I followed some of the hints here https://github.com/authts/oidc-client-ts/issues/802

const cognito = "xxxxxxxx";
const userPool = "xxxxxxxxxxxxx";
const clientId = "xxxxxxxxxxxxxxxxx";

const redirectUri = "http://localhost:3000";
const logoutUri = "http://localhost:3000";

const authorizationEndpoint = `https://${cognito}.auth.eu-west-2.amazoncognito.com/oauth2/authorize?response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}`;
const endSessionEndpoint = `https://${cognito}.auth.eu-west-2.amazoncognito.com/logout?client_id=${encodeURIComponent(clientId)}&logout_uri=${encodeURIComponent(logoutUri)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`;

const userManager = new UserManager({
  authority: `https://cognito-idp.us-east-1.amazonaws.com/${userPool}`,
  client_id: clientId,
  redirect_uri: "http://localhost:3000",
  metadata: {
    authorization_endpoint: authorizationEndpoint,
    end_session_endpoint: endSessionEndpoint,
    revocation_endpoint: `https://${cognito}.auth.us-east-1.amazoncognito.com/oauth2/revoke`,
  },
  // no revoke of "access token" (https://github.com/authts/oidc-client-ts/issues/262)
  revokeTokenTypes: [ "refresh_token" ],
  // no silent renew via "prompt=none" (https://github.com/authts/oidc-client-ts/issues/366)
  automaticSilentRenew: false,
});

However, when I call userManager.signoutRedirect(), I get sent to the login screen hosted by AWS Cognito.

If I ignore the screen and navigate to http://localhost:3000/, then I find that I am still signed in (userManager.getUser() resolves to a user).

I can get sign-out like behaviour by calling userManager.removeUser() instead, but this seems wrong.

  • Will the removeUser approach lead to issues?
  • How should oidc-client-ts be configured to work with the Cognito logout endpoint?

njlr avatar Feb 05 '24 18:02 njlr

You will need to configure the post_logout_redirect_uri in your oidc application and on IDP side, this and a valid session will make the IDP to call back into your application. In that callback you can call mgr.signoutCallback and navigate where you want.

pamapa avatar Feb 06 '24 09:02 pamapa

Thanks, I followed those steps and was able to make progress.

However, it still seems to not actually sign-out.

Here is the sign-out logic:

  if (window.document.location.pathname === "/logout") {
    console.log("signoutCallback...");

    await userManager.signoutCallback();

    console.log("signoutCallback done.");

    const maybeUser = await userManager.getUser();
    const hasUser = !!maybeUser;

    console.log({ maybeUser });
  }

And the output:

signoutCallback...
signoutCallback done.
[UserManager] getUser: user loaded
Object { hasUser: true }

Is it expected that getUser will return a user after the signoutCallback has completed?

njlr avatar Feb 06 '24 11:02 njlr

Is it expected that getUser will return a user after the signoutCallback has completed?

No after the signout process its expected, that you have no user. You will need to debug that. BTW: Which version of this library do you have? 3.0.0 is here different compared to 2.4.0....

pamapa avatar Feb 07 '24 08:02 pamapa

Is it expected that getUser will return a user after the signoutCallback has completed?

No after the signout process its expected, that you have no user. You will need to debug that. BTW: Which version of this library do you have? 3.0.0 is here different compared to 2.4.0....

Sorry, I should have specified.

oidc-client-ts@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/oidc-client-ts/-/oidc-client-ts-3.0.0.tgz#910b27193b54730dbabd93709ca000f8b1e5bdf2"
  integrity sha512-YUcel/+C4AmXXhM/geNWc1jBsBVYzd+xhvSl1NMkxKpzZXqhLKmtj1ttSdxtDYm3P2YelnAr4zS4c96+p02iFQ==
  dependencies:
    jwt-decode "^4.0.0"

Looking at the source-code, it seems like it silently returns when there is no state in the URL.

AWS Cognito (somewhat strangely) does not pass any state back from the logout callback, so perhaps this is the issue?

I may be that Cognito is not a fully compliant endpoint, but given its popularity I am hoping that people have found work-arounds.

njlr avatar Feb 07 '24 09:02 njlr

according to https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html the logout endpoint expects a client_id and a logout_uri query parameters. I managed to get logout working by adding extra query params:

    userManager.signoutRedirect({
      extraQueryParams: {
        client_id: userManager.settings.client_id,
        logout_uri: 'http://localhost:1234/logout-callback'
      }
    })

If you specify a redirect_uri/response_type instead the user will be asked to re-login before going back to your application with a token. This flow (calling signoutRedirect and receiving a new token in signoutCallback) is unexpected, I guess the lib will happily gobble the new token and sign in the user again?

pqnet avatar Jul 18 '24 10:07 pqnet