Skip to content

Commit 3e07b35

Browse files
committed
Polish Bearer Token Error Handling
Issue gh-7822 Issue gh-7823
1 parent 1b15f74 commit 3e07b35

File tree

13 files changed

+46
-152
lines changed

13 files changed

+46
-152
lines changed

config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -269,7 +269,7 @@ public void getWhenUsingDefaultsWithUnavailableJwkEndpointThenInvalidToken()
269269

270270
this.mvc.perform(get("/").with(bearerToken(token)))
271271
.andExpect(status().isUnauthorized())
272-
.andExpect(invalidTokenHeader("An error occurred while attempting to decode the Jwt"));
272+
.andExpect(invalidTokenHeader("Invalid token"));
273273
}
274274

275275
@Test

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,20 +18,16 @@
1818
import java.util.Collection;
1919

2020
import org.springframework.core.convert.converter.Converter;
21-
import org.springframework.http.HttpStatus;
2221
import org.springframework.security.authentication.AbstractAuthenticationToken;
2322
import org.springframework.security.authentication.AuthenticationProvider;
2423
import org.springframework.security.core.Authentication;
2524
import org.springframework.security.core.AuthenticationException;
2625
import org.springframework.security.core.GrantedAuthority;
27-
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
28-
import org.springframework.security.oauth2.core.OAuth2Error;
2926
import org.springframework.security.oauth2.jwt.Jwt;
3027
import org.springframework.security.oauth2.jwt.JwtDecoder;
3128
import org.springframework.security.oauth2.jwt.JwtException;
3229
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
33-
import org.springframework.security.oauth2.server.resource.BearerTokenError;
34-
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
30+
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
3531
import org.springframework.util.Assert;
3632

3733
/**
@@ -63,9 +59,6 @@ public final class JwtAuthenticationProvider implements AuthenticationProvider {
6359

6460
private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter = new JwtAuthenticationConverter();
6561

66-
private static final OAuth2Error DEFAULT_INVALID_TOKEN =
67-
invalidToken("An error occurred while attempting to decode the Jwt: Invalid token");
68-
6962
public JwtAuthenticationProvider(JwtDecoder jwtDecoder) {
7063
Assert.notNull(jwtDecoder, "jwtDecoder cannot be null");
7164
this.jwtDecoder = jwtDecoder;
@@ -88,8 +81,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
8881
try {
8982
jwt = this.jwtDecoder.decode(bearer.getToken());
9083
} catch (JwtException failed) {
91-
OAuth2Error invalidToken = invalidToken(failed.getMessage());
92-
throw new OAuth2AuthenticationException(invalidToken, invalidToken.getDescription(), failed);
84+
throw new InvalidBearerTokenException(failed.getMessage(), failed);
9385
}
9486

9587
AbstractAuthenticationToken token = this.jwtAuthenticationConverter.convert(jwt);
@@ -112,17 +104,4 @@ public void setJwtAuthenticationConverter(
112104
Assert.notNull(jwtAuthenticationConverter, "jwtAuthenticationConverter cannot be null");
113105
this.jwtAuthenticationConverter = jwtAuthenticationConverter;
114106
}
115-
116-
private static OAuth2Error invalidToken(String message) {
117-
try {
118-
return new BearerTokenError(
119-
BearerTokenErrorCodes.INVALID_TOKEN,
120-
HttpStatus.UNAUTHORIZED,
121-
message,
122-
"https://tools.ietf.org/html/rfc6750#section-3.1");
123-
} catch (IllegalArgumentException malformed) {
124-
// some third-party library error messages are not suitable for RFC 6750's error message charset
125-
return DEFAULT_INVALID_TOKEN;
126-
}
127-
}
128107
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,16 +27,13 @@
2727
import com.nimbusds.jwt.JWTParser;
2828

2929
import org.springframework.core.convert.converter.Converter;
30-
import org.springframework.http.HttpStatus;
3130
import org.springframework.lang.NonNull;
3231
import org.springframework.security.authentication.AuthenticationManager;
3332
import org.springframework.security.authentication.AuthenticationManagerResolver;
3433
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
35-
import org.springframework.security.oauth2.core.OAuth2Error;
3634
import org.springframework.security.oauth2.jwt.JwtDecoder;
3735
import org.springframework.security.oauth2.jwt.JwtDecoders;
38-
import org.springframework.security.oauth2.server.resource.BearerTokenError;
39-
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
36+
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
4037
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
4138
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
4239
import org.springframework.util.Assert;
@@ -57,8 +54,6 @@
5754
* @since 5.3
5855
*/
5956
public final class JwtIssuerAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
60-
private static final OAuth2Error DEFAULT_INVALID_TOKEN = invalidToken("Invalid token");
61-
6257
private final AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;
6358
private final Converter<HttpServletRequest, String> issuerConverter = new JwtClaimIssuerConverter();
6459

@@ -118,7 +113,7 @@ public AuthenticationManager resolve(HttpServletRequest request) {
118113
String issuer = this.issuerConverter.convert(request);
119114
AuthenticationManager authenticationManager = this.issuerAuthenticationManagerResolver.resolve(issuer);
120115
if (authenticationManager == null) {
121-
throw new OAuth2AuthenticationException(invalidToken("Invalid issuer " + issuer));
116+
throw new InvalidBearerTokenException("Invalid issuer");
122117
}
123118
return authenticationManager;
124119
}
@@ -137,9 +132,9 @@ public String convert(@NonNull HttpServletRequest request) {
137132
return issuer;
138133
}
139134
} catch (Exception e) {
140-
throw new OAuth2AuthenticationException(invalidToken(e.getMessage()));
135+
throw new InvalidBearerTokenException(e.getMessage(), e);
141136
}
142-
throw new OAuth2AuthenticationException(invalidToken("Missing issuer"));
137+
throw new InvalidBearerTokenException("Missing issuer");
143138
}
144139
}
145140

@@ -164,17 +159,4 @@ public AuthenticationManager resolve(String issuer) {
164159
return null;
165160
}
166161
}
167-
168-
private static OAuth2Error invalidToken(String message) {
169-
try {
170-
return new BearerTokenError(
171-
BearerTokenErrorCodes.INVALID_TOKEN,
172-
HttpStatus.UNAUTHORIZED,
173-
message,
174-
"https://tools.ietf.org/html/rfc6750#section-3.1");
175-
} catch (IllegalArgumentException malformed) {
176-
// some third-party library error messages are not suitable for RFC 6750's error message charset
177-
return DEFAULT_INVALID_TOKEN;
178-
}
179-
}
180162
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtReactiveAuthenticationManager.java

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,18 +19,15 @@
1919
import reactor.core.publisher.Mono;
2020

2121
import org.springframework.core.convert.converter.Converter;
22-
import org.springframework.http.HttpStatus;
2322
import org.springframework.security.authentication.AbstractAuthenticationToken;
2423
import org.springframework.security.authentication.ReactiveAuthenticationManager;
2524
import org.springframework.security.core.Authentication;
2625
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
27-
import org.springframework.security.oauth2.core.OAuth2Error;
2826
import org.springframework.security.oauth2.jwt.Jwt;
2927
import org.springframework.security.oauth2.jwt.JwtException;
3028
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
3129
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
32-
import org.springframework.security.oauth2.server.resource.BearerTokenError;
33-
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
30+
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
3431
import org.springframework.util.Assert;
3532

3633
/**
@@ -45,9 +42,6 @@ public final class JwtReactiveAuthenticationManager implements ReactiveAuthentic
4542
private Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter
4643
= new ReactiveJwtAuthenticationConverterAdapter(new JwtAuthenticationConverter());
4744

48-
private static final OAuth2Error DEFAULT_INVALID_TOKEN =
49-
invalidToken("An error occurred while attempting to decode the Jwt: Invalid token");
50-
5145
public JwtReactiveAuthenticationManager(ReactiveJwtDecoder jwtDecoder) {
5246
Assert.notNull(jwtDecoder, "jwtDecoder cannot be null");
5347
this.jwtDecoder = jwtDecoder;
@@ -78,20 +72,6 @@ public void setJwtAuthenticationConverter(
7872
}
7973

8074
private OAuth2AuthenticationException onError(JwtException e) {
81-
OAuth2Error invalidRequest = invalidToken(e.getMessage());
82-
return new OAuth2AuthenticationException(invalidRequest, invalidRequest.getDescription(), e);
83-
}
84-
85-
private static OAuth2Error invalidToken(String message) {
86-
try {
87-
return new BearerTokenError(
88-
BearerTokenErrorCodes.INVALID_TOKEN,
89-
HttpStatus.UNAUTHORIZED,
90-
message,
91-
"https://tools.ietf.org/html/rfc6750#section-3.1");
92-
} catch (IllegalArgumentException malformed) {
93-
// some third-party library error messages are not suitable for RFC 6750's error message charset
94-
return DEFAULT_INVALID_TOKEN;
95-
}
75+
return new InvalidBearerTokenException(e.getMessage(), e);
9676
}
9777
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,18 +18,15 @@
1818
import java.time.Instant;
1919
import java.util.Collection;
2020

21-
import org.springframework.http.HttpStatus;
2221
import org.springframework.security.authentication.AbstractAuthenticationToken;
2322
import org.springframework.security.authentication.AuthenticationProvider;
2423
import org.springframework.security.core.Authentication;
2524
import org.springframework.security.core.AuthenticationException;
2625
import org.springframework.security.core.GrantedAuthority;
2726
import org.springframework.security.oauth2.core.OAuth2AccessToken;
2827
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
29-
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
30-
import org.springframework.security.oauth2.core.OAuth2Error;
3128
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
32-
import org.springframework.security.oauth2.server.resource.BearerTokenError;
29+
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
3330
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
3431
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
3532
import org.springframework.util.Assert;
@@ -60,9 +57,6 @@
6057
* @see AuthenticationProvider
6158
*/
6259
public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
63-
private static final BearerTokenError DEFAULT_INVALID_TOKEN =
64-
invalidToken("An error occurred while attempting to introspect the token: Invalid token");
65-
6660
private OpaqueTokenIntrospector introspector;
6761

6862
/**
@@ -95,8 +89,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
9589
try {
9690
principal = this.introspector.introspect(bearer.getToken());
9791
} catch (OAuth2IntrospectionException failed) {
98-
OAuth2Error invalidToken = invalidToken(failed.getMessage());
99-
throw new OAuth2AuthenticationException(invalidToken);
92+
throw new InvalidBearerTokenException(failed.getMessage());
10093
}
10194

10295
AbstractAuthenticationToken result = convert(principal, bearer.getToken());
@@ -119,15 +112,4 @@ private AbstractAuthenticationToken convert(OAuth2AuthenticatedPrincipal princip
119112
token, iat, exp);
120113
return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
121114
}
122-
123-
private static BearerTokenError invalidToken(String message) {
124-
try {
125-
return new BearerTokenError("invalid_token",
126-
HttpStatus.UNAUTHORIZED, message,
127-
"https://tools.ietf.org/html/rfc7662#section-2.2");
128-
} catch (IllegalArgumentException malformed) {
129-
// some third-party library error messages are not suitable for RFC 6750's error message charset
130-
return DEFAULT_INVALID_TOKEN;
131-
}
132-
}
133115
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManager.java

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,15 +21,13 @@
2121

2222
import reactor.core.publisher.Mono;
2323

24-
import org.springframework.http.HttpStatus;
2524
import org.springframework.security.authentication.ReactiveAuthenticationManager;
2625
import org.springframework.security.core.Authentication;
2726
import org.springframework.security.core.GrantedAuthority;
2827
import org.springframework.security.oauth2.core.OAuth2AccessToken;
2928
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
30-
import org.springframework.security.oauth2.core.OAuth2Error;
3129
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
32-
import org.springframework.security.oauth2.server.resource.BearerTokenError;
30+
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
3331
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
3432
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
3533
import org.springframework.util.Assert;
@@ -60,9 +58,6 @@
6058
* @see ReactiveAuthenticationManager
6159
*/
6260
public class OpaqueTokenReactiveAuthenticationManager implements ReactiveAuthenticationManager {
63-
private static final BearerTokenError DEFAULT_INVALID_TOKEN =
64-
invalidToken("An error occurred while attempting to introspect the token: Invalid token");
65-
6661
private ReactiveOpaqueTokenIntrospector introspector;
6762

6863
/**
@@ -99,19 +94,7 @@ private Mono<BearerTokenAuthentication> authenticate(String token) {
9994
.onErrorMap(OAuth2IntrospectionException.class, this::onError);
10095
}
10196

102-
private static BearerTokenError invalidToken(String message) {
103-
try {
104-
return new BearerTokenError("invalid_token",
105-
HttpStatus.UNAUTHORIZED, message,
106-
"https://tools.ietf.org/html/rfc7662#section-2.2");
107-
} catch (IllegalArgumentException e) {
108-
// some third-party library error messages are not suitable for RFC 6750's error message charset
109-
return DEFAULT_INVALID_TOKEN;
110-
}
111-
}
112-
11397
private OAuth2AuthenticationException onError(OAuth2IntrospectionException e) {
114-
OAuth2Error invalidRequest = invalidToken(e.getMessage());
115-
return new OAuth2AuthenticationException(invalidRequest, e.getMessage());
98+
return new InvalidBearerTokenException(e.getMessage(), e);
11699
}
117100
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,12 +21,13 @@
2121
import javax.servlet.http.HttpServletRequest;
2222

2323
import org.springframework.http.HttpHeaders;
24-
import org.springframework.http.HttpStatus;
2524
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
2625
import org.springframework.security.oauth2.server.resource.BearerTokenError;
27-
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
2826
import org.springframework.util.StringUtils;
2927

28+
import static org.springframework.security.oauth2.server.resource.BearerTokenErrors.invalidRequest;
29+
import static org.springframework.security.oauth2.server.resource.BearerTokenErrors.invalidToken;
30+
3031
/**
3132
* The default {@link BearerTokenResolver} implementation based on RFC 6750.
3233
*
@@ -53,10 +54,7 @@ public String resolve(HttpServletRequest request) {
5354
String parameterToken = resolveFromRequestParameters(request);
5455
if (authorizationHeaderToken != null) {
5556
if (parameterToken != null) {
56-
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST,
57-
HttpStatus.BAD_REQUEST,
58-
"Found multiple bearer tokens in the request",
59-
"https://tools.ietf.org/html/rfc6750#section-3.1");
57+
BearerTokenError error = invalidRequest("Found multiple bearer tokens in the request");
6058
throw new OAuth2AuthenticationException(error);
6159
}
6260
return authorizationHeaderToken;
@@ -93,10 +91,7 @@ private static String resolveFromAuthorizationHeader(HttpServletRequest request)
9391
Matcher matcher = authorizationPattern.matcher(authorization);
9492

9593
if (!matcher.matches()) {
96-
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
97-
HttpStatus.UNAUTHORIZED,
98-
"Bearer token is malformed",
99-
"https://tools.ietf.org/html/rfc6750#section-3.1");
94+
BearerTokenError error = invalidToken("Bearer token is malformed");
10095
throw new OAuth2AuthenticationException(error);
10196
}
10297

@@ -115,10 +110,7 @@ private static String resolveFromRequestParameters(HttpServletRequest request) {
115110
return values[0];
116111
}
117112

118-
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST,
119-
HttpStatus.BAD_REQUEST,
120-
"Found multiple bearer tokens in the request",
121-
"https://tools.ietf.org/html/rfc6750#section-3.1");
113+
BearerTokenError error = invalidRequest("Found multiple bearer tokens in the request");
122114
throw new OAuth2AuthenticationException(error);
123115
}
124116

0 commit comments

Comments
 (0)