added flag to allow introspection, relaxed same-client restrictions on introspection and chained tokens
parent
6eabc895b9
commit
18ddd8333f
|
@ -64,8 +64,9 @@ public class ClientDetailsEntity implements ClientDetails {
|
||||||
/** Our own fields **/
|
/** Our own fields **/
|
||||||
private String clientDescription = ""; // human-readable description
|
private String clientDescription = ""; // human-readable description
|
||||||
private boolean allowMultipleAccessTokens = false; // do we allow multiple access tokens, or not?
|
private boolean allowMultipleAccessTokens = false; // do we allow multiple access tokens, or not?
|
||||||
private boolean reuseRefreshToken = false; // do we let someone reuse a refresh token?
|
private boolean reuseRefreshToken = true; // do we let someone reuse a refresh token?
|
||||||
private boolean dynamicallyRegistered = false; // was this client dynamically registered?
|
private boolean dynamicallyRegistered = false; // was this client dynamically registered?
|
||||||
|
private boolean allowIntrospection = false; // do we let this client call the introspection endpoint?
|
||||||
private Integer idTokenValiditySeconds; //timeout for id tokens
|
private Integer idTokenValiditySeconds; //timeout for id tokens
|
||||||
|
|
||||||
/** Fields from ClientDetails interface **/
|
/** Fields from ClientDetails interface **/
|
||||||
|
@ -301,6 +302,22 @@ public class ClientDetailsEntity implements ClientDetails {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the allowIntrospection
|
||||||
|
*/
|
||||||
|
@Basic
|
||||||
|
@Column(name="allow_introspection")
|
||||||
|
public boolean isAllowIntrospection() {
|
||||||
|
return allowIntrospection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param allowIntrospection the allowIntrospection to set
|
||||||
|
*/
|
||||||
|
public void setAllowIntrospection(boolean allowIntrospection) {
|
||||||
|
this.allowIntrospection = allowIntrospection;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the clientSecret is not null, then it is always required.
|
* If the clientSecret is not null, then it is always required.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -76,13 +76,6 @@ public class ChainedTokenGranter extends AbstractTokenGranter {
|
||||||
requestedScopes = new HashSet<String>();
|
requestedScopes = new HashSet<String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the incoming client id against the client that was issued the original token
|
|
||||||
// TODO: right now, this only lets a client chain a request, not a resource server. We need
|
|
||||||
// a way to let one client get a token chained from another client's token, securely.
|
|
||||||
if (!client.getClientId().equals(authorizationRequest.getClientId())) {
|
|
||||||
throw new InvalidClientException("Not the right client for this token");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if our scopes are a valid subset of what's allowed, we can continue
|
// if our scopes are a valid subset of what's allowed, we can continue
|
||||||
if (approvedScopes.containsAll(requestedScopes)) {
|
if (approvedScopes.containsAll(requestedScopes)) {
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.mitre.oauth2.service.OAuth2TokenEntityService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
||||||
|
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
||||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
|
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
|
||||||
|
@ -102,14 +103,20 @@ public class IntrospectionEndpoint {
|
||||||
ClientDetailsEntity authClient = clientService.loadClientByClientId(clientId);
|
ClientDetailsEntity authClient = clientService.loadClientByClientId(clientId);
|
||||||
|
|
||||||
if (tokenClient != null && authClient != null) {
|
if (tokenClient != null && authClient != null) {
|
||||||
if (Objects.equal(authClient, tokenClient)) { // TODO: this lets a client introspect but not an RS
|
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 it's a valid token, we'll print out information on it
|
// if it's a valid token, we'll print out information on it
|
||||||
modelAndView.setViewName("tokenIntrospection");
|
modelAndView.setViewName("tokenIntrospection");
|
||||||
modelAndView.addObject("entity", token);
|
modelAndView.addObject("entity", token);
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidClientException("Clients did not match.");
|
throw new InvalidScopeException("Tried to introspect a token of different scope");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new InvalidClientException("Clients can't introspect.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidClientException("No client found.");
|
throw new InvalidClientException("No client found.");
|
||||||
|
|
|
@ -72,6 +72,7 @@ CREATE TABLE IF NOT EXISTS client_details (
|
||||||
allow_multiple_access_tokens BOOLEAN NOT NULL DEFAULT true,
|
allow_multiple_access_tokens BOOLEAN NOT NULL DEFAULT true,
|
||||||
reuse_refresh_tokens BOOLEAN NOT NULL DEFAULT true,
|
reuse_refresh_tokens BOOLEAN NOT NULL DEFAULT true,
|
||||||
dynamically_registered BOOLEAN NOT NULL DEFAULT false,
|
dynamically_registered BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
allow_introspection BOOLEAN NOT NULL DEFAULT false,
|
||||||
id_token_validity_seconds BIGINT,
|
id_token_validity_seconds BIGINT,
|
||||||
|
|
||||||
client_id VARCHAR(256),
|
client_id VARCHAR(256),
|
||||||
|
|
|
@ -64,9 +64,10 @@ CREATE TABLE blacklisted_site (
|
||||||
CREATE TABLE client_details (
|
CREATE TABLE client_details (
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
client_description VARCHAR(256),
|
client_description VARCHAR(256),
|
||||||
allow_multiple_access_tokens TINYINT NOT NULL DEFAULT 0,
|
allow_multiple_access_tokens BOOLEAN NOT NULL DEFAULT 0,
|
||||||
reuse_refresh_tokens TINYINT NOT NULL DEFAULT 0,
|
reuse_refresh_tokens BOOLEAN NOT NULL DEFAULT 1,
|
||||||
dynamically_registered TINYINT NOT NULL DEFAULT 0,
|
dynamically_registered BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
allow_introspection BOOLEAN NOT NULL DEFAULT 0,
|
||||||
id_token_validity_seconds BIGINT,
|
id_token_validity_seconds BIGINT,
|
||||||
|
|
||||||
client_id VARCHAR(256),
|
client_id VARCHAR(256),
|
||||||
|
@ -103,7 +104,7 @@ CREATE TABLE client_details (
|
||||||
id_token_encrypted_response_int VARCHAR(256),
|
id_token_encrypted_response_int VARCHAR(256),
|
||||||
|
|
||||||
default_max_age BIGINT,
|
default_max_age BIGINT,
|
||||||
require_auth_time TINYINT NOT NULL DEFAULT 0,
|
require_auth_time BOOLEAN NOT NULL DEFAULT 0,
|
||||||
default_acr VARCHAR(256)
|
default_acr VARCHAR(256)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue