Skip to content

Commit f5912a4

Browse files
committed
Provide Bearer Token resolving strategy
This commit creates oauth2-resource-server module and provides a strategy for resolving Bearer Token from HTTP request together with default implementation that aligns with RFC 6750. Closes spring-projectsgh-5121
1 parent e86becc commit f5912a4

File tree

11 files changed

+705
-0
lines changed

11 files changed

+705
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apply plugin: 'io.spring.convention.spring-module'
2+
3+
dependencies {
4+
compile project(':spring-security-core')
5+
compile project(':spring-security-oauth2-core')
6+
compile project(':spring-security-web')
7+
compile springCoreDependency
8+
compile 'com.nimbusds:oauth2-oidc-sdk'
9+
10+
optional project(':spring-security-oauth2-jose')
11+
12+
provided 'javax.servlet:javax.servlet-api'
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.server.resource;
18+
19+
import org.springframework.security.core.Authentication;
20+
import org.springframework.security.core.AuthenticationException;
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* This exception is thrown for all Bearer Token related {@link Authentication} errors.
25+
*
26+
* @author Vedran Pavic
27+
* @since 5.1
28+
* @see <a href="https://tools.ietf.org/html/rfc6750#section-3" target="_blank">RFC 6750 Section 3: The WWW-Authenticate Response Header Field</a>
29+
*/
30+
public class BearerTokenAuthenticationException extends AuthenticationException {
31+
32+
private final BearerTokenError error;
33+
34+
/**
35+
* Create a new {@link BearerTokenAuthenticationException}.
36+
* @param error the {@link BearerTokenError Bearer Token Error}
37+
* @param message the detail message
38+
* @param cause the root cause
39+
*/
40+
public BearerTokenAuthenticationException(BearerTokenError error, String message, Throwable cause) {
41+
super(message, cause);
42+
Assert.notNull(error, "error must not be null");
43+
this.error = error;
44+
}
45+
46+
/**
47+
* Create a new {@link BearerTokenAuthenticationException}.
48+
* @param error the {@link BearerTokenError Bearer Token Error}
49+
* @param message the detail message
50+
*/
51+
public BearerTokenAuthenticationException(BearerTokenError error, String message) {
52+
super(message);
53+
Assert.notNull(error, "error must not be null");
54+
this.error = error;
55+
}
56+
57+
/**
58+
* Return the Bearer Token error
59+
* @return the error
60+
*/
61+
public BearerTokenError getError() {
62+
return error;
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.server.resource;
18+
19+
import java.io.Serializable;
20+
21+
import org.springframework.security.core.SpringSecurityCoreVersion;
22+
import org.springframework.util.Assert;
23+
24+
/**
25+
* A representation of an Bearer Token Error.
26+
*
27+
* @author Vedran Pavic
28+
* @since 5.1
29+
* @see BearerTokenErrorCodes
30+
* @see <a href="https://tools.ietf.org/html/rfc6750#section-3" target="_blank">RFC 6750 Section 3: The WWW-Authenticate Response Header Field</a>
31+
*/
32+
public final class BearerTokenError implements Serializable {
33+
34+
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
35+
36+
private final String errorCode;
37+
38+
private final String description;
39+
40+
private final String uri;
41+
42+
private final String scope;
43+
44+
/**
45+
* Create a {@code BearerTokenError} using the provided parameters.
46+
* @param errorCode the error code
47+
*/
48+
public BearerTokenError(String errorCode) {
49+
this(errorCode, null, null, null);
50+
}
51+
52+
/**
53+
* Create a {@code BearerTokenError} using the provided parameters.
54+
* @param errorCode the error code
55+
* @param description the description
56+
* @param uri the URI
57+
* @param scope the scope
58+
*/
59+
public BearerTokenError(String errorCode, String description, String uri, String scope) {
60+
Assert.hasText(errorCode, "errorCode must not be empty");
61+
this.errorCode = errorCode;
62+
this.description = description;
63+
this.uri = uri;
64+
this.scope = scope;
65+
}
66+
67+
/**
68+
* Return the error code.
69+
* @return the error code
70+
*/
71+
public String getErrorCode() {
72+
return this.errorCode;
73+
}
74+
75+
/**
76+
* Return the description.
77+
* @return the description
78+
*/
79+
public String getDescription() {
80+
return this.description;
81+
}
82+
83+
/**
84+
* Return the URI.
85+
* @return the URI
86+
*/
87+
public String getUri() {
88+
return this.uri;
89+
}
90+
91+
/**
92+
* Return the scope.
93+
* @return the scope
94+
*/
95+
public String getScope() {
96+
return scope;
97+
}
98+
99+
@Override
100+
public String toString() {
101+
return "[" + this.getErrorCode() + "]" + (this.description != null ? " " + this.description : "");
102+
}
103+
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.server.resource;
18+
19+
/**
20+
* Standard error codes defined by the OAuth 2.0 Authorization Framework: Bearer Token Usage.
21+
*
22+
* @author Vedran Pavic
23+
* @since 5.1
24+
* @see <a href="https://tools.ietf.org/html/rfc6750#section-3.1" target="_blank">RFC 6750 Section 3.1: Error Codes</a>
25+
*/
26+
public interface BearerTokenErrorCodes {
27+
28+
/**
29+
* {@code invalid_request} - The request is missing a required parameter, includes an unsupported parameter or
30+
* parameter value, repeats the same parameter, uses more than one method for including an access token, or is
31+
* otherwise malformed.
32+
*/
33+
String INVALID_REQUEST = "invalid_request";
34+
35+
/**
36+
* {@code invalid_token} - The access token provided is expired, revoked, malformed, or invalid for other
37+
* reasons.
38+
*/
39+
String INVALID_TOKEN = "invalid_token";
40+
41+
/**
42+
* {@code insufficient_scope} - The request requires higher privileges than provided by the access token.
43+
*/
44+
String INSUFFICIENT_SCOPE = "insufficient_scope";
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* OAuth 2.0 Resource Server core classes and interfaces providing support.
19+
*/
20+
package org.springframework.security.oauth2.server.resource;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.server.resource.web;
18+
19+
import javax.servlet.http.HttpServletRequest;
20+
21+
/**
22+
* A strategy for resolving Bearer Token from the {@link HttpServletRequest}.
23+
*
24+
* @author Vedran Pavic
25+
* @since 5.1
26+
* @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750 Section 2: Authenticated Requests</a>
27+
*/
28+
public interface BearerTokenResolver {
29+
30+
/**
31+
* Resolve the Bearer Token value from the request.
32+
* @param request the request
33+
* @return the Bearer Token value
34+
*/
35+
String resolve(HttpServletRequest request);
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.server.resource.web;
18+
19+
import java.util.regex.Matcher;
20+
import java.util.regex.Pattern;
21+
22+
import javax.servlet.http.HttpServletRequest;
23+
24+
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationException;
25+
import org.springframework.security.oauth2.server.resource.BearerTokenError;
26+
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
27+
import org.springframework.util.StringUtils;
28+
29+
/**
30+
* The default {@link BearerTokenResolver} implementation based on RFC 6750.
31+
*
32+
* @author Vedran Pavic
33+
* @since 5.1
34+
* @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750 Section 2: Authenticated Requests</a>
35+
*/
36+
public final class DefaultBearerTokenResolver implements BearerTokenResolver {
37+
38+
private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[^\\s]+)*$");
39+
40+
private boolean useFormEncodedBodyParameter = false;
41+
42+
private boolean useUriQueryParameter = false;
43+
44+
@Override
45+
public String resolve(HttpServletRequest request) {
46+
String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
47+
String parameterToken = request.getParameter("access_token");
48+
if (authorizationHeaderToken != null) {
49+
if (parameterToken != null) {
50+
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST);
51+
throw new BearerTokenAuthenticationException(error, error.toString());
52+
}
53+
return authorizationHeaderToken;
54+
}
55+
else if (parameterToken != null && isParameterTokenSupportedForRequest(request)) {
56+
return parameterToken;
57+
}
58+
return null;
59+
}
60+
61+
/**
62+
* Set if transport of access token using form-encoded body parameter is supported. Defaults to {@code false}.
63+
* @param useFormEncodedBodyParameter if the form-encoded body parameter is supported
64+
*/
65+
public void setUseFormEncodedBodyParameter(boolean useFormEncodedBodyParameter) {
66+
this.useFormEncodedBodyParameter = useFormEncodedBodyParameter;
67+
}
68+
69+
/**
70+
* Set if transport of access token using URI query parameter is supported. Defaults to {@code false}.
71+
* @param useUriQueryParameter if the URI query parameter is supported
72+
*/
73+
public void setUseUriQueryParameter(boolean useUriQueryParameter) {
74+
this.useUriQueryParameter = useUriQueryParameter;
75+
}
76+
77+
private static String resolveFromAuthorizationHeader(HttpServletRequest request) {
78+
String authorization = request.getHeader("Authorization");
79+
if (StringUtils.hasText(authorization)) {
80+
Matcher matcher = authorizationPattern.matcher(authorization);
81+
if (matcher.matches()) {
82+
return matcher.group("token");
83+
}
84+
}
85+
return null;
86+
}
87+
88+
private boolean isParameterTokenSupportedForRequest(HttpServletRequest request) {
89+
return ((this.useFormEncodedBodyParameter && "POST".equals(request.getMethod()))
90+
|| (this.useUriQueryParameter && "GET".equals(request.getMethod())));
91+
}
92+
93+
}

0 commit comments

Comments
 (0)