Closed wjglerum closed 4 years ago
/cc @sberyozkin this one looks like a regression. Could you prioritize it? Thanks!
I see that something changed with the principal and user information, see https://github.com/quarkusio/quarkus/commit/856226615b3aefe8a6445987479758c3af81de2b#diff-c34b979eed4ac8884d4a875479eca3cf
I tried setting https://quarkus.io/guides/security-openid-connect-web-authentication#quarkus-oidc_quarkus.oidc.authentication.user-info-required and https://quarkus.io/guides/security-openid-connect-web-authentication#quarkus-oidc_quarkus.oidc.roles.source however this also didn't solve my problem.
I also did some debugging to see what requests Quarkus is making to Azure AD. It seems that it successfully exchanged a code for a set of ID and access tokens. It also fetches the OpenID config and certs correctly on startup.
I did this with Proxyman running on my Mac intercepting and decrypting SSL traffic. With the following Quarkus config:
quarkus.oidc.proxy.host=localhost
quarkus.oidc.proxy.port=9090
@wjglerum The linked changes are not related at all to the problem so please do not enhance the existing configuration for now. I'm nearly 100% sure it is not a Quarkus OIDC issue. The reason I think it is not related is to do with your comment :
The weird bit here is the only on the last request it warns us about the fact that the state cookie does not match.
Something goes wrong in the transit.
Can you please investigate more, perhaps simplify the setup first, etc.
Thanks for your reply @sberyozkin. I was going through the changes in the OIDC extension and this came across, so I gave it a try. It's not part of the original bug report and sample project.
I tried the simplest setup as possible with my sample project, with little to no config. I also tried debugging and checking what is going on. I ruled out the calls to Azure AD from Quarkus as these are similar in both versions (checked with Proxyman).
When double checking the request flows, I found the weird bit. When Quarkus successfully requested the tokens from Azure AD with the code and redirects the user back to the application it sets a weird cookie:
Request URL: http://localhost:8080/?code=<<<code>>>&state=9a9c65c4-5005-4480-8648-bd727c27cc09&session_state=c6f3b182-acc3-43f5-a997-193762b28332
Response headers:
set-cookie: q_auth=cf068392-6be2-4159-a095-971df27b9a20|?code=<<<code>>>&state=9a9c65c4-5005-4480-8648-bd727c27cc09&session_state=c6f3b182-acc3-43f5-a997-193762b28332; Max-Age=1800; Expires=Mon, 17 Aug 2020 12:57:48 GMT; HTTPOnly
This is not what I would expect.
In 1.6.0.FINAL it sets the following cookies instead:
set-cookie: q_session=<<<token>>>|<<<token>>>|null; Max-Age=3900; Expires=Mon, 17 Aug 2020 13:59:02 GMT; HTTPOnly
set-cookie: q_auth=; Max-Age=0; Expires=Mon, 17 Aug 2020 12:54:02 GMT
There is nothing in the logs, even with debugging logging. Any pointers were we could look into? For now I'm pretty stuck on this.
Hi @wjglerum
in 1.7.0.Final
, when one issues an initial request, example, http://localhost:8080/service?a=b
, this ?a=b
will be restored once the authentication has been completed, and indeed, once all is done, you will get exactly what you've posted above.
The fact you see this strange q_auth
value suggests that the Request URL like http://localhost:8080/?code=<<<code>>>&state=9a9c65c4-5005-4480-8648-bd727c27cc09&session_state=c6f3b182-acc3-43f5-a997-193762b28332
is seen by Quarkus OIDC not as part of the code flow, i.e the original state cookie which was set when you tried to access Quarkus was not detected after the user was redirected back to Quarkus so it treats this request as a completely new independent request.
Can you set a breakpoint here please: https://github.com/quarkusio/quarkus/blob/master/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java#L205
This is where the return leg of the code flow is processed.
And here:
https://github.com/quarkusio/quarkus/blob/master/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java#L289
This is the code which runs after the code flow completes and where you should get q_auth
reset. as per your last fragment below.
@wjglerum Hi, did you have a chance to look into it ? Let me summarize again what I think is happening in your case, referring to the issue description here:
Can you try quarkus.oidc.authentication.cookie-path=/
?
@wjglerum I've started creating an Azure tenant and I have to admit I don't know what exactly I'm doing there, different types are possible, etc. Can you please create some test tenantid which I can use ?
Hi @sberyozkin, finally got some time to properly debug this. Had a quick look yesterday evening but couldn't find anything useful. Thanks to your suggestions for the breakpoints I now did manage to find an exception. Which does not happen when we use Keycloak as the OIDC provider.
The troubled bit seems to be here: https://github.com/quarkusio/quarkus/blob/master/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java#L325
It fails with the following message:
io.vertx.core.impl.NoStackTraceThrowable: Invalid path
With the following stacktrace (from the IntelliJ IDEA debug console):
0 = {StackTraceElement@7483} "io.quarkus.oidc.runtime.OidcIdentityProvider$3$1.handle(OidcIdentityProvider.java:196)"
1 = {StackTraceElement@7484} "io.quarkus.oidc.runtime.OidcIdentityProvider$3$1.handle(OidcIdentityProvider.java:192)"
2 = {StackTraceElement@7485} "io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl.lambda$decodeToken$6(OAuth2AuthProviderImpl.java:342)"
3 = {StackTraceElement@7486} "io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl.lambda$authenticate$4(OAuth2AuthProviderImpl.java:290)"
4 = {StackTraceElement@7487} "io.vertx.ext.auth.oauth2.impl.OAuth2TokenImpl.lambda$introspect$3(OAuth2TokenImpl.java:354)"
5 = {StackTraceElement@7488} "io.vertx.ext.auth.oauth2.impl.OAuth2API.fetch(OAuth2API.java:49)"
6 = {StackTraceElement@7489} "io.vertx.ext.auth.oauth2.impl.OAuth2TokenImpl.introspect(OAuth2TokenImpl.java:345)"
7 = {StackTraceElement@7490} "io.vertx.ext.auth.oauth2.impl.OAuth2TokenImpl.introspect(OAuth2TokenImpl.java:503)"
8 = {StackTraceElement@7491} "io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl.authenticate(OAuth2AuthProviderImpl.java:288)"
9 = {StackTraceElement@7492} "io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl.decodeToken(OAuth2AuthProviderImpl.java:338)"
10 = {StackTraceElement@7493} "io.quarkus.oidc.runtime.OidcIdentityProvider$3.accept(OidcIdentityProvider.java:191)"
11 = {StackTraceElement@7494} "io.quarkus.oidc.runtime.OidcIdentityProvider$3.accept(OidcIdentityProvider.java:188)"
12 = {StackTraceElement@7495} "io.smallrye.mutiny.operators.UniCreateWithEmitter.subscribing(UniCreateWithEmitter.java:22)"
13 = {StackTraceElement@7496} "io.smallrye.mutiny.operators.UniSerializedSubscriber.subscribe(UniSerializedSubscriber.java:43)"
14 = {StackTraceElement@7497} "io.smallrye.mutiny.operators.UniSerializedSubscriber.subscribe(UniSerializedSubscriber.java:38)"
15 = {StackTraceElement@7498} "io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:30)"
16 = {StackTraceElement@7499} "io.smallrye.mutiny.operators.UniBlockingAwait.await(UniBlockingAwait.java:48)"
17 = {StackTraceElement@7500} "io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:61)"
18 = {StackTraceElement@7501} "io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:42)"
19 = {StackTraceElement@7502} "io.quarkus.oidc.runtime.OidcIdentityProvider.verifyCodeFlowAccessToken(OidcIdentityProvider.java:202)"
20 = {StackTraceElement@7503} "io.quarkus.oidc.runtime.OidcIdentityProvider.validateTokenWithOidcServer(OidcIdentityProvider.java:84)"
21 = {StackTraceElement@7504} "io.quarkus.oidc.runtime.OidcIdentityProvider.authenticate(OidcIdentityProvider.java:72)"
22 = {StackTraceElement@7505} "io.quarkus.oidc.runtime.OidcIdentityProvider.access$000(OidcIdentityProvider.java:33)"
23 = {StackTraceElement@7506} "io.quarkus.oidc.runtime.OidcIdentityProvider$1.get(OidcIdentityProvider.java:59)"
24 = {StackTraceElement@7507} "io.quarkus.oidc.runtime.OidcIdentityProvider$1.get(OidcIdentityProvider.java:47)"
25 = {StackTraceElement@7508} "io.smallrye.mutiny.operators.UniCreateFromDeferredSupplier.subscribing(UniCreateFromDeferredSupplier.java:24)"
26 = {StackTraceElement@7509} "io.smallrye.mutiny.operators.UniSerializedSubscriber.subscribe(UniSerializedSubscriber.java:43)"
27 = {StackTraceElement@7510} "io.smallrye.mutiny.operators.UniSerializedSubscriber.subscribe(UniSerializedSubscriber.java:38)"
28 = {StackTraceElement@7511} "io.smallrye.mutiny.groups.UniSubscribe.withSubscriber(UniSubscribe.java:49)"
29 = {StackTraceElement@7512} "io.smallrye.mutiny.groups.UniSubscribe.with(UniSubscribe.java:69)"
30 = {StackTraceElement@7513} "io.quarkus.oidc.runtime.CodeAuthenticationMechanism$4.lambda$accept$0(CodeAuthenticationMechanism.java:299)"
31 = {StackTraceElement@7514} "io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl.lambda$authenticate$5(OAuth2AuthProviderImpl.java:318)"
32 = {StackTraceElement@7515} "io.vertx.ext.auth.oauth2.impl.flow.AuthCodeImpl.lambda$getToken$0(AuthCodeImpl.java:93)"
33 = {StackTraceElement@7516} "io.vertx.ext.auth.oauth2.impl.flow.AbstractOAuth2Flow.lambda$getToken$0(AbstractOAuth2Flow.java:148)"
34 = {StackTraceElement@7517} "io.vertx.ext.auth.oauth2.impl.OAuth2API.lambda$null$1(OAuth2API.java:129)"
35 = {StackTraceElement@7518} "io.vertx.core.http.impl.HttpClientResponseImpl$BodyHandler.notifyHandler(HttpClientResponseImpl.java:292)"
36 = {StackTraceElement@7519} "io.vertx.core.http.impl.HttpClientResponseImpl.lambda$bodyHandler$0(HttpClientResponseImpl.java:193)"
37 = {StackTraceElement@7520} "io.vertx.core.http.impl.HttpClientResponseImpl.handleEnd(HttpClientResponseImpl.java:248)"
38 = {StackTraceElement@7521} "io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.lambda$beginResponse$0(Http1xClientConnection.java:483)"
39 = {StackTraceElement@7522} "io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:237)"
40 = {StackTraceElement@7523} "io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:127)"
41 = {StackTraceElement@7524} "io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.endResponse(Http1xClientConnection.java:502)"
42 = {StackTraceElement@7525} "io.vertx.core.http.impl.Http1xClientConnection$StreamImpl.access$000(Http1xClientConnection.java:241)"
43 = {StackTraceElement@7526} "io.vertx.core.http.impl.Http1xClientConnection.handleResponseEnd(Http1xClientConnection.java:641)"
44 = {StackTraceElement@7527} "io.vertx.core.http.impl.Http1xClientConnection.handleHttpMessage(Http1xClientConnection.java:601)"
45 = {StackTraceElement@7528} "io.vertx.core.http.impl.Http1xClientConnection.handleMessage(Http1xClientConnection.java:575)"
46 = {StackTraceElement@7529} "io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:366)"
47 = {StackTraceElement@7530} "io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:43)"
48 = {StackTraceElement@7531} "io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:229)"
49 = {StackTraceElement@7532} "io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:173)"
50 = {StackTraceElement@7533} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
51 = {StackTraceElement@7534} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
52 = {StackTraceElement@7535} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
53 = {StackTraceElement@7536} "io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)"
54 = {StackTraceElement@7537} "io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)"
55 = {StackTraceElement@7538} "io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)"
56 = {StackTraceElement@7539} "io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)"
57 = {StackTraceElement@7540} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
58 = {StackTraceElement@7541} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
59 = {StackTraceElement@7542} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
60 = {StackTraceElement@7543} "io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1518)"
61 = {StackTraceElement@7544} "io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1267)"
62 = {StackTraceElement@7545} "io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1314)"
63 = {StackTraceElement@7546} "io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501)"
64 = {StackTraceElement@7547} "io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440)"
65 = {StackTraceElement@7548} "io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)"
66 = {StackTraceElement@7549} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
67 = {StackTraceElement@7550} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
68 = {StackTraceElement@7551} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
69 = {StackTraceElement@7552} "io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)"
70 = {StackTraceElement@7553} "io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)"
71 = {StackTraceElement@7554} "io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:272)"
72 = {StackTraceElement@7555} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
73 = {StackTraceElement@7556} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
74 = {StackTraceElement@7557} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
75 = {StackTraceElement@7558} "io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)"
76 = {StackTraceElement@7559} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
77 = {StackTraceElement@7560} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
78 = {StackTraceElement@7561} "io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)"
79 = {StackTraceElement@7562} "io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)"
80 = {StackTraceElement@7563} "io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)"
81 = {StackTraceElement@7564} "io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)"
82 = {StackTraceElement@7565} "io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)"
83 = {StackTraceElement@7566} "io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)"
84 = {StackTraceElement@7567} "io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)"
85 = {StackTraceElement@7568} "io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)"
86 = {StackTraceElement@7569} "io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)"
87 = {StackTraceElement@7570} "java.base/java.lang.Thread.run(Thread.java:834)"
It looks like it cannot read or parse a certain part of the response from Azure AD correctly. Which is weird as it does work in Quarkus 1.6.x.
I can share the client id + secret with you for the test App registration I created on Azure AD. How can I best sent you this (sensitive) information?
After some more debugging related to the "Invalid path" exception from above I found some more information. This error seems to come from the fetch method from the OAuth2API from Vert.x Auth, see https://github.com/vert-x3/vertx-auth/blob/3.9.2/vertx-auth-oauth2/src/main/java/io/vertx/ext/auth/oauth2/impl/OAuth2API.java#L47
Using Keycloak:
Using Azure AD
path= "https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token"
path=null
instead which fails the null check ofcourseAny idea why is this second fetch is happening?
Alright I think I found it! This works for our project and my sample project in combination with Azure AD:
quarkus.oidc.authentication.verify-access-token=false
-> does the trick
quarkus.oidc.roles.source=idtoken
-> to read the roles from the idtoken instead
The second fetch was for the access token I think. Somehow Quarkus/SmallRye JWT/Vert.x Auth doesn't pick up the right path or Azure AD doesn't expose the right configuration on their discovery endpoint for verifying access tokens.
Also see
So I think this change was indeed introduced in the commit I mentioned, https://github.com/quarkusio/quarkus/commit/856226615b3aefe8a6445987479758c3af81de2b#diff-c34b979eed4ac8884d4a875479eca3cf Not sure if we need a change on the Quarkus side though.
See https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#validating-tokens for more information on validating tokens. It should be possible to validate the access token locally right?
Hi @wjglerum Thanks for spending the time on debugging this issue. Hmm... Something strange is going on. OK,
quarkus.oidc.authentication.verify-access-token=...
I did introduce this check (true by default) because until 1.7.0
the access token (which is in the session cookie) is not validated at all which is risky, even though for the code flow we use and verify id_token
. But the users can still inject the access token, access its properties or propagate it, so it has to be verified in principle.
However it does look like it causes side-effects. Specifically, if the discovery document does not return an introspection endpoint address and the the access token is opaque (binary) or JWT but is signed by a key which is not available in the fetched JWK set - which is why you are seeing an extra fetch and the ultimate failure due to the missing introspection path.
So what I will do is I will set this property to false
by default.
quarkus.oidc.roles.source=idtoken
- you don't need to set it, it is a default in the code flow case
Thanks again, and sorry for this regression - even though IMHO it is a good improvement from the security point of view. I'll take care of it now.
@wjglerum the same issue is here, https://github.com/quarkusio/quarkus/issues/11460#issuecomment-676106720
The reason it is all working with KC is because its access token is also JWT and besides, it also reports the introspection path in the discovery doc, hence the Quarkus tests are passing
Thanks for your explanation and follow up @sberyozkin!
It would be nice if we could somehow validate the access token coming from OIDC providers like Azure AD. This should be possible right?
Because the access token returned by Azure AD is a JWT which has a kid
claim in the header which links to the keys from the endpoint found in the OIDC configuration, see:
@wjglerum Are you sure you are referring to the access token as opposed to ID token ?
One does not have to verify the access token with Quarkus in the code flow since we work with ID token as the primary token.
The fact you have the flow failing with this AT validation being on by default does indicate the AT is either opaque or has no JWK in the local JWK set which Quarkus fetched and the discovery doc also does not return an introspection endpoint address which is used as a fallback if no JWK is available locally.
Can you share the content of q_session
? You can trim a bit the content of each token but please keep the first 10 or so chars, for me to see if both are JWT or not
@wjglerum see also this comment, https://github.com/quarkusio/quarkus/issues/11460#issuecomment-676511319, if you can try to debug even more than it will clarify things. As I said, the 1st thing which has to be done is to fix this side-effect to avoid the users opening new issues. Next we can investigate, as part of a new issue, how to optionally verify not only ID but also ATs returned from Azure as part of the code flow
@wjglerum similarly to Google, Azure does not have an introspection endpoint in this discovery doc.
The access token returned with the code flow is not meant to access Quarkus itself, Quarkus App can use it to access Azure API or refresh ID tokens, same as with Google.
I'm absolutely certain the AT is not verifiable locally.
I've added this option to verify the access token with the code flow (in addition to ID token) because sometimes, especially with keycloak, users can inject AT as JsonWebToken
.
@sberyozkin thanks for changing the default for verifying the access tokens!
Here are the decoded headers from both tokens:
{
"typ": "JWT",
"alg": "RS256",
"kid": "<kid>"
}
{
"typ": "JWT",
"nonce": "<nonce>",
"alg": "RS256",
"x5t": "<kid>",
"kid": "<kid>"
}
Seeing the headers I do think that Quarkus OIDC should be able to verify the access token. As the access token uses the same kid
as the id token. I also see that Quarkus OIDC does get the keys successfully from the OIDC provider on start up and it contains the mentioned kid
.
I'll try to do some more debugging tomorrow on why it is not using the keys to verify the access token.
Alright, I had some more time to look at the validation of the access token. It seems it's not possible with Azure AD. Or at least not with the specified kid
. I tried to manually verify the tokens with the certificate obtained from the Azure AD endpoint. This works on https://jwt.io/ for the id token. It does not work with the access token it will return "Invalid signature". I turned on some more debugging and this is also what happens in the code, it fails to verify the signature of the JWT of the access token. Next it tries to use the introspection path, but as you mentioned this is not present in the OIDC config for Azure AD.
Double checked the docs for access tokens from Azure AD and they also mention that we should treat them as opaque tokens:
Clients should treat access tokens as opaque strings, as the contents of the token are intended for the resource only. https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens
I guess it makes sense to keep the default to not enforce validation of access tokens.
Describe the bug We have a webapp where we delegate the login to Azure AD with the Quarkus OIDC extension. When we upgrade to Quarkus 1.7.0.FINAL the login through our OIDC provider is broken. Locally everything works with Keycloak as the OIDC provider. However when using Azure AD as the OIDC provider we get in a loop during login.
Expected behavior Logging in with Azure AD as the OIDC provider works.
Actual behavior After logging in with Azure AD we end up with an infinite loop. Flow of requests:
Request URL:
http://localhost:8080/
Response headers:Request URL:
https://login.microsoftonline.com/<<<tenant_id>>>/oauth2/v2.0/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&state=9a9c65c4-5005-4480-8648-bd727c27cc09&scope=openid&response_type=code&client_id=<<<client_id>>>
Login flow with Azure ADRequest URL:
https://login.microsoftonline.com/kmsi
Response headers:Request URL:
http://localhost:8080/?code=<<<code>>>&state=9a9c65c4-5005-4480-8648-bd727c27cc09&session_state=c6f3b182-acc3-43f5-a997-193762b28332
Response headers:The result is an infinite loop (which your browser kills eventually)
From the logs with DEBUG level set for the OIDC plugin:
The weird bit here is the only on the last request it warns us about the fact that the state cookie does not match.
Quarkus 1.6 Compare this with the output when using Quarkus 1.6.0.FINAL
Request URL:
http://localhost:8080/
Response headers:Request URL:
https://login.microsoftonline.com/<<<tenant_id>>>/oauth2/v2.0/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&state=e27702af-ba2a-49d5-9c4d-d6ce5e621bc5&scope=openid&response_type=code&client_id=<<<client_id>>>
Login flow with Azure ADRequest URL:
https://login.microsoftonline.com/kmsi
Response headers:Request URL:
http://localhost:8080/?code=<<<code>>>&state=e27702af-ba2a-49d5-9c4d-d6ce5e621bc5&session_state=8192f17c-fc9c-4293-95f7-d0789239dc2e
Response headers:Request URL:
http://localhost:8080/
And user is logged in successfully and cookies are set properly.With the following logs:
Everything looks good here.
To Reproduce Steps to reproduce the behavior:
quarkus-oidc
enabled, see https://github.com/wjglerum/quarkus-oidc-azuremvn quarkus:dev
and try to loginConfiguration
Screenshots (If applicable, add screenshots to help explain your problem.)
Environment (please complete the following information):
uname -a
orver
:java -version
:N/A
1.7.0.FINAL
mvnw --version
orgradlew --version
):Additional context Let me know if I can provide more information. Also, did anything major change in the OIDC extension this release?