Search in sources :

Example 1 with ClientAuthorization

use of com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization in project oauth2-server by gw2auth.

the class OAuth2TokenCustomizerService method customize.

private void customize(JwtEncodingContext ctx, String clientAuthorizationId, long accountId, long clientRegistrationId) {
    final ClientAuthorization clientAuthorization = this.clientAuthorizationService.getClientAuthorization(accountId, clientAuthorizationId).orElse(null);
    final ClientConsent clientConsent = this.clientConsentService.getClientConsent(accountId, clientRegistrationId).orElse(null);
    if (clientAuthorization == null || clientConsent == null) {
        throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED));
    }
    try (ClientConsentService.LoggingContext logging = this.clientConsentService.log(accountId, clientRegistrationId, ClientConsentService.LogType.ACCESS_TOKEN)) {
        final Set<String> effectiveAuthorizedScopes = new HashSet<>(clientConsent.authorizedScopes());
        effectiveAuthorizedScopes.retainAll(clientAuthorization.authorizedScopes());
        final Set<UUID> authorizedGw2AccountIds = clientAuthorization.gw2AccountIds();
        final Set<Gw2ApiPermission> authorizedGw2ApiPermissions = effectiveAuthorizedScopes.stream().flatMap((scope) -> Gw2ApiPermission.fromOAuth2(scope).stream()).collect(Collectors.toSet());
        if (authorizedGw2ApiPermissions.isEmpty() || authorizedGw2AccountIds.isEmpty()) {
            logging.log("The Consent has been removed: responding with ACCESS_DENIED");
            throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED));
        }
        final List<ApiToken> authorizedRootTokens = this.apiTokenService.getApiTokens(accountId, authorizedGw2AccountIds);
        // in theory, this should not happen since authorized-tokens and root-tokens are related via foreign key
        if (authorizedRootTokens.isEmpty()) {
            logging.log("All linked Root-API-Tokens have been removed: responding with ACCESS_DENIED");
            throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED));
        }
        final Set<UUID> verifiedGw2AccountIds;
        final boolean hasGw2AuthVerifiedScope = effectiveAuthorizedScopes.contains(ClientConsentService.GW2AUTH_VERIFIED_SCOPE);
        if (hasGw2AuthVerifiedScope) {
            verifiedGw2AccountIds = this.verificationService.getVerifiedGw2AccountIds(accountId);
        } else {
            verifiedGw2AccountIds = Set.of();
        }
        final int gw2ApiPermissionsBitSet = Gw2ApiPermission.toBitSet(authorizedGw2ApiPermissions);
        final List<ApiSubTokenEntity> savedSubTokens = this.apiSubTokenRepository.findAllByAccountIdGw2AccountIdsAndGw2ApiPermissionsBitSet(accountId, authorizedGw2AccountIds, gw2ApiPermissionsBitSet);
        final Instant atLeastValidUntil = this.clock.instant().plus(AUTHORIZED_TOKEN_MIN_EXCESS_TIME);
        final Map<UUID, ApiSubTokenEntity> savedSubTokenByGw2AccountId = new HashMap<>(savedSubTokens.size());
        final Map<Instant, Integer> savedSubTokenCountByExpirationTime = new HashMap<>(savedSubTokens.size());
        Instant expirationTimeWithMostSavedSubTokens = null;
        for (ApiSubTokenEntity savedSubToken : savedSubTokens) {
            if (savedSubToken.expirationTime().isAfter(atLeastValidUntil)) {
                savedSubTokenByGw2AccountId.put(savedSubToken.gw2AccountId(), savedSubToken);
                final int groupCount = savedSubTokenCountByExpirationTime.merge(savedSubToken.expirationTime(), 1, Integer::sum);
                if (expirationTimeWithMostSavedSubTokens == null || groupCount > savedSubTokenCountByExpirationTime.get(expirationTimeWithMostSavedSubTokens)) {
                    expirationTimeWithMostSavedSubTokens = savedSubToken.expirationTime();
                }
            }
        }
        final Instant expirationTime;
        if (expirationTimeWithMostSavedSubTokens != null) {
            // if existing subtokens which are still valid for at least AUTHORIZED_TOKEN_MIN_EXCESS_TIME could be found, use this expiration time
            ctx.getClaims().expiresAt(expirationTimeWithMostSavedSubTokens);
            expirationTime = expirationTimeWithMostSavedSubTokens;
        } else {
            expirationTime = ctx.getClaims().build().getExpiresAt();
        }
        final Map<UUID, Map<String, Object>> tokensForJWT = new LinkedHashMap<>(authorizedGw2AccountIds.size());
        final Batch.Builder<Map<UUID, Pair<ApiToken, Gw2SubToken>>> batch = Batch.builder();
        for (ApiToken authorizedRootToken : authorizedRootTokens) {
            final Map<String, Object> tokenForJWT = new HashMap<>(3);
            final UUID gw2AccountId = authorizedRootToken.gw2AccountId();
            final String displayName = authorizedRootToken.displayName();
            final ApiSubTokenEntity potentialExistingSubToken = savedSubTokenByGw2AccountId.get(gw2AccountId);
            tokenForJWT.put("name", displayName);
            if (potentialExistingSubToken != null && potentialExistingSubToken.expirationTime().equals(expirationTime)) {
                tokenForJWT.put("token", potentialExistingSubToken.gw2ApiSubtoken());
                logging.log("Using existing and valid Subtoken for the Root-API-Token named '%s'", displayName);
            } else {
                if (authorizedRootToken.gw2ApiPermissions().containsAll(authorizedGw2ApiPermissions)) {
                    final String gw2ApiToken = authorizedRootToken.gw2ApiToken();
                    batch.add((timeout) -> this.gw2APIService.withTimeout(timeout, () -> this.gw2APIService.createSubToken(gw2ApiToken, authorizedGw2ApiPermissions, expirationTime)), (accumulator, context) -> {
                        try {
                            accumulator.put(gw2AccountId, new Pair<>(authorizedRootToken, context.get()));
                        } catch (ExecutionException | TimeoutException e) {
                            accumulator.put(gw2AccountId, new Pair<>(authorizedRootToken, null));
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            accumulator.put(gw2AccountId, new Pair<>(authorizedRootToken, null));
                        }
                        return accumulator;
                    });
                } else {
                    logging.log("The Root-API-Token named '%s' has less permissions than the authorization", displayName);
                }
            }
            if (hasGw2AuthVerifiedScope) {
                final boolean isVerified = verifiedGw2AccountIds.contains(gw2AccountId);
                tokenForJWT.put("verified", isVerified);
                logging.log("Including verified=%s for the Root-API-Token named '%s'", isVerified, displayName);
            }
            tokensForJWT.put(gw2AccountId, tokenForJWT);
        }
        final Map<UUID, Pair<ApiToken, Gw2SubToken>> result = batch.build().execute(this.gw2ApiClientExecutorService, HashMap::new, 10L, TimeUnit.SECONDS);
        final List<ApiTokenValidityUpdate> apiTokenValidityUpdates = new ArrayList<>(result.size());
        final List<ApiSubTokenEntity> apiSubTokenEntitiesToSave = new ArrayList<>(result.size());
        for (Map.Entry<UUID, Pair<ApiToken, Gw2SubToken>> entry : result.entrySet()) {
            final UUID gw2AccountId = entry.getKey();
            final Map<String, Object> tokenForJWT = tokensForJWT.get(gw2AccountId);
            final String displayName = entry.getValue().v1().displayName();
            final Gw2SubToken gw2SubToken = entry.getValue().v2();
            if (gw2SubToken != null) {
                if (gw2SubToken.permissions().equals(authorizedGw2ApiPermissions)) {
                    apiSubTokenEntitiesToSave.add(new ApiSubTokenEntity(accountId, gw2AccountId, gw2ApiPermissionsBitSet, gw2SubToken.value(), expirationTime));
                    tokenForJWT.put("token", gw2SubToken.value());
                    logging.log("Added Subtoken for the Root-API-Token named '%s'", displayName);
                } else {
                    tokenForJWT.put("error", "Failed to obtain new subtoken");
                    logging.log("The retrieved Subtoken for the Root-API-Token named '%s' appears to have less permissions than the authorization", displayName);
                }
                apiTokenValidityUpdates.add(new ApiTokenValidityUpdate(accountId, gw2AccountId, true));
            } else {
                tokenForJWT.put("error", "Failed to obtain new subtoken");
                logging.log("Failed to retrieve a new Subtoken for the Root-API-Token named '%s' from the GW2-API", displayName);
            }
        }
        this.apiTokenService.updateApiTokensValid(this.clock.instant(), apiTokenValidityUpdates);
        this.apiSubTokenRepository.saveAll(apiSubTokenEntitiesToSave);
        customize(ctx, clientConsent.accountSub(), authorizedGw2ApiPermissions, tokensForJWT);
    }
}
Also used : ApiSubTokenRepository(com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenRepository) java.util(java.util) JwtEncodingContext(org.springframework.security.oauth2.server.authorization.JwtEncodingContext) Autowired(org.springframework.beans.factory.annotation.Autowired) TimeoutException(java.util.concurrent.TimeoutException) Batch(com.gw2auth.oauth2.server.util.Batch) Service(org.springframework.stereotype.Service) ApiTokenValidityUpdate(com.gw2auth.oauth2.server.service.apitoken.ApiTokenValidityUpdate) Duration(java.time.Duration) Qualifier(org.springframework.beans.factory.annotation.Qualifier) Pair(com.gw2auth.oauth2.server.util.Pair) ExecutorService(java.util.concurrent.ExecutorService) OAuth2Authorization(org.springframework.security.oauth2.server.authorization.OAuth2Authorization) Gw2AuthUser(com.gw2auth.oauth2.server.service.user.Gw2AuthUser) Gw2SubToken(com.gw2auth.oauth2.server.service.gw2.Gw2SubToken) ClientConsent(com.gw2auth.oauth2.server.service.client.consent.ClientConsent) RegisteredClient(org.springframework.security.oauth2.server.authorization.client.RegisteredClient) ApiTokenService(com.gw2auth.oauth2.server.service.apitoken.ApiTokenService) ClientAuthorizationService(com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorizationService) OAuth2AuthenticationException(org.springframework.security.oauth2.core.OAuth2AuthenticationException) OAuth2AuthenticationToken(org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken) OAuth2ErrorCodes(org.springframework.security.oauth2.core.OAuth2ErrorCodes) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) Gw2ApiService(com.gw2auth.oauth2.server.service.gw2.Gw2ApiService) VerificationService(com.gw2auth.oauth2.server.service.verification.VerificationService) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) ClientConsentService(com.gw2auth.oauth2.server.service.client.consent.ClientConsentService) ApiSubTokenEntity(com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity) ApiToken(com.gw2auth.oauth2.server.service.apitoken.ApiToken) OAuth2TokenType(org.springframework.security.oauth2.core.OAuth2TokenType) OAuth2User(org.springframework.security.oauth2.core.user.OAuth2User) OAuth2TokenCustomizer(org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer) OAuth2Error(org.springframework.security.oauth2.core.OAuth2Error) Clock(java.time.Clock) ClientAuthorization(com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization) Transactional(org.springframework.transaction.annotation.Transactional) ClientConsentService(com.gw2auth.oauth2.server.service.client.consent.ClientConsentService) ClientConsent(com.gw2auth.oauth2.server.service.client.consent.ClientConsent) Batch(com.gw2auth.oauth2.server.util.Batch) ExecutionException(java.util.concurrent.ExecutionException) TimeoutException(java.util.concurrent.TimeoutException) Pair(com.gw2auth.oauth2.server.util.Pair) Gw2SubToken(com.gw2auth.oauth2.server.service.gw2.Gw2SubToken) ClientAuthorization(com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization) Instant(java.time.Instant) OAuth2Error(org.springframework.security.oauth2.core.OAuth2Error) ApiSubTokenEntity(com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity) ApiToken(com.gw2auth.oauth2.server.service.apitoken.ApiToken) OAuth2AuthenticationException(org.springframework.security.oauth2.core.OAuth2AuthenticationException) ApiTokenValidityUpdate(com.gw2auth.oauth2.server.service.apitoken.ApiTokenValidityUpdate)

Example 2 with ClientAuthorization

use of com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization in project oauth2-server by gw2auth.

the class ClientAuthorizationController method getClientAuthorizations.

@GetMapping(value = "/api/client/authorization/{clientId}", produces = MediaType.APPLICATION_JSON_VALUE)
public List<ClientAuthorizationResponse> getClientAuthorizations(@AuthenticationPrincipal Gw2AuthUser user, @PathVariable("clientId") UUID clientId) {
    final List<ClientAuthorization> clientAuthorizations = this.clientAuthorizationService.getClientAuthorizations(user.getAccountId(), clientId);
    // get all gw2-account ids for batch lookup
    final Set<UUID> gw2AccountIds = clientAuthorizations.stream().flatMap((v) -> v.gw2AccountIds().stream()).collect(Collectors.toSet());
    final Map<UUID, ApiToken> apiTokenByGw2AccountId = this.apiTokenService.getApiTokens(user.getAccountId(), gw2AccountIds).stream().collect(Collectors.toMap(ApiToken::gw2AccountId, Function.identity()));
    final List<ClientAuthorizationResponse> result = new ArrayList<>(clientAuthorizations.size());
    for (ClientAuthorization clientAuthorization : clientAuthorizations) {
        final List<ClientAuthorizationResponse.Token> tokens = new ArrayList<>(clientAuthorization.gw2AccountIds().size());
        for (UUID gw2AccountId : clientAuthorization.gw2AccountIds()) {
            final ApiToken apiToken = apiTokenByGw2AccountId.get(gw2AccountId);
            if (apiToken != null) {
                tokens.add(new ClientAuthorizationResponse.Token(gw2AccountId, apiToken.displayName()));
            }
        }
        result.add(ClientAuthorizationResponse.create(clientAuthorization, tokens));
    }
    return result;
}
Also used : Gw2AuthUser(com.gw2auth.oauth2.server.service.user.Gw2AuthUser) PathVariable(org.springframework.web.bind.annotation.PathVariable) AbstractRestController(com.gw2auth.oauth2.server.web.AbstractRestController) java.util(java.util) ApiTokenService(com.gw2auth.oauth2.server.service.apitoken.ApiTokenService) ClientAuthorizationService(com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorizationService) MediaType(org.springframework.http.MediaType) Autowired(org.springframework.beans.factory.annotation.Autowired) RestController(org.springframework.web.bind.annotation.RestController) Function(java.util.function.Function) Collectors(java.util.stream.Collectors) HttpStatus(org.springframework.http.HttpStatus) ApiToken(com.gw2auth.oauth2.server.service.apitoken.ApiToken) AuthenticationPrincipal(org.springframework.security.core.annotation.AuthenticationPrincipal) GetMapping(org.springframework.web.bind.annotation.GetMapping) ResponseEntity(org.springframework.http.ResponseEntity) ClientAuthorization(com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization) DeleteMapping(org.springframework.web.bind.annotation.DeleteMapping) ClientAuthorization(com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization) ApiToken(com.gw2auth.oauth2.server.service.apitoken.ApiToken) ApiToken(com.gw2auth.oauth2.server.service.apitoken.ApiToken) GetMapping(org.springframework.web.bind.annotation.GetMapping)

Example 3 with ClientAuthorization

use of com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization in project oauth2-server by gw2auth.

the class OAuth2ServerTest method consentSubmitWithLaterRemovedRootApiTokens.

@WithGw2AuthLogin
public void consentSubmitWithLaterRemovedRootApiTokens(MockHttpSession session) throws Exception {
    final long accountId = AuthenticationHelper.getUser(session).orElseThrow().getAccountId();
    final ClientRegistrationCreation clientRegistrationCreation = createClientRegistration();
    final ClientRegistration clientRegistration = clientRegistrationCreation.clientRegistration();
    // perform authorization request (which should redirect to the consent page)
    MvcResult result = performAuthorizeWithClient(session, clientRegistration, List.of(Gw2ApiPermission.ACCOUNT.oauth2())).andReturn();
    // submit the consent
    final String tokenA = TestHelper.randomRootToken();
    final String tokenB = TestHelper.randomRootToken();
    final String tokenC = TestHelper.randomRootToken();
    result = performSubmitConsent(session, clientRegistration, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), tokenA, tokenB, tokenC).andReturn();
    // verify the consent has been saved
    final ClientConsentEntity clientConsentEntity = this.clientConsentRepository.findByAccountIdAndClientRegistrationId(accountId, clientRegistration.id()).orElse(null);
    assertNotNull(clientConsentEntity);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2()), clientConsentEntity.authorizedScopes());
    // verify the authorization has been saved
    final List<ClientAuthorizationEntity> authorizations = this.clientAuthorizationRepository.findAllByAccountIdAndClientRegistrationId(accountId, clientConsentEntity.clientRegistrationId());
    assertEquals(1, authorizations.size());
    final ClientAuthorizationEntity clientAuthorization = authorizations.get(0);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2()), clientAuthorization.authorizedScopes());
    List<ClientAuthorizationTokenEntity> clientAuthorizationTokenEntities = this.clientAuthorizationTokenRepository.findAllByAccountIdAndClientAuthorizationId(accountId, clientAuthorization.id());
    assertEquals(2, clientAuthorizationTokenEntities.size());
    // set testing clock to token customizer
    Clock testingClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
    this.oAuth2TokenCustomizerService.setClock(testingClock);
    // retrieve the initial access and refresh token
    final String[] dummySubtokenA = new String[] { TestHelper.createSubtokenJWT(this.gw2AccountId1st, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L)) };
    final String[] dummySubtokenB = new String[] { TestHelper.createSubtokenJWT(this.gw2AccountId2nd, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L)) };
    result = performRetrieveTokenByCodeAndExpectValid(clientRegistrationCreation, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), Map.of(tokenA, dummySubtokenA[0], tokenB, dummySubtokenB[0])).andReturn();
    // verify the subtokens have been updated
    clientAuthorizationTokenEntities = this.clientAuthorizationTokenRepository.findAllByAccountIdAndClientAuthorizationId(accountId, clientAuthorization.id());
    assertEquals(2, clientAuthorizationTokenEntities.size());
    Set<String> savedSubtokens = this.apiSubTokenRepository.findAllByAccountIdGw2AccountIdsAndGw2ApiPermissionsBitSet(accountId, Set.of(this.gw2AccountId1st, this.gw2AccountId2nd), Gw2ApiPermission.toBitSet(Set.of(Gw2ApiPermission.ACCOUNT))).stream().map(ApiSubTokenEntity::gw2ApiSubtoken).collect(Collectors.toSet());
    assertEquals(2, savedSubtokens.size());
    assertTrue(savedSubtokens.contains(dummySubtokenA[0]));
    assertTrue(savedSubtokens.contains(dummySubtokenB[0]));
    // verify the validity status has been saved
    final List<ApiTokenEntity> apiTokenEntities = this.apiTokenRepository.findAllByAccountIdAndGw2AccountIds(accountId, Set.of(this.gw2AccountId1st, this.gw2AccountId2nd));
    assertEquals(2, apiTokenEntities.size());
    assertTrue(apiTokenEntities.get(0).isValid());
    assertInstantEquals(testingClock.instant(), apiTokenEntities.get(0).lastValidCheckTime());
    assertTrue(apiTokenEntities.get(1).isValid());
    assertInstantEquals(testingClock.instant(), apiTokenEntities.get(1).lastValidCheckTime());
    // verify the access token
    JsonNode tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA[0])), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB[0]))));
    // remove all Root-Tokens for this authorization
    for (ClientAuthorizationTokenEntity clientAuthorizationTokenEntity : clientAuthorizationTokenEntities) {
        this.apiTokenRepository.deleteByAccountIdAndGw2AccountId(clientAuthorizationTokenEntity.accountId(), clientAuthorizationTokenEntity.gw2AccountId());
    }
    // retrieve a new access token using the refresh token
    testingClock = Clock.offset(testingClock, Duration.ofMinutes(31L));
    this.oAuth2TokenCustomizerService.setClock(testingClock);
    final String refreshToken = tokenResponse.get("refresh_token").textValue();
    performRetrieveTokensByRefreshToken(clientRegistrationCreation, refreshToken).andExpect(status().isBadRequest()).andExpect(jsonPath("$.error").isString()).andExpect(jsonPath("$.access_token").doesNotExist()).andExpect(jsonPath("$.refresh_token").doesNotExist()).andReturn();
}
Also used : ApiTokenEntity(com.gw2auth.oauth2.server.repository.apitoken.ApiTokenEntity) JsonNode(com.fasterxml.jackson.databind.JsonNode) MvcResult(org.springframework.test.web.servlet.MvcResult) Clock(java.time.Clock) ClientRegistrationCreation(com.gw2auth.oauth2.server.service.client.registration.ClientRegistrationCreation) ClientAuthorizationEntity(com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationEntity) ClientRegistration(com.gw2auth.oauth2.server.service.client.registration.ClientRegistration) JSONObject(org.json.JSONObject) ClientAuthorizationTokenEntity(com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationTokenEntity) ClientConsentEntity(com.gw2auth.oauth2.server.repository.client.consent.ClientConsentEntity)

Example 4 with ClientAuthorization

use of com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization in project oauth2-server by gw2auth.

the class OAuth2ServerTest method consentSubmitAndSubmitAgainWithLessScopes.

@WithGw2AuthLogin
public void consentSubmitAndSubmitAgainWithLessScopes(MockHttpSession session) throws Exception {
    final long accountId = AuthenticationHelper.getUser(session).orElseThrow().getAccountId();
    final ClientRegistrationCreation clientRegistrationCreation = createClientRegistration();
    final ClientRegistration clientRegistration = clientRegistrationCreation.clientRegistration();
    // perform authorization request (which should redirect to the consent page)
    MvcResult result = performAuthorizeWithClient(session, clientRegistration, List.of(Gw2ApiPermission.ACCOUNT.oauth2(), Gw2ApiPermission.UNLOCKS.oauth2())).andReturn();
    // submit the consent
    final String tokenA = TestHelper.randomRootToken();
    final String tokenB = TestHelper.randomRootToken();
    final String tokenC = TestHelper.randomRootToken();
    result = performSubmitConsent(session, clientRegistration, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), tokenA, tokenB, tokenC, Set.of(Gw2ApiPermission.ACCOUNT, Gw2ApiPermission.UNLOCKS)).andReturn();
    // verify the consent has been saved
    ClientConsentEntity clientConsentEntity = this.clientConsentRepository.findByAccountIdAndClientRegistrationId(accountId, clientRegistration.id()).orElse(null);
    assertNotNull(clientConsentEntity);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2(), Gw2ApiPermission.UNLOCKS.oauth2()), clientConsentEntity.authorizedScopes());
    // verify the authorization has been saved
    final List<ClientAuthorizationEntity> authorizations = this.clientAuthorizationRepository.findAllByAccountIdAndClientRegistrationId(accountId, clientConsentEntity.clientRegistrationId());
    assertEquals(1, authorizations.size());
    final ClientAuthorizationEntity clientAuthorization = authorizations.get(0);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2(), Gw2ApiPermission.UNLOCKS.oauth2()), clientAuthorization.authorizedScopes());
    List<ClientAuthorizationTokenEntity> clientAuthorizationTokenEntities = this.clientAuthorizationTokenRepository.findAllByAccountIdAndClientAuthorizationId(accountId, clientAuthorization.id());
    assertEquals(2, clientAuthorizationTokenEntities.size());
    // set testing clock to token customizer
    Clock testingClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
    this.oAuth2TokenCustomizerService.setClock(testingClock);
    // retrieve the initial access and refresh token
    final String[] dummySubtokenA = new String[] { TestHelper.createSubtokenJWT(this.gw2AccountId1st, Set.of(Gw2ApiPermission.ACCOUNT, Gw2ApiPermission.UNLOCKS), testingClock.instant(), Duration.ofMinutes(30L)) };
    final String[] dummySubtokenB = new String[] { TestHelper.createSubtokenJWT(this.gw2AccountId2nd, Set.of(Gw2ApiPermission.ACCOUNT, Gw2ApiPermission.UNLOCKS), testingClock.instant(), Duration.ofMinutes(30L)) };
    result = performRetrieveTokenByCode(clientRegistrationCreation, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), Map.of(tokenA, dummySubtokenA[0], tokenB, dummySubtokenB[0]), Set.of(Gw2ApiPermission.ACCOUNT, Gw2ApiPermission.UNLOCKS)).andExpectAll(expectValidTokenResponse(Gw2ApiPermission.ACCOUNT.oauth2(), Gw2ApiPermission.UNLOCKS.oauth2())).andReturn();
    // verify the access token
    JsonNode tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA[0])), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB[0]))), Set.of(Gw2ApiPermission.ACCOUNT, Gw2ApiPermission.UNLOCKS));
    final String firstAuthorizationSubtokenA = dummySubtokenA[0];
    final String firstAuthorizationSubtokenB = dummySubtokenB[0];
    final JsonNode firstAuthorizationResponse = tokenResponse;
    // perform a new authorization
    // perform authorization request (which should redirect to application)
    result = performAuthorizeWithClient(session, clientRegistration, List.of(Gw2ApiPermission.ACCOUNT.oauth2()), false).andReturn();
    // verify the consent is unchanged
    clientConsentEntity = this.clientConsentRepository.findByAccountIdAndClientRegistrationId(accountId, clientRegistration.id()).orElse(null);
    assertNotNull(clientConsentEntity);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2(), Gw2ApiPermission.UNLOCKS.oauth2()), clientConsentEntity.authorizedScopes());
    // verify both authorizations exist
    clientAuthorizationTokenEntities = this.clientAuthorizationTokenRepository.findAllByAccountIdAndClientAuthorizationId(accountId, clientAuthorization.id());
    assertEquals(2, clientAuthorizationTokenEntities.size());
    // retrieve the initial access and refresh token
    dummySubtokenA[0] = TestHelper.createSubtokenJWT(this.gw2AccountId1st, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L));
    dummySubtokenB[0] = TestHelper.createSubtokenJWT(this.gw2AccountId2nd, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L));
    result = performRetrieveTokenByCodeAndExpectValid(clientRegistrationCreation, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), Map.of(tokenA, dummySubtokenA[0], tokenB, dummySubtokenB[0])).andReturn();
    // verify the access token
    tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA[0])), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB[0]))));
    // retrieve a new access and refresh token for the first authorization
    result = performRetrieveTokensByRefreshToken(clientRegistrationCreation, firstAuthorizationResponse.get("refresh_token").textValue()).andExpectAll(expectValidTokenResponse(Gw2ApiPermission.ACCOUNT.oauth2(), Gw2ApiPermission.UNLOCKS.oauth2())).andReturn();
    // verify the access token
    tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", firstAuthorizationSubtokenA)), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", firstAuthorizationSubtokenB))), Set.of(Gw2ApiPermission.ACCOUNT, Gw2ApiPermission.UNLOCKS));
}
Also used : JsonNode(com.fasterxml.jackson.databind.JsonNode) MvcResult(org.springframework.test.web.servlet.MvcResult) Clock(java.time.Clock) ClientRegistrationCreation(com.gw2auth.oauth2.server.service.client.registration.ClientRegistrationCreation) ClientAuthorizationEntity(com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationEntity) ClientRegistration(com.gw2auth.oauth2.server.service.client.registration.ClientRegistration) JSONObject(org.json.JSONObject) ClientAuthorizationTokenEntity(com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationTokenEntity) ClientConsentEntity(com.gw2auth.oauth2.server.repository.client.consent.ClientConsentEntity)

Example 5 with ClientAuthorization

use of com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization in project oauth2-server by gw2auth.

the class OAuth2ServerTest method consentSubmitWithGw2AuthVerifiedScope.

@WithGw2AuthLogin
public void consentSubmitWithGw2AuthVerifiedScope(MockHttpSession session) throws Exception {
    final long accountId = AuthenticationHelper.getUser(session).orElseThrow().getAccountId();
    final ClientRegistrationCreation clientRegistrationCreation = createClientRegistration();
    final ClientRegistration clientRegistration = clientRegistrationCreation.clientRegistration();
    // perform authorization request (which should redirect to the consent page)
    MvcResult result = performAuthorizeWithClient(session, clientRegistration, List.of(Gw2ApiPermission.ACCOUNT.oauth2(), ClientConsentService.GW2AUTH_VERIFIED_SCOPE)).andReturn();
    // submit the consent
    final String tokenA = TestHelper.randomRootToken();
    final String tokenB = TestHelper.randomRootToken();
    final String tokenC = TestHelper.randomRootToken();
    result = performSubmitConsent(session, clientRegistration, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), tokenA, tokenB, tokenC).andReturn();
    // verify the consent has been saved
    final ClientConsentEntity clientConsentEntity = this.clientConsentRepository.findByAccountIdAndClientRegistrationId(accountId, clientRegistration.id()).orElse(null);
    assertNotNull(clientConsentEntity);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2(), ClientConsentService.GW2AUTH_VERIFIED_SCOPE), clientConsentEntity.authorizedScopes());
    // verify the authorization has been saved
    final List<ClientAuthorizationEntity> authorizations = this.clientAuthorizationRepository.findAllByAccountIdAndClientRegistrationId(accountId, clientConsentEntity.clientRegistrationId());
    assertEquals(1, authorizations.size());
    final ClientAuthorizationEntity clientAuthorization = authorizations.get(0);
    assertEquals(Set.of(Gw2ApiPermission.ACCOUNT.oauth2(), ClientConsentService.GW2AUTH_VERIFIED_SCOPE), clientAuthorization.authorizedScopes());
    List<ClientAuthorizationTokenEntity> clientAuthorizationTokenEntities = this.clientAuthorizationTokenRepository.findAllByAccountIdAndClientAuthorizationId(accountId, clientAuthorization.id());
    assertEquals(2, clientAuthorizationTokenEntities.size());
    // save account verification for one account
    this.gw2AccountVerificationRepository.save(new Gw2AccountVerificationEntity(this.gw2AccountId1st, accountId));
    // set testing clock to token customizer
    Clock testingClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
    this.oAuth2TokenCustomizerService.setClock(testingClock);
    // retrieve the initial access and refresh token
    final String[] dummySubtokenA = new String[] { TestHelper.createSubtokenJWT(this.gw2AccountId1st, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L)) };
    final String[] dummySubtokenB = new String[] { TestHelper.createSubtokenJWT(this.gw2AccountId2nd, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L)) };
    result = performRetrieveTokenByCode(clientRegistrationCreation, URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())), Map.of(tokenA, dummySubtokenA[0], tokenB, dummySubtokenB[0]), Set.of(Gw2ApiPermission.ACCOUNT)).andExpectAll(expectValidTokenResponse(Gw2ApiPermission.ACCOUNT.oauth2(), ClientConsentService.GW2AUTH_VERIFIED_SCOPE)).andReturn();
    // verify the authorized tokens have been updated
    Set<String> savedSubtokens = this.apiSubTokenRepository.findAllByAccountIdGw2AccountIdsAndGw2ApiPermissionsBitSet(accountId, Set.of(this.gw2AccountId1st, this.gw2AccountId2nd), Gw2ApiPermission.toBitSet(Set.of(Gw2ApiPermission.ACCOUNT))).stream().map(ApiSubTokenEntity::gw2ApiSubtoken).collect(Collectors.toSet());
    assertEquals(2, savedSubtokens.size());
    assertTrue(savedSubtokens.contains(dummySubtokenA[0]));
    assertTrue(savedSubtokens.contains(dummySubtokenB[0]));
    // verify the validity status has been saved
    final List<ApiTokenEntity> apiTokenEntities = this.apiTokenRepository.findAllByAccountIdAndGw2AccountIds(accountId, Set.of(this.gw2AccountId1st, this.gw2AccountId2nd));
    assertEquals(2, apiTokenEntities.size());
    assertTrue(apiTokenEntities.get(0).isValid());
    assertInstantEquals(testingClock.instant(), apiTokenEntities.get(0).lastValidCheckTime());
    assertTrue(apiTokenEntities.get(1).isValid());
    assertInstantEquals(testingClock.instant(), apiTokenEntities.get(1).lastValidCheckTime());
    // verify the access token
    JsonNode tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA[0], "verified", true)), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB[0], "verified", false))));
    // remove the verification for the first account and save one for the second
    this.gw2AccountVerificationRepository.deleteById(this.gw2AccountId1st);
    this.gw2AccountVerificationRepository.save(new Gw2AccountVerificationEntity(this.gw2AccountId2nd, accountId));
    // retrieve a new access token using the refresh token
    final String refreshToken = tokenResponse.get("refresh_token").textValue();
    result = performRetrieveTokensByRefreshToken(clientRegistrationCreation, refreshToken).andExpectAll(expectValidTokenResponse(Gw2ApiPermission.ACCOUNT.oauth2(), ClientConsentService.GW2AUTH_VERIFIED_SCOPE)).andReturn();
    tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA[0], "verified", false)), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB[0], "verified", true))));
}
Also used : ApiTokenEntity(com.gw2auth.oauth2.server.repository.apitoken.ApiTokenEntity) JsonNode(com.fasterxml.jackson.databind.JsonNode) MvcResult(org.springframework.test.web.servlet.MvcResult) Clock(java.time.Clock) Gw2AccountVerificationEntity(com.gw2auth.oauth2.server.repository.verification.Gw2AccountVerificationEntity) ClientRegistrationCreation(com.gw2auth.oauth2.server.service.client.registration.ClientRegistrationCreation) ClientAuthorizationEntity(com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationEntity) ClientRegistration(com.gw2auth.oauth2.server.service.client.registration.ClientRegistration) JSONObject(org.json.JSONObject) ClientAuthorizationTokenEntity(com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationTokenEntity) ClientConsentEntity(com.gw2auth.oauth2.server.repository.client.consent.ClientConsentEntity)

Aggregations

ClientRegistration (com.gw2auth.oauth2.server.service.client.registration.ClientRegistration)10 JsonNode (com.fasterxml.jackson.databind.JsonNode)9 ClientConsentEntity (com.gw2auth.oauth2.server.repository.client.consent.ClientConsentEntity)9 ClientRegistrationCreation (com.gw2auth.oauth2.server.service.client.registration.ClientRegistrationCreation)8 Clock (java.time.Clock)8 MvcResult (org.springframework.test.web.servlet.MvcResult)8 ClientAuthorizationEntity (com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationEntity)7 ClientAuthorizationTokenEntity (com.gw2auth.oauth2.server.repository.client.authorization.ClientAuthorizationTokenEntity)7 JSONObject (org.json.JSONObject)7 ApiTokenEntity (com.gw2auth.oauth2.server.repository.apitoken.ApiTokenEntity)6 ApiToken (com.gw2auth.oauth2.server.service.apitoken.ApiToken)5 ClientAuthorization (com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorization)5 Collectors (java.util.stream.Collectors)5 Autowired (org.springframework.beans.factory.annotation.Autowired)5 ApiSubTokenEntity (com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity)4 ApiTokenService (com.gw2auth.oauth2.server.service.apitoken.ApiTokenService)4 ClientAuthorizationService (com.gw2auth.oauth2.server.service.client.authorization.ClientAuthorizationService)4 Gw2AuthUser (com.gw2auth.oauth2.server.service.user.Gw2AuthUser)4 java.util (java.util)4 MediaType (org.springframework.http.MediaType)4