@Test public void loadUserWhenUserRequestIsNullThenThrowIllegalArgumentException() { this.exception.expect(IllegalArgumentException.class); this.userService.loadUser(null); }
@Test public void loadUserWhenAuthorizedScopesDoesNotContainUserInfoScopesThenUserInfoEndpointNotRequested() { ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri("http://provider.com/user").build(); Set<String> authorizedScopes = new LinkedHashSet<>(Arrays.asList("scope1", "scope2")); OAuth2AccessToken accessToken = new OAuth2AccessToken( OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.MIN, Instant.MAX, authorizedScopes); OidcUser user = this.userService.loadUser( new OidcUserRequest(clientRegistration, accessToken, this.idToken)); assertThat(user.getUserInfo()).isNull(); }
@Test public void loadUserWhenUserInfoUriIsNullThenUserInfoEndpointNotRequested() { OidcUser user = this.userService.loadUser( new OidcUserRequest(this.clientRegistrationBuilder.build(), this.accessToken, this.idToken)); assertThat(user.getUserInfo()).isNull(); }
.userInfoUri(userInfoUri).build(); OidcUser user = this.userService.loadUser( new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));
@Test public void loadUserWhenUserInfoUriInvalidThenThrowOAuth2AuthenticationException() { this.exception.expect(OAuth2AuthenticationException.class); this.exception.expectMessage(containsString("[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource")); String userInfoUri = "http://invalid-provider.com/user"; ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); }
@Test public void loadUserWhenAuthenticationMethodHeaderSuccessResponseThenHttpMethodGet() throws Exception { String userInfoResponse = "{\n" + " \"sub\": \"subject1\",\n" + " \"name\": \"first last\",\n" + " \"given_name\": \"first\",\n" + " \"family_name\": \"last\",\n" + " \"preferred_username\": \"user1\",\n" + " \"email\": \"user1@example.com\"\n" + "}\n"; this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); RecordedRequest request = this.server.takeRequest(); assertThat(request.getMethod()).isEqualTo(HttpMethod.GET.name()); assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE); assertThat(request.getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer " + this.accessToken.getTokenValue()); }
@Test public void loadUserWhenUserInfoSuccessResponseAndUserInfoSubjectNotSameAsIdTokenSubjectThenThrowOAuth2AuthenticationException() { this.exception.expect(OAuth2AuthenticationException.class); this.exception.expectMessage(containsString("invalid_user_info_response")); String userInfoResponse = "{\n" + " \"sub\": \"other-subject\"\n" + "}\n"; this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); }
@Test public void loadUserWhenUserInfoSuccessResponseThenAcceptHeaderJson() throws Exception { String userInfoResponse = "{\n" + " \"sub\": \"subject1\",\n" + " \"name\": \"first last\",\n" + " \"given_name\": \"first\",\n" + " \"family_name\": \"last\",\n" + " \"preferred_username\": \"user1\",\n" + " \"email\": \"user1@example.com\"\n" + "}\n"; this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); assertThat(this.server.takeRequest(1, TimeUnit.SECONDS).getHeader(HttpHeaders.ACCEPT)) .isEqualTo(MediaType.APPLICATION_JSON_VALUE); }
@Test public void loadUserWhenServerErrorThenThrowOAuth2AuthenticationException() { this.exception.expect(OAuth2AuthenticationException.class); this.exception.expectMessage(containsString("[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 500 Server Error")); this.server.enqueue(new MockResponse().setResponseCode(500)); String userInfoUri = server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); }
@Test public void loadUserWhenCustomUserNameAttributeNameThenGetNameReturnsCustomUserName() { String userInfoResponse = "{\n" + " \"sub\": \"subject1\",\n" + " \"name\": \"first last\",\n" + " \"given_name\": \"first\",\n" + " \"family_name\": \"last\",\n" + " \"preferred_username\": \"user1\",\n" + " \"email\": \"user1@example.com\"\n" + "}\n"; this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri) .userNameAttributeName(StandardClaimNames.EMAIL).build(); OidcUser user = this.userService.loadUser( new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); assertThat(user.getName()).isEqualTo("user1@example.com"); }
@Test public void loadUserWhenAuthenticationMethodFormSuccessResponseThenHttpMethodPost() throws Exception { String userInfoResponse = "{\n" + " \"sub\": \"subject1\",\n" + " \"name\": \"first last\",\n" + " \"given_name\": \"first\",\n" + " \"family_name\": \"last\",\n" + " \"preferred_username\": \"user1\",\n" + " \"email\": \"user1@example.com\"\n" + "}\n"; this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri) .userInfoAuthenticationMethod(AuthenticationMethod.FORM).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); RecordedRequest request = this.server.takeRequest(); assertThat(request.getMethod()).isEqualTo(HttpMethod.POST.name()); assertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE); assertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE); assertThat(request.getBody().readUtf8()).isEqualTo("access_token=" + this.accessToken.getTokenValue()); }
@Test public void loadUserWhenUserInfoSuccessResponseInvalidThenThrowOAuth2AuthenticationException() { this.exception.expect(OAuth2AuthenticationException.class); this.exception.expectMessage(containsString("[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource")); String userInfoResponse = "{\n" + " \"sub\": \"subject1\",\n" + " \"name\": \"first last\",\n" + " \"given_name\": \"first\",\n" + " \"family_name\": \"last\",\n" + " \"preferred_username\": \"user1\",\n" + " \"email\": \"user1@example.com\"\n"; // "}\n"; // Make the JSON invalid/malformed this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); }
@Test public void loadUserWhenUserInfoSuccessResponseAndUserInfoSubjectIsNullThenThrowOAuth2AuthenticationException() { this.exception.expect(OAuth2AuthenticationException.class); this.exception.expectMessage(containsString("invalid_user_info_response")); String userInfoResponse = "{\n" + " \"email\": \"full_name@provider.com\",\n" + " \"name\": \"full name\"\n" + "}\n"; this.server.enqueue(jsonResponse(userInfoResponse)); String userInfoUri = this.server.url("/user").toString(); ClientRegistration clientRegistration = this.clientRegistrationBuilder .userInfoUri(userInfoUri) .userNameAttributeName(StandardClaimNames.EMAIL).build(); this.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); }
@Override public MappedOidcUser loadUser(OidcUserRequest userRequest) { // load user first to guarantee successful authentication OidcUser oidcUser = super.loadUser(userRequest); return createOidcUser(oidcUser, userRequest); }
/** * Augments {@link OidcUserService#loadUser(OidcUserRequest)} to add authorities * provided by Keycloak. * * Needed because {@link OidcUserService#loadUser(OidcUserRequest)} (currently) * does not provide a hook for adding custom authorities from a * {@link OidcUserRequest}. */ @Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser user = super.loadUser(userRequest); Set<GrantedAuthority> authorities = new LinkedHashSet<>(); authorities.addAll(user.getAuthorities()); authorities.addAll(extractKeycloakAuthorities(userRequest)); return new DefaultOidcUser(authorities, userRequest.getIdToken(), user.getUserInfo(), "preferred_username"); }
@Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser user = super.loadUser(userRequest); // Only post process requests from the "Okta" reg if (!"Okta".equals(userRequest.getClientRegistration().getClientName())) { return user; } // start with authorities from super Set<GrantedAuthority> authorities = new HashSet<>(user.getAuthorities()); // add 'SCOPE_' authorities authorities.addAll(TokenUtil.tokenScopesToAuthorities(userRequest.getAccessToken())); // add any authorities extracted from the 'group' claim authorities.addAll(TokenUtil.tokenClaimsToAuthorities(user.getAttributes(), groupClaim)); String userNameAttributeName = userRequest.getClientRegistration() .getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); return StringUtils.hasText(userNameAttributeName) ? new DefaultOidcUser(authorities, user.getIdToken(), user.getUserInfo(), userNameAttributeName) : new DefaultOidcUser(authorities, user.getIdToken(), user.getUserInfo()); } }
@Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser oidcUser = super.loadUser(userRequest); LemonPrincipal principal = oauth2UserService.buildPrincipal(oidcUser, userRequest.getClientRegistration().getRegistrationId()); principal.setClaims(oidcUser.getClaims()); principal.setIdToken(oidcUser.getIdToken()); principal.setUserInfo(oidcUser.getUserInfo()); return principal; } }
@Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { final OidcUserService delegate = new OidcUserService(); // Delegate to the default implementation for loading a user OidcUser oidcUser = delegate.loadUser(userRequest); final OidcIdToken idToken = userRequest.getIdToken(); final String graphApiToken; final Set<GrantedAuthority> mappedAuthorities; try { // https://github.com/MicrosoftDocs/azure-docs/issues/8121#issuecomment-387090099 // In AAD App Registration configure oauth2AllowImplicitFlow to true final ClientRegistration registration = userRequest.getClientRegistration(); final ClientCredential credential = new ClientCredential(registration.getClientId(), registration.getClientSecret()); final AzureADGraphClient graphClient = new AzureADGraphClient(credential, aadAuthProps, serviceEndpointsProps); graphApiToken = graphClient.acquireTokenForGraphApi(idToken.getTokenValue(), aadAuthProps.getTenantId()).getAccessToken(); mappedAuthorities = graphClient.getGrantedAuthorities(graphApiToken); } catch (MalformedURLException e) { throw wrapException(INVALID_REQUEST, "Failed to acquire token for Graph API.", null, e); } catch (ServiceUnavailableException | InterruptedException | ExecutionException e) { throw wrapException(SERVER_ERROR, "Failed to acquire token for Graph API.", null, e); } catch (IOException e) { throw wrapException(SERVER_ERROR, "Failed to map group to authorities.", null, e); } // Create a copy of oidcUser but use the mappedAuthorities instead oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), getUserNameAttrName(userRequest)); return oidcUser; }
@Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { final OidcUserService delegate = new OidcUserService(); // Delegate to the default implementation for loading a user OidcUser oidcUser = delegate.loadUser(userRequest); final OidcIdToken idToken = userRequest.getIdToken(); final String graphApiToken; final Set<GrantedAuthority> mappedAuthorities; try { // https://github.com/MicrosoftDocs/azure-docs/issues/8121#issuecomment-387090099 // In AAD App Registration configure oauth2AllowImplicitFlow to true final ClientRegistration registration = userRequest.getClientRegistration(); final ClientCredential credential = new ClientCredential(registration.getClientId(), registration.getClientSecret()); final AzureADGraphClient graphClient = new AzureADGraphClient(credential, aadAuthProps, serviceEndpointsProps); graphApiToken = graphClient.acquireTokenForGraphApi(idToken.getTokenValue(), aadAuthProps.getTenantId()).getAccessToken(); mappedAuthorities = graphClient.getGrantedAuthorities(graphApiToken); } catch (MalformedURLException e) { throw wrapException(INVALID_REQUEST, "Failed to acquire token for Graph API.", null, e); } catch (ServiceUnavailableException | InterruptedException | ExecutionException e) { throw wrapException(SERVER_ERROR, "Failed to acquire token for Graph API.", null, e); } catch (IOException e) { throw wrapException(SERVER_ERROR, "Failed to map group to authorities.", null, e); } // Create a copy of oidcUser but use the mappedAuthorities instead oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), getUserNameAttrName(userRequest)); return oidcUser; }