authts / oidc-client-ts

OpenID Connect (OIDC) and OAuth2 protocol support for browser-based JavaScript applications
https://authts.github.io/oidc-client-ts/
Apache License 2.0
1.25k stars 191 forks source link

AWS Cognito logout endpoint #1385

Open njlr opened 4 months ago

njlr commented 4 months ago

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.

pamapa commented 4 months ago

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.

njlr commented 4 months ago

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?

pamapa commented 4 months ago

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....

njlr commented 4 months ago

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.