mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Better events for jwt-bearer and check all details in the tests
CLoses #44137 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
2210b1ed50
commit
d0e4d1f620
@ -39,7 +39,9 @@ public interface Details {
|
||||
String AUTH_TYPE = "auth_type";
|
||||
String AUTH_METHOD = "auth_method";
|
||||
String IDENTITY_PROVIDER = "identity_provider";
|
||||
String IDENTITY_PROVIDER_ISSUER = "identity_provider_issuer";
|
||||
String IDENTITY_PROVIDER_USERNAME = "identity_provider_identity";
|
||||
String IDENTITY_PROVIDER_USER_ID = "identity_provider_user_id";
|
||||
String IDENTITY_PROVIDER_BROKER_SESSION_ID = "identity_provider_broker_session_id";
|
||||
String REGISTER_METHOD = "register_method";
|
||||
String USERNAME = "username";
|
||||
|
||||
@ -58,6 +58,8 @@ public class JWTAuthorizationGrantType extends OAuth2GrantTypeBase {
|
||||
|
||||
JWTAuthorizationGrantValidator authorizationGrantContext = JWTAuthorizationGrantValidator.createValidator(
|
||||
context.getSession(), client, assertion, formParams.getFirst(OAuth2Constants.SCOPE));
|
||||
event.detail(Details.IDENTITY_PROVIDER_ISSUER, authorizationGrantContext.getIssuer());
|
||||
event.detail(Details.IDENTITY_PROVIDER_USER_ID, authorizationGrantContext.getSubject());
|
||||
|
||||
//client must be confidential
|
||||
authorizationGrantContext.validateClient();
|
||||
@ -73,6 +75,7 @@ public class JWTAuthorizationGrantType extends OAuth2GrantTypeBase {
|
||||
if (identityProviderModel == null) {
|
||||
throw new RuntimeException("No Identity Provider for provided issuer");
|
||||
}
|
||||
event.detail(Details.IDENTITY_PROVIDER, identityProviderModel.getAlias());
|
||||
|
||||
if(!OIDCAdvancedConfigWrapper.fromClientModel(context.getClient()).getJWTAuthorizationGrantAllowedIdentityProviders().contains(identityProviderModel.getAlias())) {
|
||||
throw new RuntimeException("Identity Provider is not allowed for the client");
|
||||
|
||||
@ -42,6 +42,16 @@ public class EventAssertion {
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventAssertion sessionId(String sessionId) {
|
||||
Assertions.assertEquals(sessionId, event.getSessionId());
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventAssertion userId(String userId) {
|
||||
Assertions.assertEquals(userId, event.getUserId());
|
||||
return this;
|
||||
}
|
||||
|
||||
public EventAssertion details(String key, String value) {
|
||||
if (value != null) {
|
||||
MatcherAssert.assertThat(event.getDetails(), Matchers.hasEntry(key, value));
|
||||
|
||||
@ -6,9 +6,11 @@ import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.testframework.events.EventAssertion;
|
||||
import org.keycloak.testframework.oauth.OAuthIdentityProvider;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
|
||||
@ -83,7 +85,7 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER, Time.currentTime() + 5L));
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
|
||||
//test with iat
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER, Time.currentTime() + 20L, (long) Time.currentTime()));
|
||||
@ -93,7 +95,7 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER, Time.currentTime() + 20L, (long) Time.currentTime()));
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -109,12 +111,12 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
// Issuer as audience works
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER));
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
|
||||
// Token endpoint as audience works
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getToken(), IDP_ISSUER));
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
|
||||
// Introspection endpoint as audience does not work
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIntrospection(), IDP_ISSUER));
|
||||
@ -144,7 +146,8 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), "fake-issuer", Time.currentTime() + 300L));
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertFailure("No Identity Provider for provided issuer", response, events.poll());
|
||||
EventAssertion event = assertFailure("No Identity Provider for provided issuer", response, events.poll());
|
||||
event.details(Details.IDENTITY_PROVIDER_ISSUER, "fake-issuer");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -155,14 +158,17 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
|
||||
jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("fake-user", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER, Time.currentTime() + 300L));
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertFailure("User not found", response, events.poll());
|
||||
EventAssertion event = assertFailure("User not found", response, events.poll());
|
||||
event.details(Details.IDENTITY_PROVIDER, IDP_ALIAS);
|
||||
event.details(Details.IDENTITY_PROVIDER_ISSUER, IDP_ISSUER);
|
||||
event.details(Details.IDENTITY_PROVIDER_USER_ID, "fake-user");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplayToken() {
|
||||
String jwt = getIdentityProvider().encodeToken(createDefaultAuthorizationGrantToken());
|
||||
AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertFailure("Token reuse detected", response, events.poll());
|
||||
@ -173,10 +179,10 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
|
||||
jwt = getIdentityProvider().encodeToken(createDefaultAuthorizationGrantToken());
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
|
||||
response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -186,7 +192,7 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
});
|
||||
String jwt = getIdentityProvider().encodeToken(createDefaultAuthorizationGrantToken());
|
||||
AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
|
||||
realm.updateIdentityProviderWithCleanup(IDP_ALIAS, rep -> {
|
||||
rep.getConfig().put(OIDCIdentityProviderConfig.JWT_AUTHORIZATION_GRANT_ASSERTION_SIGNATURE_ALG, Algorithm.ES512);
|
||||
@ -213,7 +219,7 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
try {
|
||||
String jwt = getIdentityProvider().encodeToken(createDefaultAuthorizationGrantToken());
|
||||
AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
AccessToken token = assertSuccess("test-app", "basic-user", response);
|
||||
AccessToken token = assertSuccess("test-app", response);
|
||||
MatcherAssert.assertThat(List.of(token.getScope().split(" ")), Matchers.containsInAnyOrder(new String[]{"email", "profile", "address", "phone"}));
|
||||
|
||||
jwt = getIdentityProvider().encodeToken(createDefaultAuthorizationGrantToken());
|
||||
@ -229,6 +235,6 @@ public abstract class AbstractJWTAuthorizationGrantTest extends BaseAbstractJWTA
|
||||
public void testSuccessGrant() {
|
||||
String jwt = getIdentityProvider().encodeToken(createDefaultAuthorizationGrantToken());
|
||||
AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertSuccess("test-app", "basic-user", response);
|
||||
assertSuccess("test-app", response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ package org.keycloak.tests.oauth;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Details;
|
||||
@ -74,7 +75,7 @@ public class BaseAbstractJWTAuthorizationGrantTest {
|
||||
protected ManagedRealm realm;
|
||||
|
||||
@InjectUser(config = AbstractJWTAuthorizationGrantTest.FederatedUserConfiguration.class)
|
||||
ManagedUser user;
|
||||
protected ManagedUser user;
|
||||
|
||||
@InjectOAuthClient
|
||||
OAuthClient oAuthClient;
|
||||
@ -161,47 +162,58 @@ public class BaseAbstractJWTAuthorizationGrantTest {
|
||||
}
|
||||
}
|
||||
|
||||
protected AccessToken assertSuccess(String expectedClientId, String username, AccessTokenResponse response) {
|
||||
protected AccessToken assertSuccess(String expectedClientId, AccessTokenResponse response) {
|
||||
Assertions.assertTrue(response.isSuccess());
|
||||
Assertions.assertNull(response.getRefreshToken());
|
||||
AccessToken accessToken = oAuthClient.parseToken(response.getAccessToken(), AccessToken.class);
|
||||
Assertions.assertNull(accessToken.getSessionId());
|
||||
MatcherAssert.assertThat(accessToken.getId(), Matchers.startsWith("trrtag:"));
|
||||
Assertions.assertEquals(expectedClientId, accessToken.getIssuedFor());
|
||||
Assertions.assertEquals(username, accessToken.getPreferredUsername());
|
||||
Assertions.assertEquals(user.getUsername(), accessToken.getPreferredUsername());
|
||||
EventAssertion.assertSuccess(events.poll())
|
||||
.type(EventType.LOGIN)
|
||||
.clientId(expectedClientId)
|
||||
.details("grant_type", OAuth2Constants.JWT_AUTHORIZATION_GRANT)
|
||||
.details("username", username);
|
||||
.sessionId(null)
|
||||
.userId(user.getId())
|
||||
.details(Details.GRANT_TYPE, OAuth2Constants.JWT_AUTHORIZATION_GRANT)
|
||||
.details(Details.IDENTITY_PROVIDER, IDP_ALIAS)
|
||||
.details(Details.IDENTITY_PROVIDER_ISSUER, IDP_ISSUER)
|
||||
.details(Details.IDENTITY_PROVIDER_USER_ID, "basic-user-id")
|
||||
.details(Details.USERNAME, user.getUsername());
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
protected void assertFailure(String expectedErrorDescription, AccessTokenResponse response, EventRepresentation event) {
|
||||
assertFailure("invalid_grant", expectedErrorDescription, response, event);
|
||||
protected EventAssertion assertFailure(String expectedErrorDescription, AccessTokenResponse response, EventRepresentation event) {
|
||||
return assertFailure(OAuthErrorException.INVALID_GRANT, expectedErrorDescription, response, event);
|
||||
}
|
||||
|
||||
protected void assertFailure(String expectedError, String expectedErrorDescription, AccessTokenResponse response, EventRepresentation event) {
|
||||
protected EventAssertion assertFailure(String expectedError, String expectedErrorDescription, AccessTokenResponse response, EventRepresentation event) {
|
||||
Assertions.assertFalse(response.isSuccess());
|
||||
Assertions.assertEquals(expectedError, response.getError());
|
||||
Assertions.assertEquals(expectedErrorDescription, response.getErrorDescription());
|
||||
EventAssertion.assertError(event)
|
||||
return EventAssertion.assertError(event)
|
||||
.type(EventType.LOGIN_ERROR)
|
||||
.error("invalid_request")
|
||||
.details("grant_type", OAuth2Constants.JWT_AUTHORIZATION_GRANT)
|
||||
.details("reason", expectedErrorDescription);
|
||||
.sessionId(null)
|
||||
.error(OAuthErrorException.INVALID_REQUEST)
|
||||
.details(Details.GRANT_TYPE, OAuth2Constants.JWT_AUTHORIZATION_GRANT)
|
||||
.details(Details.REASON, expectedErrorDescription);
|
||||
}
|
||||
|
||||
protected void assertFailurePolicy(String expectedError, String expectedErrorDescription, AccessTokenResponse response, EventRepresentation event) {
|
||||
protected EventAssertion assertFailurePolicy(String expectedError, String expectedErrorDescription, AccessTokenResponse response, EventRepresentation event) {
|
||||
Assertions.assertFalse(response.isSuccess());
|
||||
Assertions.assertEquals(expectedError, response.getError());
|
||||
Assertions.assertEquals(expectedErrorDescription, response.getErrorDescription());
|
||||
EventAssertion.assertError(event)
|
||||
return EventAssertion.assertError(event)
|
||||
.type(EventType.LOGIN_ERROR)
|
||||
.sessionId(null)
|
||||
.userId(user.getId())
|
||||
.error(expectedError)
|
||||
.details("grant_type", OAuth2Constants.JWT_AUTHORIZATION_GRANT)
|
||||
.details("reason", Details.CLIENT_POLICY_ERROR)
|
||||
.details(Details.GRANT_TYPE, OAuth2Constants.JWT_AUTHORIZATION_GRANT)
|
||||
.details(Details.IDENTITY_PROVIDER_ISSUER, IDP_ISSUER)
|
||||
.details(Details.IDENTITY_PROVIDER_USER_ID, "basic-user-id")
|
||||
.details(Details.REASON, Details.CLIENT_POLICY_ERROR)
|
||||
.details(Details.CLIENT_POLICY_ERROR, expectedError)
|
||||
.details(Details.CLIENT_POLICY_ERROR_DETAIL, expectedErrorDescription);
|
||||
.details(Details.CLIENT_POLICY_ERROR_DETAIL, expectedErrorDescription)
|
||||
.details(Details.USERNAME, user.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,19 +33,19 @@ public class JWTAuthorizationGrantDownscopeClientPoliciesTest extends BaseAbstra
|
||||
// test with all the scopes
|
||||
String jwt = identityProvider.encodeToken(createDefaultAuthorizationGrantToken("email profile address"));
|
||||
AccessTokenResponse response = oAuthClient.openid(false).scope("address").jwtAuthorizationGrantRequest(jwt).send();
|
||||
AccessToken token = assertSuccess("test-app", "basic-user", response);
|
||||
AccessToken token = assertSuccess("test-app", response);
|
||||
MatcherAssert.assertThat(List.of(token.getScope().split(" ")), Matchers.containsInAnyOrder("email", "profile", "address"));
|
||||
|
||||
// test with less scopes => downscope
|
||||
jwt = identityProvider.encodeToken(createDefaultAuthorizationGrantToken("email profile address"));
|
||||
response = oAuthClient.openid(false).scope(null).jwtAuthorizationGrantRequest(jwt).send();
|
||||
token = assertSuccess("test-app", "basic-user", response);
|
||||
token = assertSuccess("test-app", response);
|
||||
MatcherAssert.assertThat(List.of(token.getScope().split(" ")), Matchers.containsInAnyOrder("email", "profile"));
|
||||
|
||||
// test default scopes are restricted if not present in initial token
|
||||
jwt = identityProvider.encodeToken(createDefaultAuthorizationGrantToken("profile address"));
|
||||
response = oAuthClient.openid(false).scope("address").jwtAuthorizationGrantRequest(jwt).send();
|
||||
token = assertSuccess("test-app", "basic-user", response);
|
||||
token = assertSuccess("test-app", response);
|
||||
MatcherAssert.assertThat(List.of(token.getScope().split(" ")), Matchers.containsInAnyOrder("profile", "address"));
|
||||
|
||||
// test requesting a valid optional scope for the client but not present initially
|
||||
|
||||
@ -2,9 +2,11 @@ package org.keycloak.tests.oauth;
|
||||
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.EventAssertion;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
@ -29,7 +31,10 @@ public class OIDCIdentityProviderJWTAuthorizationGrantTest extends AbstractJWTAu
|
||||
|
||||
String jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER));
|
||||
AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
|
||||
assertFailure("JWT Authorization Granted is not enabled for the identity provider", response, events.poll());
|
||||
EventAssertion event = assertFailure("JWT Authorization Granted is not enabled for the identity provider", response, events.poll());
|
||||
event.details(Details.IDENTITY_PROVIDER, IDP_ALIAS);
|
||||
event.details(Details.IDENTITY_PROVIDER_ISSUER, IDP_ISSUER);
|
||||
event.details(Details.IDENTITY_PROVIDER_USER_ID, "basic-user-id");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user