Skip to content
This repository has been archived by the owner on May 31, 2022. It is now read-only.

#894 add support for token extraction … #895

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -13,23 +13,25 @@

package org.springframework.security.oauth2.provider.authentication;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

import static org.springframework.security.oauth2.common.OAuth2AccessToken.BEARER_TYPE;
import static org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE;

/**
* {@link TokenExtractor} that strips the authenticator from a bearer token request (with an Authorization header in the
* form "Bearer <code>&lt;TOKEN&gt;</code>", or as a request parameter if that fails). The access token is the principal in
* the authentication token that is extracted.
*
*
* @author Dave Syer
*
*
*/
public class BearerTokenExtractor implements TokenExtractor {

Expand Down Expand Up @@ -57,7 +59,7 @@ protected String extractToken(HttpServletRequest request) {
logger.debug("Token not found in request parameters. Not an OAuth2 request.");
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
request.setAttribute(ACCESS_TOKEN_TYPE, BEARER_TYPE);
}
}

Expand All @@ -66,19 +68,28 @@ protected String extractToken(HttpServletRequest request) {

/**
* Extract the OAuth bearer token from a header.
*
*
* @param request The request.
* @return The token, or null if no OAuth authorization header was supplied.
*/
protected String extractHeaderToken(HttpServletRequest request) {
Enumeration<String> headers = request.getHeaders("Authorization");
while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
// Typically there should be only one Authorization header (RFC7230, 4.2), but who knows ...
while (headers.hasMoreElements()) {
String value = headers.nextElement();
if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
// Add this here for the auth details later. Would be better to change the signature of this method.
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
final int bearerIndex = value.toLowerCase().indexOf(BEARER_TYPE.toLowerCase());

if (bearerIndex >= 0) {
// RFC7230 (3.2.2) allows header fields to be defined as comma-separated lists,
// but we just care about the Bearer Token

final int tokenIndex = bearerIndex + BEARER_TYPE.length();
String authHeaderValue = value.substring(tokenIndex).trim();

// Add this here for the auth details later.
// Would be better to change the signature of this method.
request.setAttribute(ACCESS_TOKEN_TYPE, value.substring(0, BEARER_TYPE.length()).trim());

int commaIndex = authHeaderValue.indexOf(',');
if (commaIndex > 0) {
authHeaderValue = authHeaderValue.substring(0, commaIndex);
Expand Down
@@ -0,0 +1,101 @@
package org.springframework.security.oauth2.provider.authentication;


import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
* Tests RFC7230 compliance of BearerTokenExtractor.
*
* @author Jan Brennenstuhl
*
*/
public class BearerTokenExtractorTests {

private static final String SOME_OAUTH2_TOKEN = "SOME_OAUTH2_TOKEN";
private final BearerTokenExtractor objectUnderTest = new BearerTokenExtractor();

@Test
public void shouldExtractTokenWhenValidBearerTokenAvailable() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Bearer " + SOME_OAUTH2_TOKEN);

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertEquals(SOME_OAUTH2_TOKEN, extractedToken);
}

@Test
public void shouldExtractTokenWhenMultipleAuthHeadersPresent() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk");
request.addHeader("Authorization", "Bearer " + SOME_OAUTH2_TOKEN);

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertEquals(SOME_OAUTH2_TOKEN, extractedToken);
}

@Test
public void shouldExtractTokenWhenMultipleAuthHeaderValuesAvailable() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Bearer " + SOME_OAUTH2_TOKEN + ", Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk");

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertEquals(SOME_OAUTH2_TOKEN, extractedToken);
}

@Test
public void shouldExtractTokenAlthoughAuthHeaderValueIncludesSpaces() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", " Bearer " + SOME_OAUTH2_TOKEN + " ");

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertEquals(SOME_OAUTH2_TOKEN, extractedToken);
}

@Test
public void shouldExtractTokenWhenBearerNotPrimary() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk, Bearer " + SOME_OAUTH2_TOKEN);

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertEquals(SOME_OAUTH2_TOKEN, extractedToken);
}

@Test
public void shouldExtractTokenWhenBearerMisspelled() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "BEAREr " + SOME_OAUTH2_TOKEN);

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertEquals(SOME_OAUTH2_TOKEN, extractedToken);
}

@Test
public void shouldNotExtractTokenWhenAuthHeaderMissing() {
final MockHttpServletRequest request = new MockHttpServletRequest();

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertNull(extractedToken);
}

@Test
public void shouldNotExtractTokenWhenBearerTokenMissingPart1() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk");

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertNull(extractedToken);
}

@Test
public void shouldNotExtractTokenWhenBearerTokenMissingPart2() {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "");

final String extractedToken = objectUnderTest.extractHeaderToken(request);
assertNull(extractedToken);
}
}