OIDC Financial API Read Only Profile : scope MUST be returned in the
response from Token Endpoint
This commit is contained in:
parent
f0f7321c38
commit
6f6a467c7b
4 changed files with 280 additions and 0 deletions
|
@ -57,7 +57,17 @@ public class AccessTokenResponse {
|
|||
|
||||
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
|
||||
|
||||
// OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
|
||||
@JsonProperty("scope")
|
||||
protected String scope;
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
|
|
|
@ -70,6 +70,7 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -854,8 +855,57 @@ public class TokenManager {
|
|||
if (userNotBefore > notBefore) notBefore = userNotBefore;
|
||||
res.setNotBeforePolicy(notBefore);
|
||||
|
||||
// OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
|
||||
String requestedScope = clientSession.getNote(OAuth2Constants.SCOPE);
|
||||
if (accessToken != null && requestedScope != null) {
|
||||
List<String> returnedScopes = new ArrayList<String>();
|
||||
// at attachAuthenticationSession(), take over notes from AuthenticationSessionModel to AuthenticatedClientSessionModel
|
||||
List<String> requestedScopes = Arrays.asList(requestedScope.split(" "));
|
||||
|
||||
// distinguish between so called role scope and oauth scope
|
||||
// only pick up oauth scope following https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
|
||||
// for realm role - scope
|
||||
if (accessToken.getRealmAccess() != null && accessToken.getRealmAccess().getRoles() != null) {
|
||||
addRolesAsScopes(returnedScopes, requestedScopes, accessToken.getRealmAccess().getRoles());
|
||||
}
|
||||
// for client role - scope
|
||||
if (accessToken.getResourceAccess() != null) {
|
||||
for (String clientId : accessToken.getResourceAccess().keySet()) {
|
||||
if (accessToken.getResourceAccess(clientId).getRoles() != null) {
|
||||
addRolesAsScopes(returnedScopes, requestedScopes, accessToken.getResourceAccess(clientId).getRoles(), clientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : returnedScopes) {
|
||||
builder.append(s).append(" ");
|
||||
}
|
||||
res.setScope(builder.toString().trim());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private void addRolesAsScopes(List<String> returnedScopes, List<String> requestedScopes, Set<String> roles) {
|
||||
for (String r : roles) {
|
||||
for (String s : requestedScopes) {
|
||||
if (s.equals(r)) {
|
||||
returnedScopes.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addRolesAsScopes(List<String> returnedScopes, List<String> requestedScopes, Set<String> roles, String clientId) {
|
||||
for (String r : roles) {
|
||||
for (String s : requestedScopes) {
|
||||
if (s.equals(clientId + "/" + r)) {
|
||||
returnedScopes.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RefreshResult {
|
||||
|
|
|
@ -949,6 +949,8 @@ public class OAuthClient {
|
|||
private int expiresIn;
|
||||
private int refreshExpiresIn;
|
||||
private String refreshToken;
|
||||
// OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
|
||||
private String scope;
|
||||
|
||||
private String error;
|
||||
private String errorDescription;
|
||||
|
@ -970,6 +972,11 @@ public class OAuthClient {
|
|||
expiresIn = (Integer) responseJson.get("expires_in");
|
||||
refreshExpiresIn = (Integer) responseJson.get("refresh_expires_in");
|
||||
|
||||
// OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
|
||||
if (responseJson.containsKey(OAuth2Constants.SCOPE)) {
|
||||
scope = (String) responseJson.get(OAuth2Constants.SCOPE);
|
||||
}
|
||||
|
||||
if (responseJson.containsKey(OAuth2Constants.REFRESH_TOKEN)) {
|
||||
refreshToken = (String) responseJson.get(OAuth2Constants.REFRESH_TOKEN);
|
||||
}
|
||||
|
@ -1017,6 +1024,11 @@ public class OAuthClient {
|
|||
public String getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
// OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
|
||||
public PublicKey getRealmPublicKey(String realm) {
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
package org.keycloak.testsuite.oauth;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
|
||||
//OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
|
||||
public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
|
||||
|
||||
@Override
|
||||
public void beforeAbstractKeycloakTest() throws Exception {
|
||||
super.beforeAbstractKeycloakTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
|
||||
testRealms.add(realm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyNoScopeTest() throws Exception {
|
||||
String loginUser = "john-doh@localhost";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String expectedScope = "";
|
||||
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifySingleScopeAsRealmRoleTest() throws Exception {
|
||||
String loginUser = "john-doh@localhost";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "user";
|
||||
String expectedScope = requestedScope;
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyMultipleScopeAsRealmRoleTest() throws Exception {
|
||||
String loginUser = "rich.roles@redhat.com";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "user realm-composite-role";
|
||||
String expectedScope = requestedScope;
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyNotAssignedScopeAsRealmRoleTest() throws Exception {
|
||||
String loginUser = "john-doh@localhost";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "user realm-composite-role";
|
||||
String expectedScope = "user";
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifySingleScopeAsClientRoleTest() throws Exception {
|
||||
String loginUser = "john-doh@localhost";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "test-app/customer-user";
|
||||
String expectedScope = requestedScope;
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyMultipleScopeAsClientRoleTest() throws Exception {
|
||||
String loginUser = "rich.roles@redhat.com";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "test-app-scope/test-app-disallowed-by-scope test-app-scope/test-app-allowed-by-scope";
|
||||
String expectedScope = requestedScope;
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyNotAssignedScopeAsClientRoleTest() throws Exception {
|
||||
String loginUser = "rich.roles@redhat.com";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "test-app-scope/test-app-unspecified-by-scope test-app-scope/test-app-allowed-by-scope";
|
||||
String expectedScope = "test-app-scope/test-app-allowed-by-scope";
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyMultipleScopeAsRealmAndClientRoleTest() throws Exception {
|
||||
String loginUser = "rich.roles@redhat.com";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "test-app-scope/test-app-disallowed-by-scope admin test-app/customer-user test-app-scope/test-app-allowed-by-scope";
|
||||
String expectedScope = requestedScope;
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyNotAssignedScopeAsRealmAndClientRoleTest() throws Exception {
|
||||
String loginUser = "john-doh@localhost";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "test-app/customer-user test-app-scope/test-app-disallowed-by-scope admin test-app/customer-user user test-app-scope/test-app-allowed-by-scope";
|
||||
String expectedScope = "user test-app/customer-user";
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specifyDuplicatedScopeAsRealmAndClientRoleTest() throws Exception {
|
||||
String loginUser = "john-doh@localhost";
|
||||
String loginPassword = "password";
|
||||
String clientSecret = "password";
|
||||
|
||||
String requestedScope = "test-app/customer-user user user test-app/customer-user";
|
||||
String expectedScope = "user test-app/customer-user";
|
||||
|
||||
oauth.scope(requestedScope);
|
||||
oauth.doLogin(loginUser, loginPassword);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
|
||||
}
|
||||
|
||||
private void expectSuccessfulResponseFromTokenEndpoint(String code, String expectedScope, String clientSecret) throws Exception {
|
||||
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, clientSecret);
|
||||
assertEquals(200, response.getStatusCode());
|
||||
log.info("expectedScopes = " + expectedScope);
|
||||
log.info("receivedScopes = " + response.getScope());
|
||||
Collection<String> expectedScopes = Arrays.asList(expectedScope.split(" "));
|
||||
Collection<String> receivedScopes = Arrays.asList(response.getScope().split(" "));
|
||||
Assert.assertTrue(expectedScopes.containsAll(receivedScopes) && receivedScopes.containsAll(expectedScopes));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue