package com.dbg.cloud.acheron.plugins.oauth2.authserver.base; import com.dbg.cloud.acheron.plugins.oauth2.authserver.base.introspection.IntrospectionOperation; import com.dbg.cloud.acheron.plugins.oauth2.authserver.base.introspection.IntrospectionResult; import com.dbg.cloud.acheron.plugins.oauth2.authserver.base.introspection.IntrospectionSpec; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.HttpServletRequest; import java.time.Instant; import java.util.Enumeration; import java.util.Optional; public interface AccessToken { String token(); Optional<Instant> expiration(); IntrospectionResult introspection(IntrospectionSpec introspectionSpec) throws IntrospectionOperation.TechnicalException; @Slf4j final class BearerToken implements AccessToken { private static final String ACCESS_TOKEN = "access_token"; private static final String BEARER_TYPE = "Bearer"; private final String accessToken; public BearerToken(@NonNull final HttpServletRequest request) throws BearerTokenException { accessToken = extractToken(request); if (accessToken == null || accessToken.trim().isEmpty()) { throw new BearerTokenException(); } } @Override public String token() { return accessToken; } @Override public Optional<Instant> expiration() { return Optional.empty(); } @Override public IntrospectionResult introspection(final IntrospectionSpec introspectionSpec) throws IntrospectionOperation.TechnicalException { final IntrospectionOperation operation = introspectionSpec.operationForToken(accessToken); return operation.result(); } private String extractToken(final HttpServletRequest request) { String token = extractHeaderToken(request); // Bearer type allows a request parameter as well if (token == null) { log.info("{} not found in headers. Trying request parameters.", ACCESS_TOKEN); token = request.getParameter(ACCESS_TOKEN); if (token == null) { log.info("{} not found in request parameters. Not an OAuth2 request.", ACCESS_TOKEN); } } return token; } private String extractHeaderToken(final HttpServletRequest request) { final Enumeration<String> headers = request.getHeaders("Authorization"); while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that) final String value = headers.nextElement(); if ((value.toLowerCase().startsWith(BEARER_TYPE.toLowerCase()))) { String authHeaderValue = value.substring(BEARER_TYPE.length()).trim(); int commaIndex = authHeaderValue.indexOf(','); if (commaIndex > 0) { authHeaderValue = authHeaderValue.substring(0, commaIndex); } return authHeaderValue; } } return null; } public final class BearerTokenException extends Exception { } } final class Normal implements AccessToken { private final String token; private final Instant expiration; public Normal(final @NonNull String token, final @NonNull Instant expiration) { this.token = token; this.expiration = expiration; } @Override public String token() { return token; } @Override public Optional<Instant> expiration() { return Optional.of(expiration); } @Override public IntrospectionResult introspection(IntrospectionSpec introspectionSpec) throws IntrospectionOperation .TechnicalException { final IntrospectionOperation operation = introspectionSpec.operationForToken(token); return operation.result(); } } }