Auth-module: OAuth2 Keycloak authentication

Created on 18 Mar 2020  路  22Comments  路  Source: nuxt-community/auth-module

Most helpful comment

Using v5 without any issues with public client :

 keycloak: {
        scheme: 'oauth2',
        endpoints: {
          authorization: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`,
          token: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`,
          logout: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout?redirect_uri=` + encodeURIComponent(String(process.env.REMOTE_API))
        },
        token: {
          property: 'access_token',
          type: 'Bearer',
          name: 'Authorization',
          maxAge: 1800 // Can be dynamic ?
        },
        refreshToken: {
          property: 'refresh_token',
          maxAge: 60 * 60 * 24 * 30 // Can be dynamic ? 
        },
        responseType: 'code',
        grantType: 'authorization_code',
        clientId: process.env.KEYCLOAK_CLIENT_ID,
        scope: ['openid', 'profile', 'email'],
        codeChallengeMethod: 'S256',
      }
    }

All 22 comments

Using v5 without any issues with public client :

 keycloak: {
        scheme: 'oauth2',
        endpoints: {
          authorization: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`,
          token: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`,
          logout: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout?redirect_uri=` + encodeURIComponent(String(process.env.REMOTE_API))
        },
        token: {
          property: 'access_token',
          type: 'Bearer',
          name: 'Authorization',
          maxAge: 1800 // Can be dynamic ?
        },
        refreshToken: {
          property: 'refresh_token',
          maxAge: 60 * 60 * 24 * 30 // Can be dynamic ? 
        },
        responseType: 'code',
        grantType: 'authorization_code',
        clientId: process.env.KEYCLOAK_CLIENT_ID,
        scope: ['openid', 'profile', 'email'],
        codeChallengeMethod: 'S256',
      }
    }

Im using auth-next.
When I attach the redirect_uri in the logout endpoint the created logout uri is something like this
http://keycloak-url.de:keycloak-port/auth/realms/realm/protocol/openid-connect/logout?redirect_uri=http://localhost:2000?client_id=client-id&logout_uri=http://localhost:8000

So there is a double initialization of the Attributes, as there are two question marks in the url and that leads to a wrong logout redirect in the Browser. Has anyone the same problem or can help?

My nuxt.config.js strategie looks like:

keycloak: {
        scheme: 'oauth2',
        endpoints: {
          authorization: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`,
          token: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`,
          logout: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout?redirect_uri=` + encodeURIComponent(String(process.env.REMOTE_API))
        },
        token: {
          property: 'access_token',
          type: 'Bearer'
        },
        refreshToken: {
          property: 'refresh_token',
          type: 'Bearer'
        },
        responseType: 'id_token token',
        clientId: process.env.KEYCLOAK_CLIENT_ID,
        scope: ['profile', 'email'],
        accessType: 'implicit',
      }

What I want to have is an url like this:
http://keycloak-url.de:keycloak-port/auth/realms/realm/protocol/openid-connect/logout?redirect_uri=http://localhost:2000&client_id=client-id

@Philschke you should not specify the redirect_uri in endpoints.logout but by using the logoutRedirectUri documented here.

@jper92 Specifying the redirect_uri by using the logoutRedirectUri leads to a created logout url that ends with &logout_uri=http://localhost:8000. In my case, this type of redirect seems not to work, it leads to a blank page instead.

When the url parameter is manually changed to redirect_uri (or also post_logout_redirect_uri) instead of logout_uri everything works fine and as expected.

Why is the redirect not working with logout_uri parameter?
And why is the redirect url parameter called logout_uri instead of redirect_uri anyway?

@agravelot Hello, I have a problem when using @ nuxt / auth, I use it to interact with keycloak openID, with my settings. After I have passed the authorization, I am thrown to my page with pkce_code_verifier, but auth does not make a request to get tokens, if I do it manually with this code, then I will get tokens. What am I doing wrong, please tell me

    auth: {
        strategies: {
            keycloak: {
                scheme: 'oauth2',
                endpoints: {
                    authorization: 'http://localhost:8080/auth/realms/test/protocol/openid-connect/auth',
                    token: { url: http://localhost:8080/auth/realms/test/protocol/openid-connect/token'},
                    logout: 'hhttp://localhost:8080/auth/realms/test/protocol/openid-connect/logout'
                },
                token: {
                    property: 'access_token',
                    type: 'Bearer',
                    maxAge: 1800
                },
                refreshToken: {
                    property: 'refresh_token',
                    maxAge: 60 * 60 * 24 * 30
                },
                responseType: 'code',
                accessType: 'offline',
                grantType: 'authorization_code',
                redirectUri: 'http://localhost:800/callback',
                logoutRedirectUri: undefined,
                clientId: 'new-auth-test',
                scope: ['openid', 'profile', 'email'],
                state: 'UNIQUE_AND_NON_GUESSABLE',
                codeChallengeMethod: 'S256',
            }
        }
    },

Hi all,
I experienced as well logout issue due to different query parameters passed in logout function.

My workaround was to create a new scheme overriding the logout function:

Nuxtjs Logout Fix for Keycloak

The best solution would be to give the possibility to specify the request parameter in the options.

For others who are landing here searching for Logout

just to sum it up...

The Problem

Logout will give a white KeyCloak Page, no redirect is handled.

File Reference

https://github.com/nuxt-community/auth-module/blob/1122b76e39973c42373cae6d9c4bd8723f483726/src/schemes/oauth2.ts#L262-L272

Details

KeyCloak Expects redirect_uri or the propably more suitable post_logout_redirect_uri.

While the default OAuth2 Module will submits the arg logout_uri

Solution

Provided by @alvistar

Hi all,
I experienced as well logout issue due to different query parameters passed in logout function.

My workaround was to create a new scheme overriding the logout function:

Nuxtjs Logout Fix for Keycloak

The best solution would be to give the possibility to specify the request parameter in the options.

Using v5 without any issues with public client :

 keycloak: {
        scheme: 'oauth2',
        endpoints: {
          authorization: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/auth`,
          token: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`,
          logout: `${process.env.KEYCLOAK_REMOTE_HOST}/auth/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout?redirect_uri=` + encodeURIComponent(String(process.env.REMOTE_API))
        },
        token: {
          property: 'access_token',
          type: 'Bearer',
          name: 'Authorization',
          maxAge: 1800 // Can be dynamic ?
        },
        refreshToken: {
          property: 'refresh_token',
          maxAge: 60 * 60 * 24 * 30 // Can be dynamic ? 
        },
        responseType: 'code',
        grantType: 'authorization_code',
        clientId: process.env.KEYCLOAK_CLIENT_ID,
        scope: ['openid', 'profile', 'email'],
        codeChallengeMethod: 'S256',
      }
    }

I was able to login on Keycloak with this settings but is not pulling the user information, did you had the same issue?.

I'm not using userinfo endpoint, only jwt content for my case

@eramosr16 i am using this config

auth: {
    strategies: {
      keycloak: {
        scheme: '~/plugins/keycloak.ts',
        endpoints: {
          token:
            'https://auth.host.tld/auth/realms/myRealm/protocol/openid-connect/token',
          authorization:
            'https://auth.host.tld//auth/realms/myRealm/protocol/openid-connect/auth',
          userInfo:
            'https://auth.host.tld/auth/realms/myRealm/protocol/openid-connect/userinfo',
          logout:
            'https://auth.host.tld/auth/realms/myRealm/protocol/openid-connect/logout',
        },
        token: {
          property: 'access_token',
          type: 'Bearer',
          name: 'Authorization',
        },
        refreshToken: {
          property: 'refresh_token',
        },
        grantType: 'authorization_code',
        responseType: 'code',
        codeChallengeMethod: 'S256',
        clientId: 'myApp-backend-ui',
        scope: ['openid', 'profile', 'roles'],
      },
    },
  },

But never checked the values if they are used, but they are available

Also having issues with keycloak logout redirect not working but can't seem to make any of the suggested solutions work with the latest version of auth-next ^5.0.0-1611071776.3646657.

I tried using the example provided here https://gist.github.com/alvistar/b7adad5eb086915a67ad316452e86b8a but none of those import paths exist in the latest version of auth-next.

The proposed solution to create your own Scheme https://auth.nuxtjs.org/guide/scheme#creating-your-own-scheme doesn't seem to work. When I load my custom theme ...

import { Oauth2Scheme } from "@nuxtjs/auth-next"

export default class KeycloakScheme extends Oauth2Scheme {
  ...
}

I get Class extends value undefined is not a constructor or null. Additionally there are build errors ...

This dependency was not found:

* fs in ./node_modules/@nuxtjs/auth-next/dist/module.js, ./node_modules/hasha/index.js

Anyone managed to find a solution that works?

Cheers!

Hi @m2de! When creating custom schemes, you should import from ~auth/runtime

Like this:

import { Oauth2Scheme } from "~auth/runtime"

export default class KeycloakScheme extends Oauth2Scheme {
  ...
}

Hi @JoaoPedroAS51. The ~auth/runtime import doesn't work for me. Did you have to fix it ?

Hi @jyhubert! Can you tell me which version of auth module you are using and what you are trying to import? Also, are you using typescript?

@JoaoPedroAS51 @nuxtjs/auth-next with typescript. I have overriden the fetchUser method, through the Oauth2Scheme inheritance. It works fine. But I had to go through the import from @nuxts/auth-next/dist/runtime.
So, I am trying to figure out what was the import from ~auth/runtime.

@jyhubert We're aware that there's an error with imports. For now, you can import from ~auth/runtime and add a shim.d.ts to fix the types.

shim.d.ts

declare module '~auth/runtime' {
  // Here you declare what you're trying to import from `~auth/runtime`
  export type { Oauth2Scheme } from '@nuxtjs/auth-next'
}

Then in your scheme you import like import { Oauth2Scheme } from '~auth/runtime'

Thanks @JoaoPedroAS51. I will try it out.

Working with "@nuxtjs/auth-next": "^5.0.0-1613647907.37b1156"

nuxt.config.js

  auth: {
    strategies: {
      keycloak: {
        scheme: '~/plugins/keycloak.js',

plugins/keycloak.js

import { Oauth2Scheme, encodeQuery } from '@nuxtjs/auth-next/dist/runtime'

export default class KeycloakScheme extends Oauth2Scheme {
  logout () {
    if (this.options.endpoints.logout) {
      const opts = {
        client_id: this.options.clientId,
        post_logout_redirect_uri: this.logoutRedirectURI
      }
      const url = this.options.endpoints.logout + '?' + encodeQuery(opts)
      window.location.replace(url)
    }
    return this.$auth.reset()
  }
}

node_modules/@nuxtjs/auth-next/dist/runtime.mjs

// Append to last
export { encodeQuery };

Hi,

There is different problems addressed in this topic but I'm encountering the problem with the logout URL.

  1. For sure, forcing redirect_uri directly in logout endpoint, with or without the option logoutRedirectUri didn't work. Not the logout and not the redirection.

  2. Keeping the endpoint like this: ${KEYCLOAK.SERVER}/auth/realms/${KEYCLOAK.REALM}/protocol/openid-connect/logout and setting the option logoutRedirectUri makes the logout work but I stay on a blank page. As @besteinert said, manually changing logout_uri by redirect_uri works. The redirect is done.

@cs8898 summed it very well, Keycloak is waiting for redirect_uri parameter name and auth-module use logout_uri parameter name.

I tried @xeniumlee version of @alvistar solution and yes I have logout and redirection but:

  • Editing a node_modules can't be a permanent solution (!), each time I'll have to do a npm install it'll be gone?!
  • And it seems to mess up with login, the first time I log in I stay on the callback URL, I need to go back to root URL and fire again the login (without writing ID/pwd this time) to be able to access the app.

Any help will really be appreciated please :)

EDIT, I finally got it to work!

  • Instead of doing the export { encodeQuery }; in the node_module, I copied the function in the keycloak.js plugin.
  • In const opts, I didn't redefined the client_id and used redirect_uri instead of post_logout_redirect_uri
    Hope it can help others!
import { Oauth2Scheme } from '@nuxtjs/auth-next/dist/runtime'

function encodeQuery(queryObject) {
  return Object.entries(queryObject)
    .filter(([_key, value]) => typeof value !== 'undefined')
    .map(
      ([key, value]) =>
        encodeURIComponent(key) +
        (value != null ? '=' + encodeURIComponent(value) : '')
    )
    .join('&')
}

export default class KeycloakScheme extends Oauth2Scheme {
  logout() {
    if (this.options.endpoints.logout) {
      const opts = {
        redirect_uri: this.logoutRedirectURI,
      }
      const url = this.options.endpoints.logout + '?' + encodeQuery(opts)
      window.location.replace(url)
    }
    return this.$auth.reset()
  }
}

Sorry, I forgot a bit the community. But indeed, something is not working well with the redirection.
Thanks, guys for sharing.
In our project, we have overriden the fetchUser and logout methods.
We use Red Hat SSO as authentication provider.

Concerning the encodeQuery, it's better to write his own method like you did @cdefy.
Another option is to use stringify in querystring node module.

import { stringify as encodeQuery } from 'querystring'
import { Oauth2Scheme } from '@nuxtjs/auth-next/dist/runtime'

export default RhssoScheme extends Oauth2Scheme {

   logout (): void {
      const { endpoints: { logout }, clientId: client_id } = this.options
      if (logout) {
         const opts = {
            client_id,
            post_logout_redirect_uri: this.logoutRedirectURI,
         }
         const url = `${logout}?${encodeQuery(opts)}`
         location.replace(url)
      }
      return this.$auth.reset()
   }

}

@cdefy I Just created a "sample" repo to show you how i made it working int the "first" attempt while not editing the node_modules folder

https://github.com/cs8898/nuxt-auth-auth2-keycloak-sample

in an second run i started to edit the whole lib in a way that suites best for me, with some additions the link is in the sample repo (afaik i haven't documented my changes, but there are some nice to have additions)

EDIT

Seems you took the same approche with the plugin

Thanks @jyhubert and @cs8898 for your feedbacks. Yes it's working now, I don't touch it anymore ^^ !

Was this page helpful?
0 / 5 - 0 ratings