Skip to content

Commit 3b248a5

Browse files
committed
Re-authenticate users if the OIDC session cookie can not be read
1 parent 52b28e2 commit 3b248a5

File tree

3 files changed

+40
-11
lines changed

3 files changed

+40
-11
lines changed

extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,15 @@ private Uni<SecurityIdentity> reAuthenticate(String sessionCookie,
338338
context.put(TenantConfigContext.class.getName(), configContext);
339339
return resolver.getTokenStateManager().getTokens(context, configContext.oidcConfig(),
340340
sessionCookie, getTokenStateRequestContext)
341-
.onFailure(AuthenticationCompletionException.class)
341+
.onFailure(Throwable.class)
342342
.recoverWithUni(
343343
new Function<Throwable, Uni<? extends AuthorizationCodeTokens>>() {
344344
@Override
345345
public Uni<AuthorizationCodeTokens> apply(Throwable t) {
346+
Throwable failure = t instanceof AuthenticationFailedException ? t
347+
: new AuthenticationFailedException(t);
346348
return removeSessionCookie(context, configContext.oidcConfig())
347-
.replaceWith(Uni.createFrom().failure(t));
349+
.replaceWith(Uni.createFrom().failure(failure));
348350
}
349351
})
350352
.chain(new Function<AuthorizationCodeTokens, Uni<? extends SecurityIdentity>>() {

extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
import jakarta.enterprise.context.ApplicationScoped;
44

5+
import org.jboss.logging.Logger;
6+
57
import io.quarkus.oidc.AuthorizationCodeTokens;
68
import io.quarkus.oidc.OidcRequestContext;
79
import io.quarkus.oidc.OidcTenantConfig;
810
import io.quarkus.oidc.TokenStateManager;
911
import io.quarkus.oidc.runtime.OidcTenantConfig.TokenStateManager.Strategy;
10-
import io.quarkus.security.AuthenticationCompletionException;
1112
import io.quarkus.security.AuthenticationFailedException;
1213
import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm;
1314
import io.smallrye.mutiny.Uni;
@@ -17,6 +18,7 @@
1718

1819
@ApplicationScoped
1920
public class DefaultTokenStateManager implements TokenStateManager {
21+
private static final Logger LOG = Logger.getLogger(DefaultTokenStateManager.class);
2022

2123
@Override
2224
public Uni<String> createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig,
@@ -125,13 +127,17 @@ public Uni<AuthorizationCodeTokens> getTokens(RoutingContext routingContext, Oid
125127

126128
if (oidcConfig.tokenStateManager().strategy() == Strategy.KEEP_ALL_TOKENS) {
127129
accessToken = tokens[1];
128-
accessTokenExpiresIn = tokens[2].isEmpty() ? null : Long.valueOf(tokens[2]);
130+
accessTokenExpiresIn = tokens[2].isEmpty() ? null : parseAccessTokenExpiresIn(tokens[2]);
129131
refreshToken = tokens[3];
130132
} else if (oidcConfig.tokenStateManager().strategy() == Strategy.ID_REFRESH_TOKENS) {
131133
refreshToken = tokens[3];
132134
}
133135
} catch (ArrayIndexOutOfBoundsException ex) {
134-
return Uni.createFrom().failure(new AuthenticationCompletionException("Session cookie is malformed"));
136+
final String error = "Session cookie is malformed";
137+
LOG.debug(ex);
138+
return Uni.createFrom().failure(new AuthenticationFailedException(error));
139+
} catch (AuthenticationFailedException ex) {
140+
return Uni.createFrom().failure(ex);
135141
}
136142
} else {
137143
// Decrypt ID token from the q_session cookie
@@ -147,9 +153,15 @@ public Uni<AuthorizationCodeTokens> getTokens(RoutingContext routingContext, Oid
147153
String[] accessTokenData = CodeAuthenticationMechanism.COOKIE_PATTERN.split(accessTokenState);
148154
accessToken = accessTokenData[0];
149155
try {
150-
accessTokenExpiresIn = accessTokenData[1].isEmpty() ? null : Long.valueOf(accessTokenData[1]);
156+
accessTokenExpiresIn = accessTokenData[1].isEmpty() ? null
157+
: parseAccessTokenExpiresIn(accessTokenData[1]);
151158
} catch (ArrayIndexOutOfBoundsException ex) {
152-
return Uni.createFrom().failure(new AuthenticationCompletionException("Session cookie is malformed"));
159+
final String error = "Session cookie is malformed";
160+
LOG.debug(ex);
161+
// Make this error message visible in the dev mode
162+
return Uni.createFrom().failure(new AuthenticationFailedException(error));
163+
} catch (AuthenticationFailedException ex) {
164+
return Uni.createFrom().failure(ex);
153165
}
154166
}
155167
Cookie rtCookie = getRefreshTokenCookie(routingContext, oidcConfig);
@@ -179,6 +191,19 @@ public Uni<Void> deleteTokens(RoutingContext routingContext, OidcTenantConfig oi
179191
return CodeAuthenticationMechanism.VOID_UNI;
180192
}
181193

194+
private static Long parseAccessTokenExpiresIn(String accessTokenExpiresInString) {
195+
try {
196+
return Long.valueOf(accessTokenExpiresInString);
197+
} catch (NumberFormatException ex) {
198+
final String error = """
199+
Access token expires_in property in the session cookie must be a number, found %s
200+
""".formatted(accessTokenExpiresInString);
201+
LOG.debug(ex);
202+
// Make this error message visible in the dev mode
203+
throw new AuthenticationFailedException(error);
204+
}
205+
}
206+
182207
private static ServerCookie getAccessTokenCookie(RoutingContext routingContext, OidcTenantConfig oidcConfig) {
183208
return (ServerCookie) routingContext.request().getCookie(getAccessTokenCookieName(oidcConfig));
184209
}

integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -892,11 +892,13 @@ public void testIdTokenInjection() throws IOException {
892892
sessionCookie = getSessionCookie(webClient, null);
893893
assertEquals("1|2|3", sessionCookie.getValue());
894894

895+
webClient.getOptions().setRedirectEnabled(false);
896+
895897
try {
896898
webClient.getPage("http://localhost:8081/web-app");
897-
fail("401 status error is expected");
899+
fail("302 status error is expected");
898900
} catch (FailingHttpStatusCodeException ex) {
899-
assertEquals(401, ex.getStatusCode());
901+
assertEquals(302, ex.getStatusCode());
900902
assertNull(getSessionCookie(webClient, null));
901903
}
902904
webClient.getCookieManager().clearCookies();
@@ -910,9 +912,9 @@ public void testIdTokenInjection() throws IOException {
910912

911913
try {
912914
webClient.getPage("http://localhost:8081/web-app");
913-
fail("401 status error is expected");
915+
fail("302 status error is expected");
914916
} catch (FailingHttpStatusCodeException ex) {
915-
assertEquals(401, ex.getStatusCode());
917+
assertEquals(302, ex.getStatusCode());
916918
assertNull(getSessionCookie(webClient, null));
917919
}
918920
webClient.getCookieManager().clearCookies();

0 commit comments

Comments
 (0)