use of com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity 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);
}
}
use of com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity 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();
}
use of com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity 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))));
}
use of com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity in project oauth2-server by gw2auth.
the class OAuth2ServerTest method consentSubmitAndHappyFlow.
@WithGw2AuthLogin
public void consentSubmitAndHappyFlow(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());
// verify the tokens have been saved
List<ClientAuthorizationTokenEntity> clientAuthorizationTokenEntities = this.clientAuthorizationTokenRepository.findAllByAccountIdAndClientAuthorizationId(accountId, clientAuthorization.id());
assertEquals(2, clientAuthorizationTokenEntities.size());
// set testing clock to token customizer
final Clock testingClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
this.oAuth2TokenCustomizerService.setClock(testingClock);
// retrieve the initial access and refresh token
final String dummySubtokenA = TestHelper.createSubtokenJWT(this.gw2AccountId1st, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L));
final String dummySubtokenB = 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, tokenB, dummySubtokenB)).andReturn();
// verify the subtokens have been saved
final Set<String> subTokens = 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, subTokens.size());
assertTrue(subTokens.contains(dummySubtokenA));
assertTrue(subTokens.contains(dummySubtokenB));
// 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)), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB))));
// retrieve a new access token using the refresh token
final String refreshToken = tokenResponse.get("refresh_token").textValue();
result = performRetrieveTokensByRefreshTokenAndExpectValid(clientRegistrationCreation, refreshToken).andReturn();
tokenResponse = assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA)), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "token", dummySubtokenB))));
assertNotEquals(refreshToken, tokenResponse.get("refresh_token").textValue());
}
use of com.gw2auth.oauth2.server.repository.apisubtoken.ApiSubTokenEntity in project oauth2-server by gw2auth.
the class OAuth2ServerTest method consentSubmitWithUnexpectedGW2APIException.
@WithGw2AuthLogin
public void consentSubmitWithUnexpectedGW2APIException(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
final Clock testingClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
this.oAuth2TokenCustomizerService.setClock(testingClock);
// prepare the gw2 api for the next requests
final String dummySubtokenA = TestHelper.createSubtokenJWT(this.gw2AccountId1st, Set.of(Gw2ApiPermission.ACCOUNT), testingClock.instant(), Duration.ofMinutes(30L));
this.gw2RestServer.reset();
this.gw2RestServer.expect(times(2), requestTo(new StringStartsWith("/v2/createsubtoken"))).andExpect(method(HttpMethod.GET)).andExpect(MockRestRequestMatchers.header("Authorization", new StringStartsWith("Bearer "))).andExpect(queryParam("permissions", split(",", containingAll(Gw2ApiPermission.ACCOUNT.gw2())))).andExpect(queryParam("expire", asInstant(instantWithinTolerance(Instant.now().plus(Duration.ofMinutes(30L)), Duration.ofSeconds(5L))))).andRespond((request) -> {
final String gw2ApiToken = request.getHeaders().getFirst("Authorization").replaceFirst("Bearer ", "");
final String subtoken;
if (gw2ApiToken.equals(tokenA)) {
subtoken = dummySubtokenA;
} else if (gw2ApiToken.equals(tokenB)) {
throw new RuntimeException("unexpected exception");
} else {
subtoken = null;
}
if (subtoken == null || subtoken.isEmpty()) {
return new MockClientHttpResponse(new byte[0], HttpStatus.UNAUTHORIZED);
}
final MockClientHttpResponse response = new MockClientHttpResponse(new JSONObject(Map.of("subtoken", subtoken)).toString().getBytes(StandardCharsets.UTF_8), HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response;
});
// retrieve the initial access and refresh token
final String codeParam = Utils.parseQuery(URI.create(Objects.requireNonNull(result.getResponse().getRedirectedUrl())).getRawQuery()).filter(QueryParam::hasValue).filter((queryParam) -> queryParam.name().equals(OAuth2ParameterNames.CODE)).map(QueryParam::value).findFirst().orElse(null);
assertNotNull(codeParam);
// retrieve an access token
// dont use the user session here!
result = this.mockMvc.perform(post("/oauth2/token").queryParam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()).queryParam(OAuth2ParameterNames.CODE, codeParam).queryParam(OAuth2ParameterNames.CLIENT_ID, clientRegistrationCreation.clientRegistration().clientId().toString()).queryParam(OAuth2ParameterNames.CLIENT_SECRET, clientRegistrationCreation.clientSecret()).queryParam(OAuth2ParameterNames.REDIRECT_URI, TestHelper.first(clientRegistrationCreation.clientRegistration().redirectUris()).orElseThrow())).andExpectAll(expectValidTokenResponse()).andReturn();
// verify the subtokens have been updated
final 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(1, savedSubtokens.size());
assertTrue(savedSubtokens.contains(dummySubtokenA));
// 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());
for (ApiTokenEntity apiTokenEntity : apiTokenEntities) {
if (apiTokenEntity.gw2AccountId().equals(this.gw2AccountId1st)) {
assertTrue(apiTokenEntity.isValid());
assertInstantEquals(testingClock.instant(), apiTokenEntity.lastValidCheckTime());
} else {
assertTrue(apiTokenEntity.isValid());
assertTrue(testingClock.instant().isAfter(apiTokenEntity.lastValidCheckTime()));
}
}
// verify the access token
assertTokenResponse(result, () -> Map.of(this.gw2AccountId1st, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "First", "token", dummySubtokenA)), this.gw2AccountId2nd, new com.nimbusds.jose.shaded.json.JSONObject(Map.of("name", "Second", "error", "Failed to obtain new subtoken"))));
}
Aggregations