Closed majonga88 closed 3 years ago
@majonga88 I think this issue is invalid, in such cases you need to use quarkus.oidc.token.lifespan-grace
to deal with some clock skews etc
It's much better thanks, i have no longer the issue, but i experienced now a strange redirection when i'm trying to connect to my application. It redirect to the auth0 login page after every login. The actual Auth0 configuration work on a personal SpringBoot application that i use.
2020-10-29 22:59:42,607 DEBUG [io.net.han.cod.com.ZlibCodecFactory] (vert.x-eventloop-thread-34) -Dio.netty.noJdkZlibDecoder: false
2020-10-29 22:59:42,607 DEBUG [io.net.han.cod.com.ZlibCodecFactory] (vert.x-eventloop-thread-34) -Dio.netty.noJdkZlibEncoder: false
2020-10-29 22:59:42,632 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-29 22:59:42,632 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) q_auth cookie 'max-age' parameter is set to 1800
2020-10-29 22:59:46,486 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-2) [id: 0x7432ccf7, L:/192.168.1.10:64298 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-29 22:59:47,553 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) Token request redirect_uri parameter: http://localhost:8080/
2020-10-29 22:59:47,617 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-34) [id: 0xc971cdef, L:/192.168.1.10:64301 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-29 22:59:47,877 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-29 22:59:47,877 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) q_auth cookie 'max-age' parameter is set to 1800
2020-10-29 22:59:54,138 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) Token request redirect_uri parameter: http://localhost:8080/
2020-10-29 22:59:54,168 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-34) [id: 0x05f79dab, L:/192.168.1.10:64307 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-29 22:59:54,411 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-29 22:59:54,411 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-34) q_auth cookie 'max-age' parameter is set to 1800
2020-10-29 23:00:01,751 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-2) [id: 0x191346c8, L:/192.168.1.10:64315 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-29 23:00:17,389 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-2) [id: 0x3f484c24, L:/192.168.1.10:64349 - R:dev-0-77r6yl.auth0.com/104.16.185.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256```
@majonga88 can you try quarkus.oidc.authentication.cookie-path=/
?
@sberyozkin I have the same behaviour, if you want i can share with you in private message my Auth0 credentials to check, here is the logs :
2020-10-30 13:30:38,873 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:30:38,873 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) q_auth cookie 'max-age' parameter is set to 1800
2020-10-30 13:30:47,881 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-2) [id: 0xa092f812, L:/192.168.1.10:49894 - R:dev-0-77r6yl.auth0.com/104.16.185.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-30 13:31:03,069 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Token request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:31:03,101 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-15) [id: 0x39721a57, L:/192.168.1.10:49919 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-30 13:31:03,146 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-2) [id: 0xf6430673, L:/192.168.1.10:49921 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-30 13:31:03,369 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:31:03,369 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) q_auth cookie 'max-age' parameter is set to 1800
2020-10-30 13:31:07,262 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Token request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:31:07,320 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-15) [id: 0x91eb085b, L:/192.168.1.10:49935 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-30 13:31:07,566 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:31:07,566 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) q_auth cookie 'max-age' parameter is set to 1800
2020-10-30 13:31:12,778 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Token request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:31:12,803 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-15) [id: 0xec7376fa, L:/192.168.1.10:49942 - R:dev-0-77r6yl.auth0.com/104.16.184.248:443] HANDSHAKEN: protocol:TLSv1.2 cipher suite:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
2020-10-30 13:31:13,061 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) Authentication request redirect_uri parameter: http://localhost:8080/
2020-10-30 13:31:13,061 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-15) q_auth cookie 'max-age' parameter is set to 1800
@majonga88 Can you please fork this project, https://github.com/quarkusio/quarkus-quickstarts/tree/1.9.1.Final/security-openid-connect-web-authentication-quickstart, update these properties with Auth0 connection url, and see if you can reproduce it ? I'm assuming you are on the latest Quarkus. Or create a reproducer project in your githib account and ping me privately at https://quarkusio.zulipchat.com/
@sberyozkin Ok so i fork your project security-openid-connect-web-authentication-quickstart
, the authentification works without :
quarkus.oidc.roles.source=userinfo
or quarkus.oidc.roles.source=accesstoken
parameters.
I go to this url : http://localhost:8080/tokens. I have an error like we saw on #8396 which is normal without quarkus.oidc.roles.source
. See the error log below :
2020-10-31 17:36:25,926 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /tokens failed, error id: 0c347fc3-72a8-43df-a8ca-9b1d476b3947-1: org.jboss.resteasy.spi.UnhandledException: io.quarkus.oidc.OIDCException: Opaque access token can not be converted to JsonWebToken
at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:106)
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:372)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:218)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:519)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:131)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:37)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:94)
at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:231)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at java.base/java.lang.Thread.run(Thread.java:829)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
When i tried with quarkus.oidc.roles.source=userinfo
it loop on authentification page of Auth0 and quarkus.oidc.roles.source=accesstoken
send me a 403 forbidden with this kind of url : http://localhost:8080/?code=57VpewRDgvBCLIAs&state=457304d5-9917-484a-853c-e0d6ee393b50.
The branch is here : https://github.com/majonga88/security-quarkus/tree/fork
Can you help me, if you want please ping me zulipchat i'm on.
@majonga88 I think I understand now why you are seeing a loop. I believe it happens due to the wrong exception beng reported in case of the user-info request failure, I'm pretty sure I saw it but did not fix yet, so I'll take care of it as part of this issue.
The reason the userinfo request fails is because the access token does not have enough permissions for it.
Also, since the access token is binary, you get a scope
returned in the introspection response and whatever value is returned there does not match what you set in RolesAllowed
. You can inspect what is really returned there by setting a breakpoint in a custom SecurityIdentityAugmentor
, see https://quarkus.io/guides/security-customization#security-identity-customization and https://github.com/quarkusio/quarkus-security/blob/master/src/main/java/io/quarkus/security/identity/SecurityIdentity.java#L53
@sberyozkin Ok i deep looking the documentation of Auth0 about access token here and openid scopes here
When i do it manually :
We need to get an authorization code from the authorization server for generate the access token :
https://dev-0-77r6yl.auth0.com/authorize?audience=https://quickstarts/api&scope=openid&response_type=code&client_id=hSbuxfeTI25DHp0F9OPW1XbQe42ONiD6&redirect_uri=http://localhost:8080/&state=xbc
After that, we are redirect and we get a code and a state on this url : http://localhost:8080/?code=XK2RfoAaUPwlKmal&state=xbc
We keep this code and we put it on this query for getting access token :
curl --location --request POST 'https://dev-0-77r6yl.auth0.com/oauth/token' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=hSbuxfeTI25DHp0F9OPW1XbQe42ONiD6' \
--data-urlencode 'client_secret=XXXXX' \
--data-urlencode 'code=XK2RfoAaUPwlKmal' \
--data-urlencode 'redirect_uri=http://localhost:8080/'
After executed the request, i have my access token generated and when i put this one on header of https://dev-0-77r6yl.auth0.com/userinfo, i get this result :
{
"sub": "google-oauth2|116720606290557643328"
}
So i compare this manual authentification flow with Quarkus one, when we loop i have this group of request executed :
My application.properties is like this :
quarkus.oidc.auth-server-url=https://dev-0-77r6yl.auth0.com
quarkus.oidc.client-id=hSbuxfeTI25DHp0F9OPW1XbQe42ONiD6
quarkus.oidc.credentials.secret=XXXXXX
quarkus.oidc.token.lifespan-grace=10
quarkus.oidc.roles.source=accesstoken
quarkus.oidc.application-type=web-app
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
quarkus.log.level=ALL
Something it's blocked here with this request on Quarkus :
The error in console log, it's about decoding an opaque token i suppose, but i don't really know why.
2020-11-02 01:02:09,319 FINEST [io.ver.ext.aut.oau.imp.OAuth2UserImpl] (vert.x-eventloop-thread-20) Cannot decode token:: java.lang.RuntimeException: Not enough or too many segments
at io.vertx.ext.jwt.JWT.decode(JWT.java:261)
at io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl.decodeToken(OAuth2UserImpl.java:182)
at io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl.decodeToken(OAuth2UserImpl.java:156)
at io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl.init(OAuth2UserImpl.java:73)
at io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl.setAuthProvider(OAuth2UserImpl.java:100)
at io.vertx.ext.auth.oauth2.impl.OAuth2UserImpl.<init>(OAuth2UserImpl.java:49)
at io.vertx.ext.auth.oauth2.impl.OAuth2TokenImpl.<init>(OAuth2TokenImpl.java:55)
at io.vertx.ext.auth.oauth2.impl.flow.AuthCodeImpl.lambda$getToken$0(AuthCodeImpl.java:87)
at io.vertx.ext.auth.oauth2.impl.flow.AbstractOAuth2Flow.lambda$getToken$0(AbstractOAuth2Flow.java:148)
at io.vertx.ext.auth.oauth2.impl.OAuth2API.lambda$null$1(OAuth2API.java:129)
at io.vertx.core.http.impl.HttpClientResponseImpl$BodyHandler.notifyHandler(HttpClientResponseImpl.java:292)
at io.vertx.core.http.impl.HttpClientResponseImpl.lambda$bodyHandler$0(HttpClientResponseImpl.java:193)
at io.vertx.core.http.impl.HttpClientResponseImpl.handleEnd(HttpClientResponseImpl.java:248)
at io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.lambda$beginResponse$0(Http1xClientConnection.java:483)
at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:237)
at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:127)
at io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.endResponse(Http1xClientConnection.java:502)
at io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.access$000(Http1xClientConnection.java:241)
at io.vertx.core.http.impl.Http1xClientConnection.handleResponseEnd(Http1xClientConnection.java:641)
at io.vertx.core.http.impl.Http1xClientConnection.handleHttpMessage(Http1xClientConnection.java:601)
at io.vertx.core.http.impl.Http1xClientConnection.handleMessage(Http1xClientConnection.java:575)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:366)
at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:43)
at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:229)
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:163)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1518)
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1267)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1314)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:829)
@majonga88 I'm getting a bit confused. I think you said that it starts looping if you configure it to request the userinfo
, but when you try to set the source of roles to access token it is 404.
Can you clarify please ?
What happens if you remove quarkus.oidc.roles.source=accesstoken
And does Auth0
support the introspection of the tokens ?
thanks
@sberyozkin If i remove this quarkus.oidc.roles.source=accesstoken
it works, i go to Auth0 authentification page and after it redirect to the index.html.
Auth0 doesn't support the introspection of the tokens : https://community.auth0.com/t/oidc-dynamic-client-registration-token-introspection-url/37503
@majonga88 OK, thanks, so all is quite clear now:
you get a redirect loop if you request the userinfo - as I said the reason behind it is that when the userinfo request fails we report the wrong exception (the one which requires an authentication challenge - which in terms of OIDC code flow is a redirect to Auth0 in your case - but since you've already authenticated there Auth0 is silently redirecting you back, and it starts looping). I'll make sure a user gets 401 in such cases.
In the code flow you have 2 tokens, ID and access token. In your case you have a binary/opaque access token. The only way to verify it is to use an introspection endpoint which Auth0 does not support. This is not a problem usually as long as you don't use this access token but when you say quarkus.oidc.roles.source=accesstoken
what you tell Quarkus OIDC is, because it is a binary token, is to introspect this token and check the introspection response, which obviously fails.
So the only way for you to pass the roles is to update Auth0 somehow to include them in the ID token.
As I said I'm going to fix the redirect loop issue which is caused by the userinfo request failure. What remains unclear though is why a user info request fails in your case.
Lets get back to this comment.
Can you type here the curl command which you use to request the userinfo with that access token. Thanks
@sberyozkin Thank you for that. The curl :
curl --location --request GET 'https://dev-0-77r6yl.auth0.com/userinfo' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik9UVTRNekE0TjBFM1JETXhNRVkwTTBGQk16RTVSRVEzTVVVeU9EWkNPVEk1TTBFM1JVVkRSQSJ9.eyJpc3MiOiJodHRwczovL2Rldi0wLTc3cjZ5bC5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMTY3MjA2MDYyOTA1NTc2NDMzMjgiLCJhdWQiOlsiaHR0cHM6Ly9xdWlja3N0YXJ0cy9hcGkiLCJodHRwczovL2Rldi0wLTc3cjZ5bC5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNjA0MjczNDE3LCJleHAiOjE2MDQzNTk4MTcsImF6cCI6ImhTYnV4ZmVUSTI1REhwMEY5T1BXMVhiUWU0Mk9OaUQ2Iiwic2NvcGUiOiJvcGVuaWQiLCJwZXJtaXNzaW9ucyI6WyJyZWFkOmFwcG9pbnRlbWVudHMiLCJyZWFkOm1lc3NhZ2VzIl19.PSdTuujLTToIfOm6-lB1sRdNKqedSZ7OJPOeYPTc4oJVTYi6O6rqehyAbclW8A2OItziNXJNXkimUduwXab1KMqrTjWYqkkGsqKc1xRz3B7A1hPFxis3fsv2dP-qAI44lH7RS-AglD81cDxjzhkh6KVuizzBTkPdwHfHdb9a7Rd71yweM2CtX7ack9FyA0pamBaNayVKJBSA8CJGuFjNFa74204N2mk5fS6lGnzqScRQLo4f1KhcZIQOmJ5uYjjxa4SoLZcA3vTmq6RmjGn9gsXgS2-stIX8AxcAJ_G3gajsSyzvfX2VgyEFUgQzZG31A-q9sUrooDdw_OGQKK-OkA'
I will open a ticket to bring the support of introspection token in Auth0 side.
@majonga88 OK, so this token is a JWT token while we are assuming you are dealing with the binary access token. In that comment where you typed a curl request to the token endpoint, what is returned, can you type it here ?
@sberyozkin Here is the curl request for token endpoint :
curl --location --request POST 'https://dev-0-77r6yl.auth0.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=hSbuxfeTI25DHp0F9OPW1XbQe42ONiD6' \
--data-urlencode 'client_secret=XXXXX' \
--data-urlencode 'code=XK2RfoAaUPwlKmal' \
--data-urlencode 'redirect_uri=http://localhost:8080/'
And result :
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik9UVTRNekE0TjBFM1JETXhNRVkwTTBGQk16RTVSRVEzTVVVeU9EWkNPVEk1TTBFM1JVVkRSQSJ9.eyJpc3MiOiJodHRwczovL2Rldi0wLTc3cjZ5bC5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMTY3MjA2MDYyOTA1NTc2NDMzMjgiLCJhdWQiOlsiaHR0cHM6Ly9xdWlja3N0YXJ0cy9hcGkiLCJodHRwczovL2Rldi0wLTc3cjZ5bC5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNjA0MjczNDE3LCJleHAiOjE2MDQzNTk4MTcsImF6cCI6ImhTYnV4ZmVUSTI1REhwMEY5T1BXMVhiUWU0Mk9OaUQ2Iiwic2NvcGUiOiJvcGVuaWQiLCJwZXJtaXNzaW9ucyI6WyJyZWFkOmFwcG9pbnRlbWVudHMiLCJyZWFkOm1lc3NhZ2VzIl19.PSdTuujLTToIfOm6-lB1sRdNKqedSZ7OJPOeYPTc4oJVTYi6O6rqehyAbclW8A2OItziNXJNXkimUduwXab1KMqrTjWYqkkGsqKc1xRz3B7A1hPFxis3fsv2dP-qAI44lH7RS-AglD81cDxjzhkh6KVuizzBTkPdwHfHdb9a7Rd71yweM2CtX7ack9FyA0pamBaNayVKJBSA8CJGuFjNFa74204N2mk5fS6lGnzqScRQLo4f1KhcZIQOmJ5uYjjxa4SoLZcA3vTmq6RmjGn9gsXgS2-stIX8AxcAJ_G3gajsSyzvfX2VgyEFUgQzZG31A-q9sUrooDdw_OGQKK-OkA",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik9UVTRNekE0TjBFM1JETXhNRVkwTTBGQk16RTVSRVEzTVVVeU9EWkNPVEk1TTBFM1JVVkRSQSJ9.eyJpc3MiOiJodHRwczovL2Rldi0wLTc3cjZ5bC5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMTY3MjA2MDYyOTA1NTc2NDMzMjgiLCJhdWQiOiJoU2J1eGZlVEkyNURIcDBGOU9QVzFYYlFlNDJPTmlENiIsImlhdCI6MTYwNDI3MzQxNywiZXhwIjoxNjA0MzA5NDE3fQ.sPnmcBlRkXFlwRkd9H1YtKLDNMQAdLhQYMYUY2_SNoFNqClYdG8hxrywbwMYQDPmAWBhuV97kMvuIsPy7lhPCm9jkz2-UpOwtaNvZDff7a_D7yTBqAPSj-tfIiD9JDPOOIzs0y2ri_K_RE-nnKvyuPc91vLWh3aUaJVsiPBoi87Ntb5Xeik9iHvAgdbtVNfl7EbQ7AMJeu2_3pE8s7562DPFCWikvgFnZuoszm8KGHppnbMwUPQOi-IUlLrO-8f3OIBHEOFDi3NwX3j7OM6nMHjeKdvxlBclw1cXbHSUFRDdKWOCSqXDv2RvAFUyN5qSU5GYB13nhyPdsKLKndOz7w",
"scope": "openid",
"expires_in": 86400,
"token_type": "Bearer"
}
@majonga88 OK, thanks, so both ID and access tokens are JWT tokens and indeed it is the access token which contains a custom permissions
claims.
Did you say you had 403 when you were setting quarkus.oidc.roles.source=accesstoken
with your own demo ? Can you try it again, and make sure you set quarkus.oidc.roles.role-claim-path=permissions
and have @RolesAllowed("read:appointements", "read:messages")
?
This access token also has a scope to access a user info endpoint.
In order to verify the JWT access token it is sufficient to have a local JWK set containing a matching kid and no introspection endpoint is needed.
So what we need to verify next is whether a local JWK set is populated and if it does contain the matching key.
The other thing that needs to be confirmed if whether the discovery document returned from Auth0 contains the userinfo
endpoint path, as well as the jwk endpoint path.
Can you add a breakpoint, one is here and check the content of the OAuth2AuthProviderImpl
auth
object ? You will find there the JWK key set, as well as the discovered endpoint paths.
Give it a go please and then we will be able to finally figure out what may be going wrong.
@sberyozkin
Did you say you had 403 when you were setting quarkus.oidc.roles.source=accesstoken with your own demo ?
No i don't have any 403 appears in my chrome console when i do authentification :
Can you try it again, and make sure you set quarkus.oidc.roles.role-claim-path=permissions and have @RolesAllowed("read:appointements", "read:messages") ?
I try it but i have the same behaviour, it loop on authentification with this error : Cannot decode token:: java.lang.RuntimeException: Not enough or too many segments
Can you add a breakpoint, one is here and check the content of the OAuth2AuthProviderImpl auth object ?
Here is the result.
The config part in auth object :
The jwk part in auth object :
@majonga88
403
is mentioned here:
https://github.com/quarkusio/quarkus/issues/13023#issuecomment-719957210
What was that about then ?
OK, thanks for debugging it, all seems to be correct, so where is Not enough or too many segments
coming from ?
It can only happen if a token is not a signed JWT token. It just looks like what you get with curl does not correspond to what you get with Quarkus.
Since you are debugging it, can you please set a breakpoint here - this will show you the token values, and here and see what happens ?
Thanks
@sberyozkin The 403 appears when i execute this request (http://localhost:8080/?code=57VpewRDgvBCLIAs&state=457304d5-9917-484a-853c-e0d6ee393b50) directly on browser it's just a check for me to see what's going on, it's not an issue on Quarkus side here.
For debugging :
Since you are debugging it, can you please set a breakpoint here
Before this breakpoint i already have the error : Cannot decode token:: java.lang.RuntimeException: Not enough or too many segments
Then here :
context.put("access_token", opaqueAccessToken);
I have an opaqueAccessToken : wilUz6rDryuB0NOAr30PaDDhGYKU6CDT
and here and see what happens :
On this portion of code :
if (request.getToken() instanceof IdTokenCredential && (resolvedContext.oidcConfig.authentication.verifyAccessToken || resolvedContext.oidcConfig.roles.source.orElse((Object)null) == Source.accesstoken)) {
vertxContext.put("code_flow_access_token_result", verifyCodeFlowAccessToken(vertxContext, request, resolvedContext));
}
The condition is true and we execute this function verifyCodeFlowAccessToken(vertxContext, request, resolvedContext)
, an AuthenticationFailedException
i've never seen before appears here : io.vertx.core.impl.NoStackTraceThrowable: Invalid path
.
The reason is we go into OAuth2TokenImpl
on authenticate
method and we introspect the opaque token but we don't have an intropection path in our configuration (application.properties
) because Auth0 doesn't support introspection.
Regarding the documentation of Auth0, about opaque token management they said this :
Opaque access tokens are tokens in a proprietary format that you cannot access and typically contain some identifier to information in a server's persistent storage. To validate an opaque token, the recipient of the token needs to call the server that issued the token. In Auth0's case, opaque tokens can be used with the /userinfo endpoint to return a user's profile. If you receive an opaque Access Token, you don't need to validate it. You can use it with the /userinfo endpoint, and Auth0 takes care of the rest.
Reference : https://auth0.com/docs/tokens/access-tokens
So, for me the opaque token support is possible on Quarkus if we use the /userinfo endpoint.
@majonga88
So if you have an opaqueAccessToken : wilUz6rDryuB0NOAr30PaDDhGYKU6CDT
then why are you posting the curl examples where JWT tokens are used :-) ? We are just going in circles while in fact you have an opaque token which is impossible to verify because Auth0 has no introspection endpoint.
So setting the access token as the source of roles in your case is just not going to work for now as we can't get anything out of it anyway.
Re the userinfo - we don;t verify the access token if you need to get the userinfo, can you try this opaque access token to get the userinfo from curl ? The docs you have linked to is one thing, but whether this opaque access token is allowed to do it or not needs to be verified. Please type here the curl command, thanks
Or set a breakpoint here
@sberyozkin Sorry, I withdraw what I said previously, for the 403. I got this error when i tried to authenticate with userinfo parameter. For this, i change quarkus.oidc.roles.source=accesstoken
to quarkus.oidc.roles.source=userinfo
, and i changed the signing algorithm RS256 to HS256 on Auth0.
And i got this 403 when the application trying to redirecting on this url : http://localhost:8080/?code=KhcuMPIzqaSfWtbt&state=c308ebc3-367b-4dfe-b572-1993e34c1481
can you try this opaque access token to get the userinfo from curl ?
curl --location --request GET 'https://dev-0-77r6yl.auth0.com/userinfo' \
--header 'Authorization: Bearer wilUz6rDryuB0NOAr30PaDDhGYKU6CDT'
I got this result :
{
"sub": "google-oauth2|116720606290557643328"
}
Or set a breakpoint here
I didn't go into. I'm stopped before on this url : http://localhost:8080/?code=KhcuMPIzqaSfWtbt&state=c308ebc3-367b-4dfe-b572-1993e34c1481 with an 403.
@majonga88 np, OK, 403
is logical because as you see, the userinfo response contains no roles information at all.
The likely reason you did not get the breakpoint activating is because you may have not set quarkus.oidc.authentication.user-info-required
. But I'd not expect anything else but 403 here anyway, so lets not continue investigating it.
But what really remains to figure out in this issue is when exactly you are getting this redirection loop. I thought it was caused by the failure in the userinfo request but I'm not sure now. Can you please reproduce that, show which configuration you used, and then I'll ask you where to set a breakpoint thanks
Ok so i retest it, i add quarkus.oidc.authentication.user-info-required=true
and i go when i do breakpoint here
I got this token : zlkArY7A_VASvQq5c7aMv-Ut9gPj19fa
I got in there : OAuth2TokenImpl#userInfo and the request is : https://dev-0-77r6yl.auth0.com/userinfo with those headers :
{"Authorization":"Bearer B24Rol6rFLBx-yz1FUG10iUptyqO2Ntl","Accept":"application/json,application/x-www-form-urlencoded;q=0.9"}
And the result was :
{"sub":"google-oauth2|116720606290557643328"}
But i saw here on OAuth2UserImpl#init, we cannot decode the principal on here : this.accessToken = this.decodeToken("access_token");
And when i got through this OAuth2UserImpl#decodeToken, we go here :
if (!trust) {
return this.provider.getJWT().decode((String)opaque);
}
And we got the exception : java.lang.RuntimeException: Not enough or too many segments
My configuration now is this :
quarkus.oidc.auth-server-url=https://dev-0-77r6yl.auth0.com
quarkus.oidc.client-id=H0rm1TvIAz2Ge3kxX8PObxQusguzAvmw
quarkus.oidc.credentials.secret=Togg_7NLhscHugZc8r6KnQJuzhY4ORgIYr61Dw5r0pyyYG5CqjlNbfdBm5wssRLw
quarkus.oidc.token.lifespan-grace=10
quarkus.oidc.roles.source=userinfo
quarkus.oidc.authentication.user-info-required=true
quarkus.oidc.roles.role-claim-path=permissions
quarkus.oidc.application-type=web-app
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
quarkus.log.level=ALL
@majonga88 Good you've confirmed the user info itself can be retrieved, but lets stay focused now on the issue of the loop.
As I've already said quarkus.oidc.roles.source=userinfo
does not make sense in your case.
I'd really like to close this issue now :-), but I'm still interested to find out under what conditions it starts looping. Try to reproduce it
@sberyozkin I'm confused when you said "does not make sense in your case" for userinfo, because Auth0 doesn't bring any introspection path for checking opaque token, and they promote to use userInfo instead, so i go for it.
Anyway, i checked again with this parameter quarkus.oidc.roles.source=accesstoken
and i didn't see any break when the redirection is executed on Quarkus side. My idea is Auth0 force to redirect on the login page when something goes wrong.
I will close the issue, but i can't still use your framework with Auth0 at this point when i'm using scopes, while it works on Spring Boot, regading this example : https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization?download=true
Thank you to investigate with me :)
@majonga88
I'm confused when you said "does not make sense in your case" for userinfo
You are asking Quakus OIDC to use the content returned from the user info endpoint as the source of roles, and as you have seen yourself, this content has no information about the roles. Likewise, earlier on, where you were asking Quarkus OIDC to use the access token as a source of roles, again you confirmed the token was opaque/binary and Auth0 had no introspection endpoint to return that content.
So I don't know how it can work with SpringBoot if Auth0 does not include the roles in the userinfo response and it can not return the content of the binary access token but good luck with it :-)
@sberyozkin I think it would be worth investigating what Spring is doing and we are not.
@gsmet, and @majonga88, Hi, I'm sorry but given what @majonga88 has confirmed I don't believe @majonga88's setup can work with SpringBoot. What I can imagine is that @majonga88's setup in the SpringBoot project is entirely different to the Quarkus project setup.
The reason I think it can be the case is that @majonga88 has posted the working example with SpringBoot showing that the access token is in the JWT format - and also confirmed at the same time that the same access token is in fact in a binary format in a Quarkus project, therefore the access token can not be used as the source of the roles as Auth0 does not support the binary token introspection.
The other point which has been discussed is the acquisition of the roles from the user info endpoint. And again, @majonga88 has confirmed that the user info response has no roles in it at all, while also confirmed, that Quarkus OIDC can successfully get the userinfo content on its own.
So, as I said, given that in the Quarkus project 1) the access token is binary and Auth0 can not return the introspection response for the binary tokens and 2) the userinfo response has no roles in it, I don't think SpringBoot or any other OIDC provider implementation can get it working.
It is possible that there has been some confusion. But I've given my best here to help @majonga88 who is welcome to create concrete issues.
@sberyozkin @gsmet I think the best way to handle fully Auth0 integration in Quarkus, is to provide an example like you doing for Keycloak. I think many people will asks same questions around Quarkus integration with tiers providers like Okta or Auth0.
@sberyozkin Maybe when i testing it, it appends, if you try on your own, you can see something that i missed (i hope). I send you in zulip all credentials to test if you wanted to try and the project.
I strongly have to say again, i don't know why scopes working on Spring Boot and not in Quarkus with the same Auth0 configuration. I kindly remark, i'm not an expert on this, i'm just a friendly user on OIDC on both Quarkus and SpringBoot and i just trying to understand what append.
@majonga88 We have both Auth0 and Okta users confirming Quarkus OIDC works for them. The idea of having more provider specific integration examples is good and I'd encourage you to work on the Auth0 one. Start from something simple and we can move from there (copy and paste the existing web-app quickstart).
I strongly have to say again, i don't know why scopes working on Spring Boot and not in Quarkus with the same Auth0 configuration
See the problem we have been having in this issue is that we seem to can't understand each other :-). I'm sorry but I don't have time to try to reproduce something that, as per my last feedback, can't even work. Many other concrete issues and enhancements have to be addressed.
Given my previous comment,
The reason I think it can be the case is that @majonga88 has posted the working example with SpringBoot showing that the access token is in the JWT format - and also confirmed at the same time that the same access token is in fact in a binary format in a Quarkus project, therefore the access token can not be used as the source of the roles as Auth0 does not support the binary token introspection.
What can you say about this one ?
The other point which has been discussed is the acquisition of the roles from the user info endpoint. And again, @majonga88 has confirmed that the user info response has no roles in it at all, while also confirmed, that Quarkus OIDC can successfully get the userinfo content on its own.
And this one ?
So, as I said, given that in the Quarkus project 1) the access token is binary and Auth0 can not return the introspection response for the binary tokens and 2) the userinfo response has no roles in it, I don't think SpringBoot or any other OIDC provider implementation can get it working.
And this one ?
In this issue and in the previous issue you have opened we have investigated and confirmed:
So what exactly is not clear as to why Quarkus can't get any roles ?
@sberyozkin Nevermind, i found my solution.
@majonga88 would you mind sharing the solution?
@cihati I switch to Spring Boot, I was inspired by this example : https://auth0.com/blog/securing-spring-boot-apis-and-spas-with-oauth2/
And the gihub link : https://github.com/auth0-blog/spring-boot-oauth2
And my example based on this project : https://github.com/majonga88/sb-security-oauth2
@cihati I switch to Spring Boot, I was inspired by this example : https://auth0.com/blog/securing-spring-boot-apis-and-spas-with-oauth2/
And the gihub link : https://github.com/auth0-blog/spring-boot-oauth2
And my example based on this project : https://github.com/majonga88/sb-security-oauth2
Sadly, quarkus still doesn't work with Auth0 access tokens as a source of roles and permissions, yet it works with Spring projects. So to date, we can authenticate using auth0 but can't do much else without adding information to the idtoken via rules which isn't recommended.
Describe the bug
Regarding to test the fix of this bug : https://github.com/quarkusio/quarkus/issues/8396, when i trying to connect to my application via Auth0 and i'm experiencing a new issue.
I inspect the code on vertx-jwt-3.9.3 dependency, JWT.class inside io.vertx.ext.jwt package, line 337 to 343 and i've seen my timestamp iat more recent than the timestamp now.
Here is the difference between the two dates, more interpretable read here :
Thank you for your help.
Expected behavior
The present token should not expired in this case.
Actual behavior
The token is expired.
To Reproduce
Here is the target project : https://github.com/majonga88/security-quarkus
Environment (please complete the following information):
java -version
: 11mvnw --version
orgradlew --version
): Maven