brought introspection endpoint and introspection token services into compliance with draft, addresses #376
parent
2d16b8d458
commit
a9da88fb79
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<OAuth2RefreshTokenEntity>() {
|
||||
@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());
|
||||
|
||||
|
|
|
@ -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<String,Boolean> entity = ImmutableMap.of("valid", Boolean.FALSE);
|
||||
Map<String,Boolean> entity = ImmutableMap.of("active", Boolean.FALSE);
|
||||
model.addAttribute("entity", entity);
|
||||
return "jsonEntityView";
|
||||
}
|
||||
|
||||
OAuth2AccessTokenEntity token = null;
|
||||
|
||||
ClientDetailsEntity tokenClient = null;
|
||||
Set<String> 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<String,Boolean> 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<String,Boolean> 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);
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in New Issue