use of oidc.exceptions.UnknownClientException in project OpenConext-oidcng by OpenConext.
the class IntrospectEndpoint method introspect.
@PostMapping(value = { "oidc/introspect" }, consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE })
public ResponseEntity<Map<String, Object>> introspect(HttpServletRequest request) throws ParseException, IOException, java.text.ParseException {
HTTPRequest httpRequest = ServletUtils.createHTTPRequest(request);
TokenIntrospectionRequest tokenIntrospectionRequest = TokenIntrospectionRequest.parse(httpRequest);
ClientAuthentication clientAuthentication = tokenIntrospectionRequest.getClientAuthentication();
String accessTokenValue = tokenIntrospectionRequest.getToken().getValue();
// https://tools.ietf.org/html/rfc7662 is vague about the authorization requirements, but we enforce basic auth
if (!(clientAuthentication instanceof PlainClientSecret)) {
LOG.warn("No authentication present");
throw new UnauthorizedException("Invalid user / secret");
}
String clientId = clientAuthentication.getClientID().getValue();
OpenIDClient resourceServer = openIDClientRepository.findOptionalByClientId(clientId).orElseThrow(() -> new UnknownClientException(clientId));
MDCContext.mdcContext("action", "Introspect", "rp", resourceServer.getClientId(), "accessTokenValue", accessTokenValue);
if (!secretsMatch((PlainClientSecret) clientAuthentication, resourceServer)) {
LOG.warn("Secret does not match for RS " + resourceServer.getClientId());
throw new UnauthorizedException("Invalid user / secret");
}
if (!resourceServer.isResourceServer()) {
LOG.warn("RS required for not configured for RP " + resourceServer.getClientId());
throw new UnauthorizedException("Requires ResourceServer");
}
Optional<SignedJWT> optionalSignedJWT = tokenGenerator.parseAndValidateSignedJWT(accessTokenValue);
if (!optionalSignedJWT.isPresent()) {
LOG.warn("Invalid access_token " + accessTokenValue);
return ResponseEntity.ok(Collections.singletonMap("active", false));
}
SignedJWT signedJWT = optionalSignedJWT.get();
String jwtId = signedJWT.getJWTClaimsSet().getJWTID();
Optional<AccessToken> optionalAccessToken = accessTokenRepository.findByJwtId(jwtId);
if (!optionalAccessToken.isPresent()) {
LOG.warn("No access_token found " + accessTokenValue);
return ResponseEntity.ok(Collections.singletonMap("active", false));
}
AccessToken accessToken = optionalAccessToken.get();
if (accessToken.isExpired(Clock.systemDefaultZone())) {
LOG.warn("Access token is expired " + accessTokenValue);
return ResponseEntity.ok(Collections.singletonMap("active", false));
}
List<String> scopes = accessToken.getScopes();
Map<String, Object> result = new TreeMap<>();
boolean isUserAccessToken = !accessToken.isClientCredentials();
if (isUserAccessToken) {
OpenIDClient openIDClient = openIDClientRepository.findOptionalByClientId(accessToken.getClientId()).orElseThrow(() -> new UnknownClientException(accessToken.getClientId()));
if (!openIDClient.getClientId().equals(resourceServer.getClientId()) && !openIDClient.getAllowedResourceServers().contains(resourceServer.getClientId())) {
throw new UnauthorizedException(String.format("RP %s is not allowed to use the API of resource server %s. Allowed resource servers are %s", accessToken.getClientId(), resourceServer.getClientId(), openIDClient.getAllowedResourceServers()));
}
User user = tokenGenerator.decryptAccessTokenWithEmbeddedUserInfo(signedJWT);
result.put("updated_at", user.getUpdatedAt());
if (resourceServer.isIncludeUnspecifiedNameID()) {
result.put("unspecified_id", user.getUnspecifiedNameId());
}
result.put("authenticating_authority", user.getAuthenticatingAuthority());
result.put("sub", user.getSub());
result.putAll(user.getAttributes());
List<String> acrClaims = user.getAcrClaims();
if (!CollectionUtils.isEmpty(acrClaims)) {
result.put("acr", String.join(" ", acrClaims));
}
boolean validPseudonymisation = validPseudonymisation(result, resourceServer, openIDClient);
if (!validPseudonymisation && enforceEduidResourceServerLinkedAccount) {
LOG.warn(String.format("Pseudonymisation failed. No eduperson_principal_name for RS %s", resourceServer.getClientId()));
return ResponseEntity.ok(Collections.singletonMap("active", false));
}
}
// The following claims can not be overridden by the
result.put("active", true);
result.put("scope", String.join(" ", scopes));
result.put("client_id", accessToken.getClientId());
result.put("exp", accessToken.getExpiresIn().getTime() / 1000L);
result.put("sub", accessToken.getSub());
result.put("iss", issuer);
result.put("token_type", "Bearer");
LOG.debug(String.format("Returning introspect active %s for RS %s", true, resourceServer.getClientId()));
return ResponseEntity.ok(result);
}
use of oidc.exceptions.UnknownClientException in project OpenConext-oidcng by OpenConext.
the class TokenEndpoint method token.
@PostMapping(value = "oidc/token", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE })
public ResponseEntity token(HttpServletRequest request) throws IOException, ParseException, JOSEException, java.text.ParseException, CertificateException, BadJOSEException {
HTTPRequest httpRequest = ServletUtils.createHTTPRequest(request);
TokenRequest tokenRequest = TokenRequest.parse(httpRequest);
ClientAuthentication clientAuthentication = tokenRequest.getClientAuthentication();
if (clientAuthentication != null && !(clientAuthentication instanceof PlainClientSecret || clientAuthentication instanceof JWTAuthentication)) {
throw new IllegalArgumentException(String.format("Unsupported '%s' findByClientId authentication in token endpoint", clientAuthentication.getClass()));
}
AuthorizationGrant authorizationGrant = tokenRequest.getAuthorizationGrant();
if (clientAuthentication == null && authorizationGrant instanceof AuthorizationCodeGrant && ((AuthorizationCodeGrant) authorizationGrant).getCodeVerifier() == null) {
throw new CodeVerifierMissingException("code_verifier required without client authentication");
}
String clientId = clientAuthentication != null ? clientAuthentication.getClientID().getValue() : tokenRequest.getClientID().getValue();
OpenIDClient client = openIDClientRepository.findOptionalByClientId(clientId).orElseThrow(() -> new UnknownClientException(clientId));
if (clientAuthentication == null && !client.isPublicClient()) {
throw new UnauthorizedException("Non-public client requires authentication");
}
if (clientAuthentication != null) {
if (clientAuthentication instanceof PlainClientSecret && !secretsMatch((PlainClientSecret) clientAuthentication, client)) {
throw new UnauthorizedException("Invalid user / secret");
} else if (clientAuthentication instanceof JWTAuthentication && !verifySignature((JWTAuthentication) clientAuthentication, client, this.tokenEndpoint)) {
throw new UnauthorizedException("Invalid user / signature");
}
}
MDCContext.mdcContext("action", "Token", "rp", clientId, "grant", authorizationGrant.getType().getValue());
if (!client.getGrants().contains(authorizationGrant.getType().getValue())) {
throw new InvalidGrantException("Invalid grant: " + authorizationGrant.getType().getValue());
}
if (authorizationGrant instanceof AuthorizationCodeGrant) {
return handleAuthorizationCodeGrant((AuthorizationCodeGrant) authorizationGrant, client);
} else if (authorizationGrant instanceof ClientCredentialsGrant) {
return handleClientCredentialsGrant(client, tokenRequest);
} else if (authorizationGrant instanceof RefreshTokenGrant) {
return handleRefreshCodeGrant((RefreshTokenGrant) authorizationGrant, client);
}
throw new IllegalArgumentException("Not supported - yet - authorizationGrant " + authorizationGrant.getType().getValue());
}
use of oidc.exceptions.UnknownClientException in project OpenConext-oidcng by OpenConext.
the class AuthorizationEndpoint method doAuthorization.
private ModelAndView doAuthorization(MultiValueMap<String, String> parameters, OidcSamlAuthentication samlAuthentication, HttpServletRequest request, boolean consentRequired) throws ParseException, CertificateException, JOSEException, IOException, BadJOSEException, java.text.ParseException, URISyntaxException {
AuthorizationRequest authenticationRequest = AuthorizationRequest.parse(parameters);
Scope scope = authenticationRequest.getScope();
boolean isOpenIdClient = scope != null && isOpenIDRequest(scope.toStringList());
String clientId = authenticationRequest.getClientID().getValue();
OpenIDClient client = openIDClientRepository.findOptionalByClientId(clientId).orElseThrow(() -> new UnknownClientException(clientId));
MDCContext.mdcContext("action", "Authorize", "rp", client.getClientId());
if (isOpenIdClient) {
AuthenticationRequest oidcAuthenticationRequest = AuthenticationRequest.parse(parameters);
if (oidcAuthenticationRequest.specifiesRequestObject()) {
oidcAuthenticationRequest = JWTRequest.parse(oidcAuthenticationRequest, client);
LOG.debug("/oidc/authorize with JWT 'request'");
}
// swap reference
authenticationRequest = oidcAuthenticationRequest;
}
State state = authenticationRequest.getState();
String redirectURI = validateRedirectionURI(authenticationRequest.getRedirectionURI(), client).getRedirectURI();
List<String> scopes = validateScopes(openIDClientRepository, authenticationRequest.getScope(), client);
ResponseType responseType = validateGrantType(authenticationRequest, client);
User user = samlAuthentication.getUser();
MDCContext.mdcContext(user);
if (scope != null) {
List<String> scopeList = scope.toStringList();
boolean apiScopeRequested = !(scopeList.size() == 0 || (scopeList.size() == 1 && scopeList.contains("openid")));
Set<String> filteredScopes = scopeList.stream().filter(s -> !s.equalsIgnoreCase("openid")).map(String::toLowerCase).collect(toSet());
List<OpenIDClient> resourceServers = openIDClientRepository.findByScopes_NameIn(filteredScopes);
Prompt prompt = authenticationRequest.getPrompt();
boolean consentFromPrompt = prompt != null && prompt.toStringList().contains("consent");
/*
* We prompt for consent when the following conditions are met:
* Consent feature toggle is on
* The RP has requested scope(s) other then openid
* Manage attribute "oidc:consentRequired" is true for the RP or the RP has explicitly asked for consent
* There is at least one ResourceServer that has the requested scope(s) configured in manage
*/
if (consentRequired && apiScopeRequested && (consentFromPrompt || client.isConsentRequired()) && resourceServers.size() > 0) {
LOG.info("Asking for consent for User " + user + " and scopes " + scopes);
return doConsent(parameters, client, filteredScopes, resourceServers);
}
}
// We do not provide SSO as does EB not - up to the identity provider
logout(request);
ResponseMode responseMode = authenticationRequest.impliedResponseMode();
if (responseType.impliesCodeFlow()) {
AuthorizationCode authorizationCode = createAndSaveAuthorizationCode(authenticationRequest, client, user);
LOG.debug(String.format("Returning authorizationCode flow %s %s", ResponseMode.FORM_POST, redirectURI));
if (responseMode.equals(ResponseMode.FORM_POST)) {
Map<String, String> body = new HashMap<>();
body.put("redirect_uri", redirectURI);
body.put("code", authorizationCode.getCode());
if (state != null && StringUtils.hasText(state.getValue())) {
body.put("state", state.getValue());
}
return new ModelAndView("form_post", body);
}
return new ModelAndView(new RedirectView(authorizationRedirect(redirectURI, state, authorizationCode.getCode(), responseMode.equals(ResponseMode.FRAGMENT))));
} else if (responseType.impliesImplicitFlow() || responseType.impliesHybridFlow()) {
if (responseType.impliesImplicitFlow()) {
// User information is encrypted in access token
LOG.debug("Deleting user " + user.getSub());
userRepository.delete(user);
}
Map<String, Object> body = authorizationEndpointResponse(user, client, authenticationRequest, scopes, responseType, state);
LOG.debug(String.format("Returning implicit flow %s %s", ResponseMode.FORM_POST, redirectURI));
if (responseMode.equals(ResponseMode.FORM_POST)) {
body.put("redirect_uri", redirectURI);
return new ModelAndView("form_post", body);
}
if (responseMode.equals(ResponseMode.QUERY)) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(redirectURI);
body.forEach(builder::queryParam);
return new ModelAndView(new RedirectView(builder.toUriString()));
}
if (responseMode.equals(ResponseMode.FRAGMENT)) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(redirectURI);
String fragment = body.entrySet().stream().map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue())).collect(Collectors.joining("&"));
builder.fragment(fragment);
return new ModelAndView(new RedirectView(builder.toUriString()));
}
throw new IllegalArgumentException("Response mode " + responseMode + " not supported");
}
throw new IllegalArgumentException("Not yet implemented response_type: " + responseType.toString());
}
use of oidc.exceptions.UnknownClientException in project OpenConext-oidcng by OpenConext.
the class RedirectAuthenticationFailureHandler method onAuthenticationFailure.
@SneakyThrows
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
HttpSession session = request.getSession(false);
SavedRequest savedRequest = null;
if (session != null) {
savedRequest = (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
}
if (savedRequest == null) {
savedRequest = requestCache.getRequest(request, response);
}
if (savedRequest != null) {
Map<String, String[]> parameterMap = savedRequest.getParameterMap();
Map<String, List<String>> parameters = parameterMap.keySet().stream().collect(Collectors.toMap(key -> key, key -> Arrays.asList(parameterMap.get(key))));
List<String> redirectUris = parameters.get("redirect_uri");
URI redirectURI = CollectionUtils.isEmpty(redirectUris) ? null : new URI(redirectUris.get(0));
List<String> clientIds = parameters.get("client_id");
String clientId = CollectionUtils.isEmpty(clientIds) ? null : clientIds.get(0);
OpenIDClient openIDClient = openIDClientRepository.findOptionalByClientId(clientId).orElseThrow(() -> new UnknownClientException(clientId));
AuthorizationEndpoint.validateRedirectionURI(redirectURI, openIDClient);
request.setAttribute(REDIRECT_URI_VALID, true);
} else if (exception instanceof ContextSaml2AuthenticationException) {
request.setAttribute(REDIRECT_URI_VALID, true);
throw exception;
}
/*
* Will be picked up by the ErrorController. Do note that if the user has stepped up his account in eduID, then
* the initial session is no longer around.
*/
if (exception instanceof Saml2AuthenticationException) {
throw new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, "The requesting service has indicated that the authenticated user is required to have validated attributes. Your institution has not provided this."), "The requesting service has indicated that the authenticated user is required to have validated attributes. Your institution has not provided this.", exception);
}
throw exception;
}
use of oidc.exceptions.UnknownClientException in project OpenConext-oidcng by OpenConext.
the class AuthnRequestConverter method convert.
@SneakyThrows
@Override
public AuthnRequest convert(Saml2AuthenticationRequestContext ctx) {
CustomSaml2AuthenticationRequestContext context = (CustomSaml2AuthenticationRequestContext) ctx;
HttpServletRequest request = context.getRequest();
HttpSession session = request.getSession(false);
if (session == null) {
LOG.warn("There is no session in the HttpServletRequest. CookiesNotSupportedException will be thrown");
} else {
Enumeration<String> attributeNames = session.getAttributeNames();
List<String> list = Collections.list(attributeNames);
if (!list.contains("SPRING_SECURITY_SAVED_REQUEST")) {
LOG.info("There is a session in the HttpServletRequest with ID " + session.getId() + " which does not contain a saved request. Attribute names are: " + list.toString());
}
}
SavedRequest savedRequest = requestCache.getRequest(request, null);
if (savedRequest == null) {
throw new CookiesNotSupportedException();
}
Map<String, String[]> parameterMap = savedRequest.getParameterMap();
Map<String, List<String>> parameters = parameterMap.keySet().stream().collect(Collectors.toMap(key -> key, key -> Arrays.asList(parameterMap.get(key))));
List<String> redirectUris = parameters.get("redirect_uri");
URI redirectURI = CollectionUtils.isEmpty(redirectUris) ? null : new URI(redirectUris.get(0));
List<String> clientIds = parameters.get("client_id");
String clientId = CollectionUtils.isEmpty(clientIds) ? null : clientIds.get(0);
OpenIDClient openIDClient = openIDClientRepository.findOptionalByClientId(clientId).orElseThrow(() -> new UnknownClientException(clientId));
AuthorizationEndpoint.validateRedirectionURI(redirectURI, openIDClient);
request.setAttribute(REDIRECT_URI_VALID, true);
AuthorizationRequest authorizationRequest = AuthorizationRequest.parse(parameters);
validateAuthorizationRequest(authorizationRequest, openIDClient);
RelyingPartyRegistration relyingParty = context.getRelyingPartyRegistration();
AuthnRequestBuilder authnRequestBuilder = (AuthnRequestBuilder) registry.getBuilderFactory().getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
AuthnRequest authnRequest = authnRequestBuilder.buildObject();
authnRequest.setID("ARQ" + UUID.randomUUID().toString().substring(1));
authnRequest.setIssueInstant(Instant.now());
authnRequest.setProtocolBinding(POST.getUrn());
IssuerBuilder issuerBuilder = (IssuerBuilder) registry.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
Issuer issuer = issuerBuilder.buildObject();
issuer.setValue(relyingParty.getEntityId());
authnRequest.setIssuer(issuer);
authnRequest.setDestination(context.getDestination());
authnRequest.setAssertionConsumerServiceURL(context.getAssertionConsumerServiceUrl());
saveAuthenticationRequestUrl(savedRequest, authnRequest, authorizationRequest.getClientID());
enhanceAuthenticationRequest(authnRequest, parameters);
return authnRequest;
}
Aggregations