refactored dynamic registration endpoint's checks for client consistency
parent
22ab1173a7
commit
deaccf437e
|
@ -112,169 +112,21 @@ public class ClientDynamicRegistrationEndpoint {
|
||||||
newClient.setClientId(null);
|
newClient.setClientId(null);
|
||||||
newClient.setClientSecret(null);
|
newClient.setClientSecret(null);
|
||||||
|
|
||||||
// set of scopes that are OK for clients to dynamically register for
|
// do validation on the fields
|
||||||
Set<SystemScope> dynScopes = scopeService.getDynReg();
|
try {
|
||||||
|
newClient = validateScopes(newClient);
|
||||||
// scopes that the client is asking for
|
newClient = validateResponseTypes(newClient);
|
||||||
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
|
newClient = validateGrantTypes(newClient);
|
||||||
|
newClient = validateRedirectUris(newClient);
|
||||||
// the scopes that the client can have must be a subset of the dynamically allowed scopes
|
newClient = validateAuth(newClient);
|
||||||
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);
|
} catch (ValidationException ve) {
|
||||||
|
// validation failed, return an error
|
||||||
// if the client didn't ask for any, give them the defaults
|
m.addAttribute("error", ve.getError());
|
||||||
if (allowedScopes == null || allowedScopes.isEmpty()) {
|
m.addAttribute("errorMessage", ve.getErrorDescription());
|
||||||
allowedScopes = scopeService.getDefaults();
|
m.addAttribute("code", ve.getStatus());
|
||||||
}
|
|
||||||
|
|
||||||
newClient.setScope(scopeService.toStrings(allowedScopes));
|
|
||||||
|
|
||||||
|
|
||||||
// set default grant types if needed
|
|
||||||
if (newClient.getGrantTypes() == null || newClient.getGrantTypes().isEmpty()) {
|
|
||||||
if (newClient.getScope().contains("offline_access")) { // client asked for offline access
|
|
||||||
newClient.setGrantTypes(Sets.newHashSet("authorization_code", "refresh_token")); // allow authorization code and refresh token grant types by default
|
|
||||||
} else {
|
|
||||||
newClient.setGrantTypes(Sets.newHashSet("authorization_code")); // allow authorization code grant type by default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getResponseTypes() == null) {
|
|
||||||
newClient.setResponseTypes(new HashSet<String>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter out unknown grant types
|
|
||||||
// TODO: make this a pluggable service
|
|
||||||
Set<String> requestedGrantTypes = new HashSet<String>(newClient.getGrantTypes());
|
|
||||||
requestedGrantTypes.removeAll(
|
|
||||||
ImmutableSet.of("authorization_code", "implicit",
|
|
||||||
"password", "client_credentials", "refresh_token",
|
|
||||||
"urn:ietf:params:oauth:grant_type:redelegate"));
|
|
||||||
if (!requestedGrantTypes.isEmpty()) {
|
|
||||||
// return an error, there were unknown grant types requested
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Unknown grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
return "jsonErrorView";
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't allow "password" grant type for dynamic registration
|
|
||||||
if (newClient.getGrantTypes().contains("password")) {
|
|
||||||
// return an error, you can't dynamically register for the password grant
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "The password grant type is not allowed in dynamic registration on this server.");
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't allow clients to have multiple incompatible grant types and scopes
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code")) {
|
|
||||||
|
|
||||||
// check for incompatible grants
|
|
||||||
if (newClient.getGrantTypes().contains("implicit") ||
|
|
||||||
newClient.getGrantTypes().contains("client_credentials")) {
|
|
||||||
// return an error, you can't have these grant types together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getResponseTypes().contains("token")) {
|
|
||||||
// return an error, you can't have this grant type and response type together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible response types requested: " + newClient.getGrantTypes() + " / " + newClient.getResponseTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
newClient.getResponseTypes().add("code");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getGrantTypes().contains("implicit")) {
|
|
||||||
|
|
||||||
// check for incompatible grants
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code") ||
|
|
||||||
newClient.getGrantTypes().contains("client_credentials")) {
|
|
||||||
// return an error, you can't have these grant types together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getResponseTypes().contains("code")) {
|
|
||||||
// return an error, you can't have this grant type and response type together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible response types requested: " + newClient.getGrantTypes() + " / " + newClient.getResponseTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
newClient.getResponseTypes().add("token");
|
|
||||||
|
|
||||||
// don't allow refresh tokens in implicit clients
|
|
||||||
newClient.getGrantTypes().remove("refresh_token");
|
|
||||||
newClient.getScope().remove("offline_access");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getGrantTypes().contains("client_credentials")) {
|
|
||||||
|
|
||||||
// check for incompatible grants
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code") ||
|
|
||||||
newClient.getGrantTypes().contains("implicit")) {
|
|
||||||
// return an error, you can't have these grant types together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newClient.getResponseTypes().isEmpty()) {
|
|
||||||
// return an error, you can't have this grant type and response type together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible response types requested: " + newClient.getGrantTypes() + " / " + newClient.getResponseTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't allow refresh tokens or id tokens in client_credentials clients
|
|
||||||
newClient.getGrantTypes().remove("refresh_token");
|
|
||||||
newClient.getScope().remove("offline_access");
|
|
||||||
newClient.getScope().remove("openid");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getGrantTypes().isEmpty()) {
|
|
||||||
// return an error, you need at least one grant type selected
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Clients must register at least one grant type.");
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to make sure this client registered a redirect URI if using a redirect flow
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code") || newClient.getGrantTypes().contains("implicit")) {
|
|
||||||
if (newClient.getRedirectUris() == null || newClient.getRedirectUris().isEmpty()) {
|
|
||||||
// return an error
|
|
||||||
m.addAttribute("error", "invalid_client_uri");
|
|
||||||
m.addAttribute("errorMessage", "Clients using a redirect-based grant type must register at least one redirect URI.");
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String uri : newClient.getRedirectUris()) {
|
|
||||||
if (blacklistService.isBlacklisted(uri)) {
|
|
||||||
// return an error
|
|
||||||
m.addAttribute("error", "invalid_client_uri");
|
|
||||||
m.addAttribute("errorMessage", "Redirect URI is not allowed: " + uri);
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (newClient.getTokenEndpointAuthMethod() == null) {
|
if (newClient.getTokenEndpointAuthMethod() == null) {
|
||||||
newClient.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC);
|
newClient.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC);
|
||||||
}
|
}
|
||||||
|
@ -420,147 +272,21 @@ public class ClientDynamicRegistrationEndpoint {
|
||||||
newClient.setCreatedAt(oldClient.getCreatedAt());
|
newClient.setCreatedAt(oldClient.getCreatedAt());
|
||||||
newClient.setReuseRefreshToken(oldClient.isReuseRefreshToken());
|
newClient.setReuseRefreshToken(oldClient.isReuseRefreshToken());
|
||||||
|
|
||||||
// set of scopes that are OK for clients to dynamically register for
|
// do validation on the fields
|
||||||
Set<SystemScope> dynScopes = scopeService.getDynReg();
|
try {
|
||||||
|
newClient = validateScopes(newClient);
|
||||||
// scopes that the client is asking for
|
newClient = validateResponseTypes(newClient);
|
||||||
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
|
newClient = validateGrantTypes(newClient);
|
||||||
|
newClient = validateRedirectUris(newClient);
|
||||||
// the scopes that the client can have must be a subset of the dynamically allowed scopes
|
newClient = validateAuth(newClient);
|
||||||
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);
|
} catch (ValidationException ve) {
|
||||||
|
// validation failed, return an error
|
||||||
// if the client didn't ask for any, give them the defaults
|
m.addAttribute("error", ve.getError());
|
||||||
if (allowedScopes == null || allowedScopes.isEmpty()) {
|
m.addAttribute("errorMessage", ve.getErrorDescription());
|
||||||
allowedScopes = scopeService.getDefaults();
|
m.addAttribute("code", ve.getStatus());
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that the client doesn't ask for scopes it can't have
|
|
||||||
newClient.setScope(scopeService.toStrings(allowedScopes));
|
|
||||||
|
|
||||||
if (newClient.getResponseTypes() == null) {
|
|
||||||
newClient.setResponseTypes(new HashSet<String>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter out unknown grant types
|
|
||||||
// TODO: make this a pluggable service
|
|
||||||
Set<String> requestedGrantTypes = new HashSet<String>(newClient.getGrantTypes());
|
|
||||||
requestedGrantTypes.removeAll(
|
|
||||||
ImmutableSet.of("authorization_code", "implicit",
|
|
||||||
"password", "client_credentials", "refresh_token",
|
|
||||||
"urn:ietf:params:oauth:grant_type:redelegate"));
|
|
||||||
if (!requestedGrantTypes.isEmpty()) {
|
|
||||||
// return an error, there were unknown grant types requested
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Unknown grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
return "jsonErrorView";
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't allow "password" grant type for dynamic registration
|
|
||||||
if (newClient.getGrantTypes().contains("password")) {
|
|
||||||
// return an error, you can't dynamically register for the password grant
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "The password grant type is not allowed in dynamic registration on this server.");
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't allow clients to have multiple incompatible grant types and scopes
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code")) {
|
|
||||||
|
|
||||||
// check for incompatible grants
|
|
||||||
if (newClient.getGrantTypes().contains("implicit") ||
|
|
||||||
newClient.getGrantTypes().contains("client_credentials")) {
|
|
||||||
// return an error, you can't have these grant types together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
newClient.getResponseTypes().add("code");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getGrantTypes().contains("implicit")) {
|
|
||||||
|
|
||||||
// check for incompatible grants
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code") ||
|
|
||||||
newClient.getGrantTypes().contains("client_credentials")) {
|
|
||||||
// return an error, you can't have these grant types together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
newClient.getResponseTypes().add("token");
|
|
||||||
|
|
||||||
// don't allow refresh tokens in implicit clients
|
|
||||||
newClient.getGrantTypes().remove("refresh_token");
|
|
||||||
newClient.getScope().remove("offline_access");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getGrantTypes().contains("client_credentials")) {
|
|
||||||
|
|
||||||
// check for incompatible grants
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code") ||
|
|
||||||
newClient.getGrantTypes().contains("implicit")) {
|
|
||||||
// return an error, you can't have these grant types together
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Incompatible grant types requested: " + newClient.getGrantTypes());
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't allow refresh tokens or id tokens in client_credentials clients
|
|
||||||
newClient.getGrantTypes().remove("refresh_token");
|
|
||||||
newClient.getScope().remove("offline_access");
|
|
||||||
newClient.getScope().remove("openid");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getGrantTypes().isEmpty()) {
|
|
||||||
// return an error, you need at least one grant type selected
|
|
||||||
m.addAttribute("error", "invalid_client_metadata");
|
|
||||||
m.addAttribute("errorMessage", "Clients must register at least one grant type.");
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to make sure this client registered a redirect URI if using a redirect flow
|
|
||||||
if (newClient.getGrantTypes().contains("authorization_code") || newClient.getGrantTypes().contains("implicit")) {
|
|
||||||
if (newClient.getRedirectUris() == null || newClient.getRedirectUris().isEmpty()) {
|
|
||||||
// return an error
|
|
||||||
m.addAttribute("error", "invalid_client_uri");
|
|
||||||
m.addAttribute("errorMessage", "Clients using a redirect-based grant type must register at least one redirect URI.");
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String uri : newClient.getRedirectUris()) {
|
|
||||||
if (blacklistService.isBlacklisted(uri)) {
|
|
||||||
// return an error
|
|
||||||
m.addAttribute("error", "invalid_client_uri");
|
|
||||||
m.addAttribute("errorMessage", "Redirect URI is not allowed: " + uri);
|
|
||||||
m.addAttribute("code", HttpStatus.BAD_REQUEST);
|
|
||||||
return "jsonErrorView";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getTokenEndpointAuthMethod() == null) {
|
|
||||||
newClient.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_BASIC ||
|
|
||||||
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_JWT ||
|
|
||||||
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_POST) {
|
|
||||||
|
|
||||||
// we need to generate a secret
|
|
||||||
newClient = clientService.generateClientSecret(newClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// save the client
|
// save the client
|
||||||
ClientDetailsEntity savedClient = clientService.updateClient(oldClient, newClient);
|
ClientDetailsEntity savedClient = clientService.updateClient(oldClient, newClient);
|
||||||
|
@ -627,4 +353,198 @@ public class ClientDynamicRegistrationEndpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateScopes(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
// set of scopes that are OK for clients to dynamically register for
|
||||||
|
Set<SystemScope> dynScopes = scopeService.getDynReg();
|
||||||
|
|
||||||
|
// scopes that the client is asking for
|
||||||
|
Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());
|
||||||
|
|
||||||
|
// the scopes that the client can have must be a subset of the dynamically allowed scopes
|
||||||
|
Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);
|
||||||
|
|
||||||
|
// if the client didn't ask for any, give them the defaults
|
||||||
|
if (allowedScopes == null || allowedScopes.isEmpty()) {
|
||||||
|
allowedScopes = scopeService.getDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
newClient.setScope(scopeService.toStrings(allowedScopes));
|
||||||
|
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateResponseTypes(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
if (newClient.getResponseTypes() == null) {
|
||||||
|
newClient.setResponseTypes(new HashSet<String>());
|
||||||
|
}
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateGrantTypes(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
// set default grant types if needed
|
||||||
|
if (newClient.getGrantTypes() == null || newClient.getGrantTypes().isEmpty()) {
|
||||||
|
if (newClient.getScope().contains("offline_access")) { // client asked for offline access
|
||||||
|
newClient.setGrantTypes(Sets.newHashSet("authorization_code", "refresh_token")); // allow authorization code and refresh token grant types by default
|
||||||
|
} else {
|
||||||
|
newClient.setGrantTypes(Sets.newHashSet("authorization_code")); // allow authorization code grant type by default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter out unknown grant types
|
||||||
|
// TODO: make this a pluggable service
|
||||||
|
Set<String> requestedGrantTypes = new HashSet<String>(newClient.getGrantTypes());
|
||||||
|
requestedGrantTypes.removeAll(
|
||||||
|
ImmutableSet.of("authorization_code", "implicit",
|
||||||
|
"password", "client_credentials", "refresh_token",
|
||||||
|
"urn:ietf:params:oauth:grant_type:redelegate"));
|
||||||
|
if (!requestedGrantTypes.isEmpty()) {
|
||||||
|
// return an error, there were unknown grant types requested
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Unknown grant types requested: " + newClient.getGrantTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't allow "password" grant type for dynamic registration
|
||||||
|
if (newClient.getGrantTypes().contains("password")) {
|
||||||
|
// return an error, you can't dynamically register for the password grant
|
||||||
|
throw new ValidationException("invalid_client_metadata", "The password grant type is not allowed in dynamic registration on this server.", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't allow clients to have multiple incompatible grant types and scopes
|
||||||
|
if (newClient.getGrantTypes().contains("authorization_code")) {
|
||||||
|
|
||||||
|
// check for incompatible grants
|
||||||
|
if (newClient.getGrantTypes().contains("implicit") ||
|
||||||
|
newClient.getGrantTypes().contains("client_credentials")) {
|
||||||
|
// return an error, you can't have these grant types together
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Incompatible grant types requested: " + newClient.getGrantTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getResponseTypes().contains("token")) {
|
||||||
|
// return an error, you can't have this grant type and response type together
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Incompatible response types requested: " + newClient.getGrantTypes() + " / " + newClient.getResponseTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
newClient.getResponseTypes().add("code");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getGrantTypes().contains("implicit")) {
|
||||||
|
|
||||||
|
// check for incompatible grants
|
||||||
|
if (newClient.getGrantTypes().contains("authorization_code") ||
|
||||||
|
newClient.getGrantTypes().contains("client_credentials")) {
|
||||||
|
// return an error, you can't have these grant types together
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Incompatible grant types requested: " + newClient.getGrantTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getResponseTypes().contains("code")) {
|
||||||
|
// return an error, you can't have this grant type and response type together
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Incompatible response types requested: " + newClient.getGrantTypes() + " / " + newClient.getResponseTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
newClient.getResponseTypes().add("token");
|
||||||
|
|
||||||
|
// don't allow refresh tokens in implicit clients
|
||||||
|
newClient.getGrantTypes().remove("refresh_token");
|
||||||
|
newClient.getScope().remove("offline_access");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getGrantTypes().contains("client_credentials")) {
|
||||||
|
|
||||||
|
// check for incompatible grants
|
||||||
|
if (newClient.getGrantTypes().contains("authorization_code") ||
|
||||||
|
newClient.getGrantTypes().contains("implicit")) {
|
||||||
|
// return an error, you can't have these grant types together
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Incompatible grant types requested: " + newClient.getGrantTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newClient.getResponseTypes().isEmpty()) {
|
||||||
|
// return an error, you can't have this grant type and response type together
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Incompatible response types requested: " + newClient.getGrantTypes() + " / " + newClient.getResponseTypes(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't allow refresh tokens or id tokens in client_credentials clients
|
||||||
|
newClient.getGrantTypes().remove("refresh_token");
|
||||||
|
newClient.getScope().remove("offline_access");
|
||||||
|
newClient.getScope().remove("openid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getGrantTypes().isEmpty()) {
|
||||||
|
// return an error, you need at least one grant type selected
|
||||||
|
throw new ValidationException("invalid_client_metadata", "Clients must register at least one grant type.", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateRedirectUris(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
// check to make sure this client registered a redirect URI if using a redirect flow
|
||||||
|
if (newClient.getGrantTypes().contains("authorization_code") || newClient.getGrantTypes().contains("implicit")) {
|
||||||
|
if (newClient.getRedirectUris() == null || newClient.getRedirectUris().isEmpty()) {
|
||||||
|
// return an error
|
||||||
|
throw new ValidationException("invalid_client_uri", "Clients using a redirect-based grant type must register at least one redirect URI.", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String uri : newClient.getRedirectUris()) {
|
||||||
|
if (blacklistService.isBlacklisted(uri)) {
|
||||||
|
// return an error
|
||||||
|
throw new ValidationException("invalid_client_uri", "Redirect URI is not allowed: " + uri, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientDetailsEntity validateAuth(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
|
if (newClient.getTokenEndpointAuthMethod() == null) {
|
||||||
|
newClient.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_BASIC ||
|
||||||
|
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_JWT ||
|
||||||
|
newClient.getTokenEndpointAuthMethod() == AuthMethod.SECRET_POST) {
|
||||||
|
|
||||||
|
// we need to generate a secret
|
||||||
|
newClient = clientService.generateClientSecret(newClient);
|
||||||
|
}
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown by utility methods when a client fails to validate. Contains information
|
||||||
|
* to be returned.
|
||||||
|
* @author jricher
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private class ValidationException extends Exception {
|
||||||
|
private String error;
|
||||||
|
private String errorDescription;
|
||||||
|
private HttpStatus status;
|
||||||
|
public ValidationException(String error, String errorDescription,
|
||||||
|
HttpStatus status) {
|
||||||
|
this.error = error;
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
public void setError(String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
public String getErrorDescription() {
|
||||||
|
return errorDescription;
|
||||||
|
}
|
||||||
|
public void setErrorDescription(String errorDescription) {
|
||||||
|
this.errorDescription = errorDescription;
|
||||||
|
}
|
||||||
|
public HttpStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
public void setStatus(HttpStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue