use of oidc.model.User 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.model.User in project OpenConext-oidcng by OpenConext.
the class TokenEndpoint method handleAuthorizationCodeGrant.
private ResponseEntity handleAuthorizationCodeGrant(AuthorizationCodeGrant authorizationCodeGrant, OpenIDClient client) {
String code = authorizationCodeGrant.getAuthorizationCode().getValue();
MDCContext.mdcContext("code", "code");
AuthorizationCode authorizationCode = concurrentAuthorizationCodeRepository.findByCodeNotAlreadyUsedAndMarkAsUsed(code);
if (authorizationCode == null) {
/*
* Now it become's tricky. Did we get an 'null' because the code was bogus or because it was already
* used? To both satisfy the - highly theoretical - risk of the audit race condition and the OIDC certification
* demand of deleting access_token issued with the re-used authorization code we need to query again.
*
* If they code was bogus this will result in a 404 exception by the authorizationCodeRepository#findByCode
* and if we find something then we know there was a re-use issue.
*/
AuthorizationCode byCode = authorizationCodeRepository.findByCode(code);
accessTokenRepository.deleteByAuthorizationCodeId(byCode.getId());
throw new TokenAlreadyUsedException("Authorization code already used");
}
if (!authorizationCode.getClientId().equals(client.getClientId())) {
throw new UnauthorizedException("Client is not authorized for the authorization code");
}
if (authorizationCodeGrant.getRedirectionURI() != null && !authorizationCodeGrant.getRedirectionURI().toString().equals(authorizationCode.getRedirectUri())) {
throw new RedirectMismatchException("Redirects do not match");
}
if (authorizationCode.isRedirectURIProvided() && authorizationCodeGrant.getRedirectionURI() == null) {
throw new RedirectMismatchException("Redirect URI is mandatory if specified in code request");
}
if (authorizationCode.isExpired(Clock.systemDefaultZone())) {
throw new UnauthorizedException("Authorization code expired");
}
CodeVerifier codeVerifier = authorizationCodeGrant.getCodeVerifier();
String codeChallenge = authorizationCode.getCodeChallenge();
if (codeVerifier != null) {
if (codeChallenge == null) {
throw new CodeVerifierMissingException("code_verifier present, but no code_challenge in the authorization_code");
}
CodeChallengeMethod codeChallengeMethod = CodeChallengeMethod.parse(authorizationCode.getCodeChallengeMethod());
CodeChallenge computed = CodeChallenge.compute(codeChallengeMethod, codeVerifier);
// Constant time comparison
if (!MessageDigest.isEqual(codeChallenge.getBytes(), computed.getValue().getBytes())) {
LOG.error(String.format("CodeVerifier %s with method %s does not match codeChallenge %s. Expected codeChallenge is %s", codeVerifier.getValue(), codeChallengeMethod, codeChallenge, computed.getValue()));
throw new CodeVerifierMissingException("code_verifier does not match code_challenge");
}
}
User user = userRepository.findUserBySub(authorizationCode.getSub());
MDCContext.mdcContext(user);
// User information is encrypted in access token
LOG.debug("Deleting user " + user.getSub());
userRepository.delete(user);
Map<String, Object> body = tokenEndpointResponse(Optional.of(user), client, authorizationCode.getScopes(), authorizationCode.getIdTokenClaims(), false, authorizationCode.getNonce(), Optional.of(authorizationCode.getAuthTime()), Optional.of(authorizationCode.getId()));
return new ResponseEntity<>(body, responseHttpHeaders, HttpStatus.OK);
}
use of oidc.model.User in project OpenConext-oidcng by OpenConext.
the class ResourceCleanerTest method clean.
@Test
public void clean() throws URISyntaxException {
Class[] classes = { User.class, UserConsent.class, AccessToken.class, RefreshToken.class, AuthorizationCode.class, AuthenticationRequest.class };
Stream.of(classes).forEach(clazz -> mongoTemplate.remove(new Query(), clazz));
Date expiresIn = Date.from(LocalDateTime.now().minusDays(1).atZone(ZoneId.systemDefault()).toInstant());
Stream.of(accessToken("value", expiresIn), refreshToken(expiresIn), new AuthorizationCode("code", "sub", "clientId", emptyList(), new URI("http://redirectURI"), "codeChallenge", "codeChallengeMethod", "nonce", emptyList(), true, expiresIn), new User("nope", "unspecifiedNameId", "authenticatingAuthority", "clientId", Collections.emptyMap(), Collections.emptyList()), new AuthenticationRequest(UUID.randomUUID().toString(), expiresIn, "clientID", "http://localhost/authorize"), userConsent()).forEach(o -> mongoTemplate.insert(o));
subject.clean();
Stream.of(classes).forEach(clazz -> assertEquals(0, mongoTemplate.findAll(clazz).size()));
}
use of oidc.model.User in project OpenConext-oidcng by OpenConext.
the class ResourceCleanerTest method userConsent.
private UserConsent userConsent() {
UserConsent userConsent = new UserConsent(new User("sub", "unspecifiedNameId", "http://mockidp", "clientId", Collections.emptyMap(), Collections.emptyList()), Arrays.asList("openid", "profile"), new OpenIDClient());
Date lastAccessed = Date.from(new Date().toInstant().minus(365 * 10, ChronoUnit.DAYS).atZone(ZoneId.systemDefault()).toInstant());
ReflectionTestUtils.setField(userConsent, "lastAccessed", lastAccessed);
return userConsent;
}
use of oidc.model.User in project OpenConext-oidcng by OpenConext.
the class TokenEndpointTest method user.
private User user(String issuer) {
User user = new User();
ReflectionTestUtils.setField(user, "sub", issuer);
return user;
}
Aggregations