OIDC Financial API Read Only Profile : scope MUST be returned in the

response from Token Endpoint
This commit is contained in:
Takashi Norimatsu 2017-10-04 12:59:49 +09:00
parent f0f7321c38
commit 6f6a467c7b
4 changed files with 280 additions and 0 deletions

View file

@ -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;

View file

@ -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 {

View file

@ -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) {

View file

@ -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));
}
}