Skip to content

Commit 77180c6

Browse files
authored
Merge pull request #47040 from sberyozkin/pass_failed_token_with_authentication_exception
Include the token with AuthenticaionFailedException
2 parents 5bd9ae8 + f40f37d commit 77180c6

File tree

6 files changed

+146
-77
lines changed

6 files changed

+146
-77
lines changed

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

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.security.cert.Certificate;
66
import java.security.cert.X509Certificate;
77
import java.util.List;
8+
import java.util.Map;
89
import java.util.function.Function;
910

1011
import javax.net.ssl.SSLPeerUnverifiedException;
@@ -35,8 +36,8 @@ public Uni<SecurityIdentity> authenticate(RoutingContext context,
3536
// if a bearer token is provided try to authenticate
3637
if (token != null) {
3738
try {
38-
setCertificateThumbprint(context, oidcTenantConfig);
39-
setDPopProof(context, oidcTenantConfig);
39+
setCertificateThumbprint(context, oidcTenantConfig, token);
40+
setDPopProof(context, oidcTenantConfig, token);
4041
} catch (AuthenticationFailedException ex) {
4142
return Uni.createFrom().failure(ex);
4243
}
@@ -46,29 +47,29 @@ public Uni<SecurityIdentity> authenticate(RoutingContext context,
4647
return Uni.createFrom().nullItem();
4748
}
4849

49-
private static void setCertificateThumbprint(RoutingContext context, OidcTenantConfig oidcTenantConfig) {
50+
private static void setCertificateThumbprint(RoutingContext context, OidcTenantConfig oidcTenantConfig, String token) {
5051
if (oidcTenantConfig.token().binding().certificate()) {
51-
Certificate cert = getCertificate(context);
52+
Certificate cert = getCertificate(context, token);
5253
if (!(cert instanceof X509Certificate)) {
5354
LOG.warn("Access token must be bound to X509 client certiifcate");
54-
throw new AuthenticationFailedException();
55+
throw new AuthenticationFailedException(tokenMap(token));
5556
}
5657
context.put(OidcConstants.X509_SHA256_THUMBPRINT,
5758
TrustStoreUtils.calculateThumprint((X509Certificate) cert));
5859
}
5960
}
6061

61-
private static void setDPopProof(RoutingContext context, OidcTenantConfig oidcTenantConfig) {
62+
private static void setDPopProof(RoutingContext context, OidcTenantConfig oidcTenantConfig, String token) {
6263
if (OidcConstants.DPOP_SCHEME.equals(oidcTenantConfig.token().authorizationScheme())) {
6364

6465
List<String> proofs = context.request().headers().getAll(OidcConstants.DPOP_SCHEME);
6566
if (proofs == null || proofs.isEmpty()) {
6667
LOG.warn("DPOP proof header must be present to verify the DPOP access token binding");
67-
throw new AuthenticationFailedException();
68+
throw new AuthenticationFailedException(tokenMap(token));
6869
}
6970
if (proofs.size() != 1) {
7071
LOG.warn("Only a single DPOP proof header is accepted");
71-
throw new AuthenticationFailedException();
72+
throw new AuthenticationFailedException(tokenMap(token));
7273
}
7374
String proof = proofs.get(0);
7475

@@ -78,28 +79,28 @@ private static void setDPopProof(RoutingContext context, OidcTenantConfig oidcTe
7879

7980
if (!OidcConstants.DPOP_TOKEN_TYPE.equals(proofJwtHeaders.getString(OidcConstants.TOKEN_TYPE_HEADER))) {
8081
LOG.warn("Invalid DPOP proof token type ('typ') header");
81-
throw new AuthenticationFailedException();
82+
throw new AuthenticationFailedException(tokenMap(token));
8283
}
8384

8485
// Check HTTP method and request URI
8586
String proofHttpMethod = proofJwtClaims.getString(OidcConstants.DPOP_HTTP_METHOD);
8687
if (proofHttpMethod == null) {
8788
LOG.warn("DPOP proof HTTP method claim is missing");
88-
throw new AuthenticationFailedException();
89+
throw new AuthenticationFailedException(tokenMap(token));
8990
}
9091

9192
String httpMethod = context.request().method().name();
9293
if (!httpMethod.equals(proofHttpMethod)) {
9394
LOG.warnf("DPOP proof HTTP method claim %s does not match the request HTTP method %s", proofHttpMethod,
9495
httpMethod);
95-
throw new AuthenticationFailedException();
96+
throw new AuthenticationFailedException(tokenMap(token));
9697
}
9798

9899
// Check HTTP request URI
99100
String proofHttpRequestUri = proofJwtClaims.getString(OidcConstants.DPOP_HTTP_REQUEST_URI);
100101
if (proofHttpRequestUri == null) {
101102
LOG.warn("DPOP proof HTTP request uri claim is missing");
102-
throw new AuthenticationFailedException();
103+
throw new AuthenticationFailedException(tokenMap(token));
103104
}
104105

105106
String httpRequestUri = context.request().absoluteURI();
@@ -110,7 +111,7 @@ private static void setDPopProof(RoutingContext context, OidcTenantConfig oidcTe
110111
if (!httpRequestUri.equals(proofHttpRequestUri)) {
111112
LOG.warnf("DPOP proof HTTP request uri claim %s does not match the request HTTP uri %s", proofHttpRequestUri,
112113
httpRequestUri);
113-
throw new AuthenticationFailedException();
114+
throw new AuthenticationFailedException(tokenMap(token));
114115
}
115116

116117
context.put(OidcUtils.DPOP_PROOF, proof);
@@ -119,15 +120,19 @@ private static void setDPopProof(RoutingContext context, OidcTenantConfig oidcTe
119120
}
120121
}
121122

122-
private static Certificate getCertificate(RoutingContext context) {
123+
private static Certificate getCertificate(RoutingContext context, String token) {
123124
try {
124125
return context.request().sslSession().getPeerCertificates()[0];
125126
} catch (SSLPeerUnverifiedException e) {
126127
LOG.warn("Access token must be certificate bound but no client certificate is available");
127-
throw new AuthenticationFailedException();
128+
throw new AuthenticationFailedException(tokenMap(token));
128129
}
129130
}
130131

132+
private static Map<String, Object> tokenMap(String token) {
133+
return Map.of(OidcConstants.ACCESS_TOKEN_VALUE, token);
134+
}
135+
131136
public Uni<ChallengeData> getChallenge(RoutingContext context) {
132137
Uni<TenantConfigContext> tenantContext = resolver.resolveContext(context);
133138
return tenantContext.onItem().transformToUni(new Function<TenantConfigContext, Uni<? extends ChallengeData>>() {

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,8 @@ public Uni<? extends SecurityIdentity> apply(Throwable t) {
369369
}
370370
if (t instanceof LogoutException) {
371371
LOG.debugf("User has been logged out, authentication challenge is required");
372-
return Uni.createFrom().failure(new AuthenticationFailedException(t));
372+
return Uni.createFrom()
373+
.failure(new AuthenticationFailedException(t, tokenMap(currentIdToken)));
373374
}
374375

375376
if (!(t instanceof TokenAutoRefreshException)) {
@@ -392,7 +393,7 @@ public Uni<? extends SecurityIdentity> apply(Throwable t) {
392393
LOG.debugf(
393394
"Session can not be verified due to an unresolved key exception, reauthentication is required");
394395
// Redirect the user to the OIDC provider to re-authenticate
395-
failure = new AuthenticationFailedException();
396+
failure = new AuthenticationFailedException(tokenMap(currentIdToken));
396397
} else {
397398
// Failures such as the signature verification failures require 401 status
398399
String error = logAuthenticationError(context, t);
@@ -1327,14 +1328,15 @@ public Uni<SecurityIdentity> apply(final AuthorizationCodeTokens tokens, final T
13271328
LOG.debug("Using the current SecurityIdentity since the ID token is still valid");
13281329
return Uni.createFrom().item(fallback);
13291330
} else {
1330-
return Uni.createFrom().failure(new AuthenticationFailedException(t));
1331+
return Uni.createFrom()
1332+
.failure(new AuthenticationFailedException(t, tokenMap(currentIdToken)));
13311333
}
13321334
} else if (configContext.oidcConfig().authentication().sessionExpiredPath().isPresent()) {
13331335
// Token has expired but the refresh does not work, check if the session expired page is available
13341336
return redirectToSessionExpiredPage(context, configContext);
13351337
}
13361338
// Redirect to the OIDC provider to reauthenticate
1337-
return Uni.createFrom().failure(new AuthenticationFailedException(t));
1339+
return Uni.createFrom().failure(new AuthenticationFailedException(t, tokenMap(currentIdToken)));
13381340
} else {
13391341
context.put(OidcConstants.ACCESS_TOKEN_VALUE, tokens.getAccessToken());
13401342
context.put(AuthorizationCodeTokens.class.getName(), tokens);
@@ -1368,7 +1370,7 @@ public SecurityIdentity apply(SecurityIdentity identity) {
13681370
@Override
13691371
public Throwable apply(Throwable tInner) {
13701372
LOG.debugf("Verifying the refreshed ID token failed %s", errorMessage(tInner));
1371-
return new AuthenticationFailedException(tInner);
1373+
return new AuthenticationFailedException(tInner, tokenMap(currentIdToken));
13721374
}
13731375
});
13741376
}
@@ -1392,7 +1394,7 @@ public AuthorizationCodeTokens apply(AuthorizationCodeTokens tokens) {
13921394
if (!autoRefresh) {
13931395
LOG.debugf(
13941396
"ID token is not returned in the refresh token grant response, re-authentication is required");
1395-
throw new AuthenticationFailedException();
1397+
throw new AuthenticationFailedException(tokenMap(currentIdToken));
13961398
} else {
13971399
// Auto-refresh is triggered while current ID token is still valid, continue using it.
13981400
tokens.setIdToken(currentIdToken);
@@ -1532,4 +1534,8 @@ public Void apply(Void t) {
15321534
}
15331535

15341536
}
1537+
1538+
private static Map<String, Object> tokenMap(String token) {
1539+
return Map.of(OidcConstants.ID_TOKEN_VALUE, token);
1540+
}
15351541
}

0 commit comments

Comments
 (0)