diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java index e166dca15..46363a656 100644 --- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java +++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java @@ -101,7 +101,7 @@ public class IntrospectingTokenService implements ResourceServerTokenServices { // create a default authentication object with authority ROLE_API private Authentication createAuthentication(JsonObject token){ // TODO: make role/authority configurable somehow - return new PreAuthenticatedAuthenticationToken(token.get("subject").getAsString(), null, AuthorityUtils.createAuthorityList("ROLE_API")); + return new PreAuthenticatedAuthenticationToken(token.get("sub").getAsString(), null, AuthorityUtils.createAuthorityList("ROLE_API")); } private OAuth2AccessToken createAccessToken(final JsonObject token, final String tokenString){ @@ -142,7 +142,7 @@ public class IntrospectingTokenService implements ResourceServerTokenServices { return false; } - if (!tokenResponse.get("valid").getAsBoolean()){ + if (!tokenResponse.get("active").getAsBoolean()){ // non-valid token return false; } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenIntrospectionView.java b/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenIntrospectionView.java index 4b908ade5..291679440 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenIntrospectionView.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/view/TokenIntrospectionView.java @@ -25,12 +25,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.web.servlet.view.AbstractView; +import com.google.common.base.Joiner; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.Gson; @@ -89,19 +91,38 @@ public class TokenIntrospectionView extends AbstractView { public JsonElement serialize(OAuth2AccessTokenEntity src, Type typeOfSrc, JsonSerializationContext context) { JsonObject token = new JsonObject(); - token.addProperty("valid", true); + token.addProperty("active", true); - JsonArray scopes = new JsonArray(); - for (String scope : src.getScope()) { - scopes.add(new JsonPrimitive(scope)); - } - token.add("scope", scopes); - - token.add("expires_at", context.serialize(src.getExpiration())); + token.addProperty("scope", Joiner.on(" ").join(src.getScope())); + token.add("exp", context.serialize(src.getExpiration())); + //token.addProperty("audience", src.getAuthenticationHolder().getAuthentication().getAuthorizationRequest().getClientId()); - token.addProperty("subject", src.getAuthenticationHolder().getAuthentication().getName()); + token.addProperty("sub", src.getAuthenticationHolder().getAuthentication().getName()); + + token.addProperty("client_id", src.getAuthenticationHolder().getAuthentication().getAuthorizationRequest().getClientId()); + + token.addProperty("token_type", src.getTokenType()); + + return token; + } + + }) + .registerTypeAdapter(OAuth2RefreshTokenEntity.class, new JsonSerializer() { + @Override + public JsonElement serialize(OAuth2RefreshTokenEntity src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject token = new JsonObject(); + + token.addProperty("active", true); + + token.addProperty("scope", Joiner.on(" ").join(src.getAuthenticationHolder().getAuthentication().getAuthorizationRequest().getScope())); + + token.add("exp", context.serialize(src.getExpiration())); + + //token.addProperty("audience", src.getAuthenticationHolder().getAuthentication().getAuthorizationRequest().getClientId()); + + token.addProperty("sub", src.getAuthenticationHolder().getAuthentication().getName()); token.addProperty("client_id", src.getAuthenticationHolder().getAuthentication().getAuthorizationRequest().getClientId()); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java index 45d1f3443..76163ddad 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java @@ -17,10 +17,13 @@ package org.mitre.oauth2.web; import java.security.Principal; +import java.util.Collection; import java.util.Map; +import java.util.Set; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.model.OAuth2RefreshTokenEntity; import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.slf4j.Logger; @@ -58,27 +61,51 @@ public class IntrospectionEndpoint { @PreAuthorize("hasRole('ROLE_CLIENT')") @RequestMapping("/introspect") - public String verify(@RequestParam("token") String tokenValue, Principal p, Model model) { + public String verify(@RequestParam("token") String tokenValue, @RequestParam("resource_id") String resourceId, @RequestParam("token_type_hint") String tokenType, + Principal p, Model model) { if (Strings.isNullOrEmpty(tokenValue)) { logger.error("Verify failed; token value is null"); - Map entity = ImmutableMap.of("valid", Boolean.FALSE); + Map entity = ImmutableMap.of("active", Boolean.FALSE); model.addAttribute("entity", entity); return "jsonEntityView"; } - OAuth2AccessTokenEntity token = null; + ClientDetailsEntity tokenClient = null; + Set scopes = null; + Object token = null; + try { - token = tokenServices.readAccessToken(tokenValue); + + // check access tokens first (includes ID tokens) + OAuth2AccessTokenEntity access = tokenServices.readAccessToken(tokenValue); + + tokenClient = access.getClient(); + scopes = access.getScope(); + + token = access; + } catch (InvalidTokenException e) { - logger.error("Verify failed; AuthenticationException", e); - Map entity = ImmutableMap.of("valid", Boolean.FALSE); - model.addAttribute("entity", entity); - return "jsonEntityView"; + logger.error("Verify failed; Invalid access token. Checking refresh token.", e); + try { + + // check refresh tokens next + OAuth2RefreshTokenEntity refresh = tokenServices.getRefreshToken(tokenValue); + + tokenClient = refresh.getClient(); + scopes = refresh.getAuthenticationHolder().getAuthentication().getAuthorizationRequest().getScope(); + + token = refresh; + + } catch (InvalidTokenException e2) { + logger.error("Verify failed; Invalid refresh token", e2); + Map entity = ImmutableMap.of("active", Boolean.FALSE); + model.addAttribute("entity", entity); + return "jsonEntityView"; + } } - ClientDetailsEntity tokenClient = token.getClient(); // clientID is the principal name in the authentication String clientId = p.getName(); ClientDetailsEntity authClient = clientService.loadClientByClientId(clientId); @@ -87,7 +114,7 @@ public class IntrospectionEndpoint { if (authClient.isAllowIntrospection()) { // if it's the same client that the token was issued to, or it at least has all the scopes the token was issued with - if (authClient.equals(tokenClient) || authClient.getScope().containsAll(token.getScope())) { + if (authClient.equals(tokenClient) || authClient.getScope().containsAll(scopes)) { // if it's a valid token, we'll print out information on it model.addAttribute("entity", token); diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java index 1d0ff4615..4fbbb5cb6 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/RevocationEndpoint.java @@ -41,31 +41,26 @@ public class RevocationEndpoint { private static Logger logger = LoggerFactory.getLogger(RevocationEndpoint.class); - public RevocationEndpoint() { - - } - - // TODO @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") @RequestMapping("/revoke") - public String revoke(@RequestParam("token") String tokenValue, Principal principal, Model model) { + public String revoke(@RequestParam("token") String tokenValue, @RequestParam("token_type_hint") String tokenType, Principal principal, Model model) { // This is the token as passed in from OAuth (in case we need it some day) //OAuth2AccessTokenEntity tok = tokenServices.getAccessToken((OAuth2Authentication) principal); - AuthorizationRequest clientAuth = null; + AuthorizationRequest authRequest = null; if (principal instanceof OAuth2Authentication) { // if the client is acting on its own behalf (the common case), pull out the client authorization request - clientAuth = ((OAuth2Authentication) principal).getAuthorizationRequest(); + authRequest = ((OAuth2Authentication) principal).getAuthorizationRequest(); } try { // check and handle access tokens first OAuth2AccessTokenEntity accessToken = tokenServices.readAccessToken(tokenValue); - if (clientAuth != null) { + if (authRequest != null) { // client acting on its own, make sure it owns the token - if (!accessToken.getClient().getClientId().equals(clientAuth.getClientId())) { + if (!accessToken.getClient().getClientId().equals(authRequest.getClientId())) { // trying to revoke a token we don't own, throw a 403 model.addAttribute("code", HttpStatus.FORBIDDEN); return "httpCodeView"; @@ -83,9 +78,9 @@ public class RevocationEndpoint { try { OAuth2RefreshTokenEntity refreshToken = tokenServices.getRefreshToken(tokenValue); - if (clientAuth != null) { + if (authRequest != null) { // client acting on its own, make sure it owns the token - if (!refreshToken.getClient().getClientId().equals(clientAuth.getClientId())) { + if (!refreshToken.getClient().getClientId().equals(authRequest.getClientId())) { // trying to revoke a token we don't own, throw a 403 model.addAttribute("code", HttpStatus.FORBIDDEN); return "httpCodeView";