nuxt-oidc-auth
nuxt-oidc-auth copied to clipboard
Tracking keycloak problems
This issue is a tracking issue to ensure that keycloak compatibility is as good as possible. It would be great if @DallasHoff could support by providing working and non working versions/configurations, so that we can try to work out either a configuration or a code change to fix the broken ones.
We use Keycloak version 21, and it now works when using tokenRequestType: 'form-urlencoded'.
just raised https://github.com/itpropro/nuxt-oidc-auth/pull/25 and it works for keycloak version 12...
just raised #25 and it works for keycloak version 12...
If that PR made keycloak 12 to work, try setting it to json, as the header from that PR is already automatically set, when form-urlencoded is configured. Your PR would effectively set it to application/json or none.
Hello! During testing Keycloak connection, we've identified the following issues:
All environment variables are correctly read except NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL. Due to Docker usage, we're compelled to manually add this variable in the Dockerfile, which we aim to avoid. We desire automatic determination of redirectUri for Keycloak or the ability to pass query parameters to the login method. Our application requires Keycloak to understand the requesting domain, a functionality we're unsure how to implement currently. Automatic determination of logoutUrl is also desired. To implement logout without confirmation in Keycloak, passing the idToken is necessary. However, setting the exposeIdToken option from the documentation leads to endless redirection between Keycloak and the application. Additionally, idToken cannot be set via environment variables, and post_logout_redirect_uri is dynamic for us.
All environment variables are correctly read except NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL. Due to Docker usage, we're compelled to manually add this variable in the Dockerfile, which we aim to avoid.
I ran into this too. For Keycloak, NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL needs to be passed to the baseUrl option in nuxt.config.ts. Because of this, the value needs to be available at build time, not just run time, so you'll need to pass it as a build-arg to the Docker file.
ARG NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL
ENV NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL=${NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL}
Then reference it in nuxt.config.ts.
baseUrl: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL,
The issue is that with how Nitro and thereby Nuxt resolves and merges the config. It is always required to have the specific key in the config, even if it is just with an empty string. If the key doesn't exist in the config, it will never be merged from the environment.
We also have problems with keycloak <= 24.0.3. In one realm we even set up Kerberos in combination with an LDAP. Here we also have the problem that the user is stuck in an endless redirect loop between application and keycloak. Unfortunately we haven't been able to identify the error yet. The calls and responses look like they do without Kerberos but for some reason the composable (oidcAuth) no longer gets the session. Unfortunately, I'm not deep enough into nuxt/nitro to understand where the exchange of session information fails.
+++++++++++++++++++++++++
We solved the problem with the env files as follows:
{
// ...
scripts: {
"build:beta": "npx nuxi build --dotenv .env.beta",
// ...
}
// ...
}
But we would also like it if the user were redirected to where they originally wanted to go after logging in.
The only thing I've been able to find out so far is that the answer from keycloak is the same, only the size of the tokens differs slightly. For the realm with Kerberos, the fetch() call (get session) returns an empty response. So the loop starts again from the beginning. With a realm without Kerberos, everything works. The fetch() then also delivers the session. Here are two more pictures where I installed log output.
with Kerberos
without Kerberos
new findings: the problem seems to be the access token size. In the realm with Kerberos which the users access via LDAP. These users have a variety of roles that increase the access token. But we can't do without it.
@itpropro I found the problem. If the user session object becomes too large due to a lot of data in the token, such as roles, permissions, etc., then the maximum session size of the H3 is exhausted. The result is that no more data is saved in the session, which is why an empty object is returned when “get session” is used. I would therefore recommend only packing the most necessary data into the session. Everything else can be stored with useStorage, which has no limit (as far as I understood correctly).
https://github.com/unjs/h3/issues/649
The issue is that with how Nitro and thereby Nuxt resolves and merges the config. It is always required to have the specific key in the config, even if it is just with an empty string. If the key doesn't exist in the config, it will never be merged from the environment.
We already have it in config with empty value, but still it's needed on build
@itpropro I found the problem. If the user session object becomes too large due to a lot of data in the token, such as roles, permissions, etc., then the maximum session size of the H3 is exhausted. The result is that no more data is saved in the session, which is why an empty object is returned when “get session” is used. I would therefore recommend only packing the most necessary data into the session. Everything else can be stored with useStorage, which has no limit (as far as I understood correctly).
Good find, I would have to look into splitting up the data for that. I can think of some security implications based on that, but I will try to find a good solution for that. Which fields have you experienced to contain that much information? The token shouldn't contain that much information exactly for that reason, which is why most IdPs limit the info it can contain.
@itpropro I found the problem. If the user session object becomes too large due to a lot of data in the token, such as roles, permissions, etc., then the maximum session size of the H3 is exhausted. The result is that no more data is saved in the session, which is why an empty object is returned when “get session” is used. I would therefore recommend only packing the most necessary data into the session. Everything else can be stored with useStorage, which has no limit (as far as I understood correctly). unjs/h3#649
Good find, I would have to look into splitting up the data for that. I can think of some security implications based on that, but I will try to find a good solution for that. Which fields have you experienced to contain that much information? The token shouldn't contain that much information exactly for that reason, which is why most IdPs limit the info it can contain.
The problem for us is the AccessToken. This also includes the roles. Thus, we control what the user is allowed to see. In addition, the AccessToken with the roles is required for the underlying APIs, which also use the roles to check what the user gets back.
For our case I removed the AccessToken (exposeAccessToken = false). Because for us this alone is around 2500-4500 characters long. The size of this is due to the user roles. A user can have up to 50-60 roles.
I rewrote the session for us so that the AccessToken now comes from the persistentSession. In addition, I implemented the redirection to the path before login.
@itpropro I found the problem. If the user session object becomes too large due to a lot of data in the token, such as roles, permissions, etc., then the maximum session size of the H3 is exhausted. The result is that no more data is saved in the session, which is why an empty object is returned when “get session” is used. I would therefore recommend only packing the most necessary data into the session. Everything else can be stored with useStorage, which has no limit (as far as I understood correctly). unjs/h3#649
Good find, I would have to look into splitting up the data for that. I can think of some security implications based on that, but I will try to find a good solution for that. Which fields have you experienced to contain that much information? The token shouldn't contain that much information exactly for that reason, which is why most IdPs limit the info it can contain.
The problem for us is the AccessToken. This also includes the roles. Thus, we control what the user is allowed to see. In addition, the AccessToken with the roles is required for the underlying APIs, which also use the roles to check what the user gets back.
For our case I removed the AccessToken (exposeAccessToken = false). Because for us this alone is around 2500-4500 characters long. The size of this is due to the user roles. A user can have up to 50-60 roles.
I rewrote the session for us so that the AccessToken now comes from the persistentSession. In addition, I implemented the redirection to the path before login.
That sounds as if the accessToken is used for a lot of information that should probably be handled by an authorization system, which is why most IdPs limit the size of the token, but I also know a lot of systems that misuse the accessToken for information exchanges like that. If you don't mind, it would be great if you could share the code changes in a PR, so that others can also use this workaround if they have a similar problem. If there is still some abstraction work to do, feel free to submit a draft PR and I take care of the rest.
Ok my ticket (https://github.com/itpropro/nuxt-oidc-auth/issues/28) is a duplicate of this ticket, just noticed this:
Workaround
providers: { keycloak: { audience: 'account', baseUrl: '[keycloak.baseUrl]', clientId: '', clientSecret:'', redirectUri: '', exposeAccessToken: true, }, },
On startup replace the keycloak.baseUrl with the NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL
I have the same problem @Marlight
But I dont have any roles in my token, Im using out of the boxe Keycloak, with fresh user for testing purpose
Im having infinite loop : login page -> keycloak -> login page -> keycloak -> ...
I try another provider
sorry but nuxt3 auth is a real nightmare
I have the same problem @Marlight
But I dont have any roles in my token, Im using out of the boxe Keycloak, with fresh user for testing purpose
Im having infinite loop : login page -> keycloak -> login page -> keycloak -> ...
I try another provider
sorry but nuxt3 auth is a real nightmare
@BeSaad I can understand you. It also took me a lot of time to understand when and where in nuxt. I don't understand everything completely yet either.
The following would be good to know about your problem: How big is your access token? (number of characters) It would also be helpful if you activated the preverse log in the browser's dev tools in the network tab. Show me which calls he really makes.
Have you perhaps disabled cookies?
I guess the token is simply named "oidc" and is encrypted. In that case 667 characters.
You can see here the cookies
And the network tab shows the loop
This redirection is anormal I guess (
(cookies are enabled)
I guess the token is simply named "oidc" and is encrypted. In that case 667 characters.
You can see here the cookies
And the network tab shows the loop
This redirection is anormal I guess (
(cookies are enabled)
@BeSaad I think your Keycloak provider is configured incorrectly. The redirectUri must point to the callback endpoint of your Nuxt application. The nuxt-oidc-auth module adds this.
This is how I configured my keycloak provider:
const providerConfig = {
keycloak: {
baseUrl: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL,
clientId: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID,
clientSecret: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_SECRET,
redirectUri: process.env.ORIGIN + process.env.BASE_URL + 'auth/keycloak/callback',
logoutUrl: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL + 'protocol/openid-connect/logout',
exposeAccessToken: true, // optional, if you need the access token in your nuxt app
},
}
my .env file looks like:
ORIGIN=https://localhost:3002
BASE_URL=/
# OIDC MODULE CONFIG
NUXT_OIDC_SESSION_SECRET=xxx
NUXT_OIDC_TOKEN_KEY=xxx
NUXT_OIDC_AUTH_SESSION_SECRET=xxx
# KEYCLOAK PROVIDER CONFIG
NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL=https://xxx.xxxx.de/realms/xxxx/
NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID=xxx
NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_SECRET=xxx
I hope this helps you.
Its better because now I have logs
[nuxt-oidc-auth] WARN [nuxt-oidc-auth]: Persistent user session not found
[nuxt-oidc-auth] ℹ [nuxt-oidc-auth]: Session expired
But It stills redirecting me to the login
Thanks for your help
I deleted the realm and recreated one from scratch, Its now working, thanks again for your help
Im having those logs, even if I activated offline_access scope for the client
[nuxt-oidc-auth] WARN [nuxt-oidc-auth]: Persistent user session not found
[nuxt-oidc-auth] ℹ [nuxt-oidc-auth]: Session expired
ERROR [nuxt] [request error] [unhandled] [500] Cannot read properties of null (reading 'refreshToken')
at refreshUserSession (./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/nuxt-oidc-auth/dist/runtime/server/utils/session.mjs:29:54)
at requireUserSession (./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/nuxt-oidc-auth/dist/runtime/server/utils/session.mjs:80:9)
at Object.handler (./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/nuxt-oidc-auth/dist/runtime/server/api/session.get.mjs:4:19)
at async ./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:1962:19
at async Object.callAsync (./node_modules/.pnpm/[email protected]/node_modules/unctx/dist/index.mjs:72:16)
at async toNodeHandle (./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2249:7)
at async ufetch (./node_modules/.pnpm/[email protected]/node_modules/unenv/runtime/fetch/index.mjs:9:17)
at async $fetchRaw2 (./node_modules/.pnpm/[email protected]/node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:222:26)
at async $fetchRaw2 (./node_modules/.pnpm/[email protected]/node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:263:14)
at async $fetch2 (./node_modules/.pnpm/[email protected]/node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:268:15)
[nuxt-oidc-auth] ℹ [nuxt-oidc-auth]: Session expired
I guess my realm has a wrong config?
Im having those logs, even if I activated offline_access scope for the client
[nuxt-oidc-auth] WARN [nuxt-oidc-auth]: Persistent user session not found [nuxt-oidc-auth] ℹ [nuxt-oidc-auth]: Session expired ERROR [nuxt] [request error] [unhandled] [500] Cannot read properties of null (reading 'refreshToken') at refreshUserSession (./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/nuxt-oidc-auth/dist/runtime/server/utils/session.mjs:29:54) at requireUserSession (./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/nuxt-oidc-auth/dist/runtime/server/utils/session.mjs:80:9) at Object.handler (./node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/nuxt-oidc-auth/dist/runtime/server/api/session.get.mjs:4:19) at async ./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:1962:19 at async Object.callAsync (./node_modules/.pnpm/[email protected]/node_modules/unctx/dist/index.mjs:72:16) at async toNodeHandle (./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2249:7) at async ufetch (./node_modules/.pnpm/[email protected]/node_modules/unenv/runtime/fetch/index.mjs:9:17) at async $fetchRaw2 (./node_modules/.pnpm/[email protected]/node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:222:26) at async $fetchRaw2 (./node_modules/.pnpm/[email protected]/node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:263:14) at async $fetch2 (./node_modules/.pnpm/[email protected]/node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:268:15) [nuxt-oidc-auth] ℹ [nuxt-oidc-auth]: Session expiredI guess my realm has a wrong config?
Same problem here, from my dev tools and debugger I see that Keycloak (version 24.0.1) is not returning a refresh token. Even though I enabled "Use refresh token" from the clients "OpenID Connect Compatibility Modes". I also tried adding the scope "offline_access" and still get no refresh token.
My plugin config:
oidc: {
defaultProvider: 'keycloak',
providers: {
keycloak: {
audience: 'account',
baseUrl: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL,
clientId: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID,
clientSecret: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_SECRET,
redirectUri: process.env.BASE_URL + '/auth/keycloak/callback',
logoutUrl: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL + '/protocol/openid-connect/logout',
exposeAccessToken: true
}
},
session: {
expirationThreshold: 60
}
},
Edit: I checked back on my Keycloak Configuration and wrote a little tool (just an express server creating the code challenge and having a callback to show if refresh token is included in the response) to debug the authentication process and then digged deeper and debugged the callbackEventHandler from your lib. What I first thought is wrong. There's a refresh token present in the token response from keycloak, it also is set in the user session but somehow in refreshUserSession the persistentSession is not retrieved.
Same looping issue here, after a while the nuxt app takes me to the /auth/login page and when i try to login it goes to keycloak(doesn't ask for id pass as am already logged in) and redirects back to /auth/login
a small workaround i found is that if i goto /hardlogout then am able to login from /auth/login > keycloak login page(by putting in id pass on keycloak page) > back to / and everything works as normal for a while
My Config :
routeRules: {
"/backendapi/": {
proxy: {
to: ${process.env.NUXT_APP_BACKEND_URL}/api/**,
},
},
"/keycloakapi/": {
proxy: {
to: ${process.env.NUXT_OIDC_ADMIN_KEYCLOAK_URL}/**,
},
},
"/hardlogout": {
redirect: ${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL}/protocol/openid-connect/logout?post_logout_redirect_uri=${process.env.NUXT_APP_DOMAIN}/auth/keycloak/logout&client_id=${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID},
},
},
oidc: {
defaultProvider: "keycloak",
providers: {
keycloak: {
tokenRequestType: "form-urlencoded",
baseUrl: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL as string,
clientId: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID as string,
clientSecret: process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_SECRET as string,
redirectUri: process.env.NUXT_APP_DOMAIN + "/auth/keycloak/callback",
exposeAccessToken: true,
},
},
middleware: {
globalMiddlewareEnabled: true,
customLoginPage: true,
},
session: {
automaticRefresh: true,
expirationCheck: true,
expirationThreshold: 3600,
maxAge: 1800,
},
},
can anyone of the maintainers tell me how can i catch this error ERROR [nuxt] [request error] [unhandled] [500] Failed to refresh token coming from runtime/server/utils/oidc
and i can redirect to
${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL}/protocol/openid-connect/logout?post_logout_redirect_uri=${process.env.NUXT_APP_DOMAIN}/auth/keycloak/logout&client_id=${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID}
this solves the problem since after this logout, the user is able to login to the dashboard instead of the login loop.
@raj-saroj-vst-au4 I added:
{
...
logoutRedirectParameterName: `client_id=${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID}&post_logout_redirect_uri`,
...
}
This allowed me to logout the user with keycloak logout confirmation screen. Im trying to skip this screen but Im having trouble getting the idToken even with exposeIdToken: true. Im getting this error in the console when trying to log in.
Set-Cookie header is ignored in response from url: https://localhost:3000/auth/keycloak/callback. The combined size of the name and value must be less than or equal to 4096 characters.
I assume nuxt-oidc-auth module is not able to retrieve access_token here and therefore authentication logic breaks.
I need the idToken to logout without confirmation screen using id_token_hint and post_logout_redirect_uri query parmas attached to logout endpoint. If anyone knows a way I would be very grateful 😄
${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL}/protocol/openid-connect/logout?post_logout_redirect_uri=${process.env.NUXT_APP_DOMAIN}/auth/keycloak/logout&client_id=${process.env.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID}
this does exactly what you're looking for.... you may refer to your .well-known/openid-configuration and refer the url from there.
As stated in https://github.com/unjs/h3/issues/649, the cookie limitation is a given and should be circumvented by only storing essential data in a JWT. Using JWTs for data transfer is a bad practice as well as having non essential user information in there. As by the OIDC/OAuth standards, the AccessToken should never be parsed except for validation and even then only if you application is the defined audience.
The exposeIdToken and exposeAccessToken options were just added for absolute edge cases and should be avoided if possible. If you need user data, please request that from a database or use the accessToken (after removing everything not necessary) against a userInfo endpoint.
If there are still problems with other parts of the Keycloak implementation besides too large token sizes, feel free to open a new issue for that.
@Mattymoo007 @raj-saroj-vst-au4 @BeSaad Version 0.17.0 completely reworked the token logic so that the id and access token is no longer stored within the session, but the exposeIdToken and exposeAccessToken properties will work as before.
This completely avoids the token from getting too large.
@itpropro Thank you for your effort, this is the most promising library for Nuxt concerning auth,
Im still facing one small issue, now im able to login properly and I have a token
But when I try to refresh the user Im getting:
Set-Cookie header is ignored in response from url: http://localhost:3000/api/_auth/refresh. The combined size of the name and value must be less than or equal to 4096 characters.


(cookies are enabled)