Search in sources :

Example 1 with User

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);
}
Also used : HTTPRequest(com.nimbusds.oauth2.sdk.http.HTTPRequest) User(oidc.model.User) UnknownClientException(oidc.exceptions.UnknownClientException) OpenIDClient(oidc.model.OpenIDClient) TokenIntrospectionRequest(com.nimbusds.oauth2.sdk.TokenIntrospectionRequest) SignedJWT(com.nimbusds.jwt.SignedJWT) TreeMap(java.util.TreeMap) PlainClientSecret(com.nimbusds.oauth2.sdk.auth.PlainClientSecret) AccessToken(oidc.model.AccessToken) UnauthorizedException(oidc.exceptions.UnauthorizedException) ClientAuthentication(com.nimbusds.oauth2.sdk.auth.ClientAuthentication) PostMapping(org.springframework.web.bind.annotation.PostMapping)

Example 2 with User

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);
}
Also used : AuthorizationCode(oidc.model.AuthorizationCode) User(oidc.model.User) CodeChallengeMethod(com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod) CodeVerifierMissingException(oidc.exceptions.CodeVerifierMissingException) TokenAlreadyUsedException(oidc.exceptions.TokenAlreadyUsedException) CodeVerifier(com.nimbusds.oauth2.sdk.pkce.CodeVerifier) ResponseEntity(org.springframework.http.ResponseEntity) UnauthorizedException(oidc.exceptions.UnauthorizedException) RedirectMismatchException(oidc.exceptions.RedirectMismatchException) CodeChallenge(com.nimbusds.oauth2.sdk.pkce.CodeChallenge)

Example 3 with User

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()));
}
Also used : AuthorizationCode(oidc.model.AuthorizationCode) User(oidc.model.User) Query(org.springframework.data.mongodb.core.query.Query) AuthenticationRequest(oidc.model.AuthenticationRequest) URI(java.net.URI) Date(java.util.Date) AbstractIntegrationTest(oidc.AbstractIntegrationTest) Test(org.junit.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest)

Example 4 with User

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;
}
Also used : User(oidc.model.User) OpenIDClient(oidc.model.OpenIDClient) UserConsent(oidc.model.UserConsent) Date(java.util.Date)

Example 5 with User

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;
}
Also used : User(oidc.model.User)

Aggregations

User (oidc.model.User)17 SignedJWT (com.nimbusds.jwt.SignedJWT)7 OpenIDClient (oidc.model.OpenIDClient)7 AuthorizationCode (oidc.model.AuthorizationCode)5 Test (org.junit.Test)5 AbstractIntegrationTest (oidc.AbstractIntegrationTest)4 UnauthorizedException (oidc.exceptions.UnauthorizedException)4 AccessToken (oidc.model.AccessToken)4 EncryptedTokenValue (oidc.model.EncryptedTokenValue)4 OidcSamlAuthentication (oidc.user.OidcSamlAuthentication)4 HTTPRequest (com.nimbusds.oauth2.sdk.http.HTTPRequest)3 Date (java.util.Date)3 UnknownClientException (oidc.exceptions.UnknownClientException)3 AuthenticationRequest (oidc.model.AuthenticationRequest)3 TokenValue (oidc.model.TokenValue)3 JWTClaimsSet (com.nimbusds.jwt.JWTClaimsSet)2 ClientAuthentication (com.nimbusds.oauth2.sdk.auth.ClientAuthentication)2 PlainClientSecret (com.nimbusds.oauth2.sdk.auth.PlainClientSecret)2 LinkedHashMap (java.util.LinkedHashMap)2 Map (java.util.Map)2