KEYCLOAK-15270 Account REST API doesn't verify audience
This commit is contained in:
parent
b62d68a591
commit
a9a719b88c
13 changed files with 225 additions and 53 deletions
|
@ -15,7 +15,7 @@ public class ExampleRestResource {
|
|||
|
||||
public ExampleRestResource(KeycloakSession session) {
|
||||
this.session = session;
|
||||
this.auth = new AppAuthManager().authenticateBearerToken(session, session.getContext().getRealm());
|
||||
this.auth = new AppAuthManager.BearerTokenAuthenticator(session).authenticate();
|
||||
}
|
||||
|
||||
@Path("companies")
|
||||
|
|
|
@ -30,9 +30,7 @@ import org.keycloak.services.managers.AuthenticationManager.AuthResult;
|
|||
public class Tokens {
|
||||
|
||||
public static AccessToken getAccessToken(KeycloakSession keycloakSession) {
|
||||
AppAuthManager authManager = new AppAuthManager();
|
||||
KeycloakContext context = keycloakSession.getContext();
|
||||
AuthResult authResult = authManager.authenticateBearerToken(keycloakSession, context.getRealm(), context.getUri(), context.getConnection(), context.getRequestHeaders());
|
||||
AuthResult authResult = new AppAuthManager.BearerTokenAuthenticator(keycloakSession).authenticate();
|
||||
|
||||
if (authResult != null) {
|
||||
return authResult.getToken();
|
||||
|
@ -42,9 +40,9 @@ public class Tokens {
|
|||
}
|
||||
|
||||
public static AccessToken getAccessToken(String accessToken, KeycloakSession keycloakSession) {
|
||||
AppAuthManager authManager = new AppAuthManager();
|
||||
KeycloakContext context = keycloakSession.getContext();
|
||||
AuthResult authResult = authManager.authenticateBearerToken(accessToken, keycloakSession, context.getRealm(), context.getUri(), context.getConnection(), context.getRequestHeaders());
|
||||
AuthResult authResult = new AppAuthManager.BearerTokenAuthenticator(keycloakSession)
|
||||
.setTokenString(accessToken)
|
||||
.authenticate();
|
||||
|
||||
if (authResult != null) {
|
||||
return authResult.getToken();
|
||||
|
|
|
@ -84,7 +84,6 @@ import org.keycloak.services.CorsErrorResponseException;
|
|||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
import org.keycloak.services.clientpolicy.DefaultClientPolicyManager;
|
||||
import org.keycloak.services.clientpolicy.TokenRefreshContext;
|
||||
import org.keycloak.services.clientpolicy.TokenRequestContext;
|
||||
import org.keycloak.services.managers.AppAuthManager;
|
||||
|
@ -797,7 +796,7 @@ public class TokenEndpoint {
|
|||
|
||||
}
|
||||
|
||||
AuthenticationManager.AuthResult authResult = AuthenticationManager.verifyIdentityToken(session, realm, session.getContext().getUri(), clientConnection, true, true, false, subjectToken, headers);
|
||||
AuthenticationManager.AuthResult authResult = AuthenticationManager.verifyIdentityToken(session, realm, session.getContext().getUri(), clientConnection, true, true, null, false, subjectToken, headers);
|
||||
if (authResult == null) {
|
||||
event.detail(Details.REASON, "subject_token validation failure");
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
|
|
|
@ -53,7 +53,7 @@ public class AppAuthManager extends AuthenticationManager {
|
|||
*
|
||||
* @return the token string or {@literal null}
|
||||
*/
|
||||
private String extractTokenStringFromAuthHeader(String authHeader) {
|
||||
private static String extractTokenStringFromAuthHeader(String authHeader) {
|
||||
|
||||
if (authHeader == null) {
|
||||
return null;
|
||||
|
@ -83,7 +83,7 @@ public class AppAuthManager extends AuthenticationManager {
|
|||
* @param headers
|
||||
* @return the token string or {@literal null} if the Authorization header is not of type Bearer, or the token string is missing.
|
||||
*/
|
||||
public String extractAuthorizationHeaderTokenOrReturnNull(HttpHeaders headers) {
|
||||
public static String extractAuthorizationHeaderTokenOrReturnNull(HttpHeaders headers) {
|
||||
String authHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
return extractTokenStringFromAuthHeader(authHeader);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public class AppAuthManager extends AuthenticationManager {
|
|||
* @return the token string or {@literal null} of the Authorization header is missing
|
||||
* @throws NotAuthorizedException if the Authorization header is not of type Bearer, or the token string is missing.
|
||||
*/
|
||||
public String extractAuthorizationHeaderToken(HttpHeaders headers) {
|
||||
public static String extractAuthorizationHeaderToken(HttpHeaders headers) {
|
||||
String authHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
if (authHeader == null) {
|
||||
return null;
|
||||
|
@ -107,23 +107,65 @@ public class AppAuthManager extends AuthenticationManager {
|
|||
return tokenString;
|
||||
}
|
||||
|
||||
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm) {
|
||||
KeycloakContext ctx = session.getContext();
|
||||
return authenticateBearerToken(session, realm, ctx.getUri(), ctx.getConnection(), ctx.getRequestHeaders());
|
||||
}
|
||||
public static class BearerTokenAuthenticator {
|
||||
private KeycloakSession session;
|
||||
private RealmModel realm;
|
||||
private UriInfo uriInfo;
|
||||
private ClientConnection connection;
|
||||
private HttpHeaders headers;
|
||||
private String tokenString;
|
||||
private String audience;
|
||||
|
||||
public AuthResult authenticateBearerToken(KeycloakSession session) {
|
||||
return authenticateBearerToken(session, session.getContext().getRealm(), session.getContext().getUri(), session.getContext().getConnection(), session.getContext().getRequestHeaders());
|
||||
}
|
||||
public BearerTokenAuthenticator(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
||||
return authenticateBearerToken(extractAuthorizationHeaderToken(headers), session, realm, uriInfo, connection, headers);
|
||||
}
|
||||
public BearerTokenAuthenticator setSession(KeycloakSession session) {
|
||||
this.session = session;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthResult authenticateBearerToken(String tokenString, KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
||||
if (tokenString == null) return null;
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, false, tokenString, headers);
|
||||
return authResult;
|
||||
public BearerTokenAuthenticator setRealm(RealmModel realm) {
|
||||
this.realm = realm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BearerTokenAuthenticator setUriInfo(UriInfo uriInfo) {
|
||||
this.uriInfo = uriInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BearerTokenAuthenticator setConnection(ClientConnection connection) {
|
||||
this.connection = connection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BearerTokenAuthenticator setHeaders(HttpHeaders headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BearerTokenAuthenticator setTokenString(String tokenString) {
|
||||
this.tokenString = tokenString;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BearerTokenAuthenticator setAudience(String audience) {
|
||||
this.audience = audience;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthResult authenticate() {
|
||||
KeycloakContext ctx = session.getContext();
|
||||
if (realm == null) realm = ctx.getRealm();
|
||||
if (uriInfo == null) uriInfo = ctx.getUri();
|
||||
if (connection == null) connection = ctx.getConnection();
|
||||
if (headers == null) headers = ctx.getRequestHeaders();
|
||||
if (tokenString == null) tokenString = extractAuthorizationHeaderToken(headers);
|
||||
// audience can be null
|
||||
|
||||
return verifyIdentityToken(session, realm, uriInfo, connection, true, true, audience, false, tokenString, headers);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -779,7 +779,7 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
String tokenString = cookie.getValue();
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, true, tokenString, session.getContext().getRequestHeaders(), VALIDATE_IDENTITY_COOKIE);
|
||||
AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, null, true, tokenString, session.getContext().getRequestHeaders(), VALIDATE_IDENTITY_COOKIE);
|
||||
if (authResult == null) {
|
||||
expireIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
|
||||
expireOldIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
|
||||
|
@ -1261,7 +1261,7 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
public static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, boolean checkTokenType,
|
||||
boolean isCookie, String tokenString, HttpHeaders headers, Predicate<? super AccessToken>... additionalChecks) {
|
||||
String checkAudience, boolean isCookie, String tokenString, HttpHeaders headers, Predicate<? super AccessToken>... additionalChecks) {
|
||||
try {
|
||||
TokenVerifier<AccessToken> verifier = TokenVerifier.create(tokenString, AccessToken.class)
|
||||
.withDefaultChecks()
|
||||
|
@ -1269,6 +1269,11 @@ public class AuthenticationManager {
|
|||
.checkActive(checkActive)
|
||||
.checkTokenType(checkTokenType)
|
||||
.withChecks(additionalChecks);
|
||||
|
||||
if (checkAudience != null) {
|
||||
verifier.audience(checkAudience);
|
||||
}
|
||||
|
||||
String kid = verifier.getHeader().getKeyId();
|
||||
String algorithm = verifier.getHeader().getAlgorithm().name();
|
||||
|
||||
|
|
|
@ -448,8 +448,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
this.event.event(EventType.IDENTITY_PROVIDER_RETRIEVE_TOKEN);
|
||||
|
||||
try {
|
||||
AppAuthManager authManager = new AppAuthManager();
|
||||
AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(this.session, this.realmModel, this.session.getContext().getUri(), this.clientConnection, this.request.getHttpHeaders());
|
||||
AuthenticationManager.AuthResult authResult = new AppAuthManager.BearerTokenAuthenticator(session)
|
||||
.setRealm(realmModel)
|
||||
.setConnection(clientConnection)
|
||||
.setHeaders(request.getHttpHeaders())
|
||||
.authenticate();
|
||||
|
||||
if (authResult != null) {
|
||||
AccessToken token = authResult.getToken();
|
||||
|
|
|
@ -114,7 +114,10 @@ public class AccountLoader {
|
|||
}
|
||||
|
||||
private AccountRestService getAccountRestService(ClientModel client, String versionStr) {
|
||||
AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session);
|
||||
AuthenticationManager.AuthResult authResult = new AppAuthManager.BearerTokenAuthenticator(session)
|
||||
.setAudience(client.getClientId())
|
||||
.authenticate();
|
||||
|
||||
if (authResult == null) {
|
||||
throw new NotAuthorizedException("Bearer token required");
|
||||
}
|
||||
|
|
|
@ -87,12 +87,10 @@ public class AdminConsole {
|
|||
@Context
|
||||
protected Providers providers;
|
||||
|
||||
protected AppAuthManager authManager;
|
||||
protected RealmModel realm;
|
||||
|
||||
public AdminConsole(RealmModel realm) {
|
||||
this.realm = realm;
|
||||
this.authManager = new AppAuthManager();
|
||||
}
|
||||
|
||||
public static class WhoAmI {
|
||||
|
@ -195,7 +193,12 @@ public class AdminConsole {
|
|||
@NoCache
|
||||
public Response whoAmI(final @Context HttpHeaders headers) {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm, session.getContext().getUri(), clientConnection, headers);
|
||||
AuthenticationManager.AuthResult authResult = new AppAuthManager.BearerTokenAuthenticator(session)
|
||||
.setRealm(realm)
|
||||
.setConnection(clientConnection)
|
||||
.setHeaders(headers)
|
||||
.authenticate();
|
||||
|
||||
if (authResult == null) {
|
||||
return Response.status(401).build();
|
||||
}
|
||||
|
|
|
@ -72,7 +72,6 @@ public class AdminRoot {
|
|||
@Context
|
||||
protected HttpResponse response;
|
||||
|
||||
protected AppAuthManager authManager;
|
||||
protected TokenManager tokenManager;
|
||||
|
||||
@Context
|
||||
|
@ -80,7 +79,6 @@ public class AdminRoot {
|
|||
|
||||
public AdminRoot() {
|
||||
this.tokenManager = new TokenManager();
|
||||
this.authManager = new AppAuthManager();
|
||||
}
|
||||
|
||||
public static UriBuilder adminBaseUrl(UriInfo uriInfo) {
|
||||
|
@ -153,7 +151,7 @@ public class AdminRoot {
|
|||
|
||||
|
||||
protected AdminAuth authenticateRealmAdminRequest(HttpHeaders headers) {
|
||||
String tokenString = authManager.extractAuthorizationHeaderToken(headers);
|
||||
String tokenString = AppAuthManager.extractAuthorizationHeaderToken(headers);
|
||||
if (tokenString == null) throw new NotAuthorizedException("Bearer");
|
||||
AccessToken token;
|
||||
try {
|
||||
|
@ -169,7 +167,13 @@ public class AdminRoot {
|
|||
throw new NotAuthorizedException("Unknown realm in token");
|
||||
}
|
||||
session.getContext().setRealm(realm);
|
||||
AuthenticationManager.AuthResult authResult = authManager.authenticateBearerToken(session, realm, session.getContext().getUri(), clientConnection, headers);
|
||||
|
||||
AuthenticationManager.AuthResult authResult = new AppAuthManager.BearerTokenAuthenticator(session)
|
||||
.setRealm(realm)
|
||||
.setConnection(clientConnection)
|
||||
.setHeaders(headers)
|
||||
.authenticate();
|
||||
|
||||
if (authResult == null) {
|
||||
logger.debug("Token not valid");
|
||||
throw new NotAuthorizedException("Bearer");
|
||||
|
|
|
@ -32,7 +32,7 @@ public class ExampleRestResource {
|
|||
|
||||
public ExampleRestResource(KeycloakSession session) {
|
||||
this.session = session;
|
||||
this.auth = new AppAuthManager().authenticateBearerToken(session, session.getContext().getRealm());
|
||||
this.auth = new AppAuthManager.BearerTokenAuthenticator(session).authenticate();
|
||||
}
|
||||
|
||||
@Path("companies")
|
||||
|
|
|
@ -1203,7 +1203,7 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
|
||||
Assert.assertThat(apps.keySet(), containsInAnyOrder(
|
||||
/* "root-url-client", */ "Account", "Account Console", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant"));
|
||||
/* "root-url-client", */ "Account", "Account Console", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant", "custom-audience"));
|
||||
|
||||
rsu.add(testRealm().roles().get("user").toRepresentation())
|
||||
.update();
|
||||
|
@ -1211,7 +1211,7 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
driver.navigate().refresh();
|
||||
apps = applicationsPage.getApplications();
|
||||
Assert.assertThat(apps.keySet(), containsInAnyOrder(
|
||||
"root-url-client", "Account", "Account Console", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant"));
|
||||
"root-url-client", "Account", "Account Console", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant", "custom-audience"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1230,7 +1230,7 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
|
||||
Assert.assertThat(apps.keySet(), containsInAnyOrder(
|
||||
"root-url-client", "Account", "Account Console", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant"));
|
||||
"root-url-client", "Account", "Account Console", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant", "custom-audience"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1245,7 +1245,7 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
applicationsPage.assertCurrent();
|
||||
|
||||
Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
|
||||
Assert.assertThat(apps.keySet(), containsInAnyOrder("root-url-client", "Account", "Account Console", "Broker", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant"));
|
||||
Assert.assertThat(apps.keySet(), containsInAnyOrder("root-url-client", "Account", "Account Console", "Broker", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant", "custom-audience"));
|
||||
|
||||
AccountApplicationsPage.AppEntry accountEntry = apps.get("Account");
|
||||
Assert.assertThat(accountEntry.getRolesAvailable(), containsInAnyOrder(
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
|||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
|
||||
|
@ -73,6 +74,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
|
||||
|
@ -294,7 +296,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
null, UserModel.RequiredAction.UPDATE_PASSWORD.toString(), false, 1);
|
||||
|
||||
CredentialRepresentation password1 = password.getUserCredentials().get(0);
|
||||
Assert.assertNull(password1.getSecretData());
|
||||
assertNull(password1.getSecretData());
|
||||
Assert.assertNotNull(password1.getCredentialData());
|
||||
|
||||
AccountCredentialResource.CredentialContainer otp = credentials.get(1);
|
||||
|
@ -341,7 +343,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
Assert.assertEquals(1, credentials.size());
|
||||
password = credentials.get(0);
|
||||
Assert.assertEquals(PasswordCredentialModel.TYPE, password.getType());
|
||||
Assert.assertNull(password.getUserCredentials());
|
||||
assertNull(password.getUserCredentials());
|
||||
}
|
||||
|
||||
|
||||
|
@ -452,8 +454,8 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
credentials = getCredentials();
|
||||
assertExpectedCredentialTypes(credentials, PasswordCredentialModel.TYPE, OTPCredentialModel.TYPE);
|
||||
AccountCredentialResource.CredentialContainer otpCredential = credentials.get(1);
|
||||
Assert.assertNull(otpCredential.getCreateAction());
|
||||
Assert.assertNull(otpCredential.getUpdateAction());
|
||||
assertNull(otpCredential.getCreateAction());
|
||||
assertNull(otpCredential.getUpdateAction());
|
||||
|
||||
// Revert - re-enable requiredAction and remove OTP credential from the user
|
||||
setRequiredActionEnabledStatus(UserModel.RequiredAction.CONFIGURE_TOTP.name(), true);
|
||||
|
@ -578,7 +580,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
public void listApplications() throws Exception {
|
||||
oauth.clientId("in-use-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "view-applications-access", "password");
|
||||
Assert.assertNull(tokenResponse.getErrorDescription());
|
||||
assertNull(tokenResponse.getErrorDescription());
|
||||
|
||||
TokenUtil token = new TokenUtil("view-applications-access", "password");
|
||||
List<ClientRepresentation> applications = SimpleHttp
|
||||
|
@ -600,7 +602,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
public void listApplicationsFiltered() throws Exception {
|
||||
oauth.clientId("in-use-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "view-applications-access", "password");
|
||||
Assert.assertNull(tokenResponse.getErrorDescription());
|
||||
assertNull(tokenResponse.getErrorDescription());
|
||||
|
||||
TokenUtil token = new TokenUtil("view-applications-access", "password");
|
||||
List<ClientRepresentation> applications = SimpleHttp
|
||||
|
@ -623,7 +625,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.clientId("offline-client");
|
||||
OAuthClient.AccessTokenResponse offlineTokenResponse = oauth.doGrantAccessTokenRequest("secret1", "view-applications-access", "password");
|
||||
Assert.assertNull(offlineTokenResponse.getErrorDescription());
|
||||
assertNull(offlineTokenResponse.getErrorDescription());
|
||||
|
||||
TokenUtil token = new TokenUtil("view-applications-access", "password");
|
||||
List<ClientRepresentation> applications = SimpleHttp
|
||||
|
@ -687,7 +689,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
public void listApplicationsWithRootUrl() throws Exception {
|
||||
oauth.clientId("root-url-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "view-applications-access", "password");
|
||||
Assert.assertNull(tokenResponse.getErrorDescription());
|
||||
assertNull(tokenResponse.getErrorDescription());
|
||||
|
||||
TokenUtil token = new TokenUtil("view-applications-access", "password");
|
||||
List<ClientRepresentation> applications = SimpleHttp
|
||||
|
@ -1100,7 +1102,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.clientId("offline-client");
|
||||
OAuthClient.AccessTokenResponse offlineTokenResponse = oauth.doGrantAccessTokenRequest("secret1", "view-applications-access", "password");
|
||||
Assert.assertNull(offlineTokenResponse.getErrorDescription());
|
||||
assertNull(offlineTokenResponse.getErrorDescription());
|
||||
|
||||
TokenUtil token = new TokenUtil("view-applications-access", "password");
|
||||
|
||||
|
@ -1142,4 +1144,47 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
assertEquals("API version not found", response.asJson().get("error").textValue());
|
||||
assertEquals(404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAudience() throws Exception {
|
||||
oauth.clientId("custom-audience");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
assertNull(tokenResponse.getErrorDescription());
|
||||
|
||||
SimpleHttp.Response response = SimpleHttp.doGet(getAccountUrl(null), httpClient)
|
||||
.auth(tokenResponse.getAccessToken())
|
||||
.header("Accept", "application/json")
|
||||
.asResponse();
|
||||
assertEquals(401, response.getStatus());
|
||||
|
||||
// update to correct audience
|
||||
org.keycloak.representations.idm.ClientRepresentation clientRep = testRealm().clients().findByClientId("custom-audience").get(0);
|
||||
ProtocolMapperRepresentation mapperRep = clientRep.getProtocolMappers().stream().filter(m -> m.getName().equals("aud")).findFirst().orElse(null);
|
||||
assertNotNull("Audience mapper not found", mapperRep);
|
||||
mapperRep.getConfig().put("included.custom.audience", "account");
|
||||
testRealm().clients().get(clientRep.getId()).getProtocolMappers().update(mapperRep.getId(), mapperRep);
|
||||
|
||||
tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
assertNull(tokenResponse.getErrorDescription());
|
||||
|
||||
response = SimpleHttp.doGet(getAccountUrl(null), httpClient)
|
||||
.auth(tokenResponse.getAccessToken())
|
||||
.header("Accept", "application/json")
|
||||
.asResponse();
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
// remove audience completely
|
||||
testRealm().clients().get(clientRep.getId()).getProtocolMappers().delete(mapperRep.getId());
|
||||
|
||||
tokenResponse = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
assertNull(tokenResponse.getErrorDescription());
|
||||
|
||||
response = SimpleHttp.doGet(getAccountUrl(null), httpClient)
|
||||
.auth(tokenResponse.getAccessToken())
|
||||
.header("Accept", "application/json")
|
||||
.asResponse();
|
||||
assertEquals(401, response.getStatus());
|
||||
|
||||
// custom-audience client is used only in this test so no need to revert the changes
|
||||
}
|
||||
}
|
||||
|
|
|
@ -414,7 +414,77 @@
|
|||
"enabled": true,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"secret": "password",
|
||||
"webOrigins": [ "http://localtest.me:8180" ]
|
||||
"webOrigins": [ "http://localtest.me:8180" ],
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "aud-account",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-audience-mapper",
|
||||
"config": {
|
||||
"included.client.audience": "account",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "aud-admin",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-audience-mapper",
|
||||
"config": {
|
||||
"included.client.audience": "security-admin-console",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"clientId": "custom-audience",
|
||||
"enabled": true,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"secret": "password",
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "aud",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-audience-mapper",
|
||||
"config": {
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"included.custom.audience": "foo-bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "client roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-client-role-mapper",
|
||||
"config": {
|
||||
"user.attribute": "foo",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "resource_access.${client_id}.roles",
|
||||
"jsonType.label": "String",
|
||||
"multivalued": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "realm roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-realm-role-mapper",
|
||||
"config": {
|
||||
"user.attribute": "foo",
|
||||
"access.token.claim": "true",
|
||||
"claim.name": "realm_access.roles",
|
||||
"jsonType.label": "String",
|
||||
"multivalued": "true"
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultClientScopes": [
|
||||
"web-origins",
|
||||
"profile",
|
||||
"email"
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
|
|
Loading…
Reference in a new issue