nuxt-community / auth-module

Zero-boilerplate authentication support for Nuxt 2
https://auth.nuxtjs.org
MIT License
1.93k stars 925 forks source link

OAuth2 Logout - `logoutRedirectUri` does not work correctly #1781

Open franck-grenier opened 2 years ago

franck-grenier commented 2 years ago

Version

module: 5.0.0-1648802546.c9880dc nuxt: 2.15.8

Nuxt configuration

mode:

Nuxt configuration

auth: {
        cookie: {
            options: {
                expires: 365,
                secure: process.env.APP_ENV !== 'local',
            },
        },
        strategies: {
            idp: {
                scheme: 'oauth2',
                endpoints: {
                    authorization: process.env.OAUTH_AUTHORIZE_URL,
                    token: process.env.OAUTH_TOKEN_URL,
                    userInfo: process.env.OAUTH_USER_URL,
                    logout: process.env.OAUTH_LOGOUT_URL,
                },
                clientId: process.env.OAUTH_CLIENT_ID,
                token: {
                    property: 'access_token',
                    type: 'Bearer',
                    maxAge: 1800,
                },
                refreshToken: {
                    property: 'refresh_token',
                    maxAge: 60 * 60 * 24 * 30,
                },
                responseType: 'code',
                grantType: 'authorization_code',
                redirectUri: process.env.OAUTH_CALLBACK_URL,
                logoutRedirectUri: process.env.OAUTH_REDIRECT_AFTER_LOGOUT_URL,
                codeChallengeMethod: 'plain',
                autoFetchUser: false,
                autoLogout: true,
            },
        },
        plugins: [
            '~/plugins/updateLoggedInUser.js',
        ],
    },
}

Reproduction

What is expected?

I expect logout to do 3 actions :

  1. GET request on my OAuth2 logout endpoint which logs user out from my auth server and answers 302 to logoutRedirectUri :white_check_mark:
  2. logout user from my front Nuxt app :white_check_mark:
  3. Redirect my user to logoutRedirectUri after logout :stop_sign:

The strange behaviour is that if I logout from homepage (/), redirection to logoutRedirectUr works. But not from any other pages.

What is actually happening?

The GET request on my logout auth server endpoint is sent but immediately cancelled on client side and ends in 499 on server side (Nginx). The logout is effective on the auth server. But Nuxt does not get any answer and does not redirect user to logoutRedirectUri.

Additional information

I'm not sure about what my OAuth2 logout endpoint should do according to Nuxt Auth. Does it need to answer 200 or 302 to logoutRedirectUri ? What could cancel the GET request on Nuxt side ?

Thanks for your help

Checklist

drayanqi commented 2 years ago

Having the exact same problem here, anybody has an answer ?

franck-grenier commented 2 years ago

I found the faulty code: https://github.com/nuxt-community/auth-module/blob/dev/src/schemes/oauth2.ts#L320

logout(): void {
  if (this.options.endpoints.logout) {
    const opts = {
      client_id: this.options.clientId + '',
      logout_uri: this.logoutRedirectURI
    }
    const url = this.options.endpoints.logout + '?' + encodeQuery(opts)
    window.location.replace(url)
  }
  return this.$auth.reset() // <---- this seems to stop the upper "window.location.replace(url)"
}

As logout endpoint is optional and reset() function does not return anything, I'd rather go with something like that:

logout() {
  this.$auth.reset();
  if (this.options.endpoints.logout) {
    const opts = {
      client_id: this.options.clientId + "",
      logout_uri: this.logoutRedirectURI
    };
    const url = this.options.endpoints.logout + "?" + encodeQuery(opts);
    window.location.replace(url);
  }
}

I'm also investigating around a resetInterceptor which seems to send Axios requests on reset.

franck-grenier commented 2 years ago

More details on this problem.

In the reset() method, this.$auth.setUser(false); is faulty :

reset() {
  this.$auth.setUser(false); // <---- this stops the upper "window.location.replace(url)"
  this.token.reset();
  this.refreshToken.reset();
  this.requestHandler.reset();
}

this.$auth.setUser(false); sometimes redirects to /login before getting the response of window.location.replace(url);.

franck-grenier commented 2 years ago

Ok, looks like we found a solution.

It's a "race condition" problem between 2 navigation instructions :

Disabling the option watchLoggedIn (https://auth.nuxtjs.org/api/options/#watchloggedin) solved the problem as it disabled auto-redirection to /login on this.$auth.setUser(false);.

Interesting topic about browsers behaviour : https://stackoverflow.com/questions/2536793/does-changing-window-location-stop-execution-of-javascript

However, I think Nuxt Auth logout functions should handle that point natively.