Search in sources :

Example 41 with JWSInput

use of org.keycloak.jose.jws.JWSInput in project keycloak by keycloak.

the class BrokerLinkAndTokenExchangeTest method testExternalExchange.

@Test
@UncaughtServerErrorExpected
public void testExternalExchange() throws Exception {
    RealmResource childRealm = adminClient.realms().realm(CHILD_IDP);
    String accessToken = oauth.doGrantAccessTokenRequest(PARENT_IDP, PARENT2_USERNAME, "password", null, PARENT_CLIENT, "password").getAccessToken();
    Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
    Client httpClient = AdminClientUtil.createResteasyClient();
    try {
        WebTarget exchangeUrl = childTokenExchangeWebTarget(httpClient);
        System.out.println("Exchange url: " + exchangeUrl.getUri().toString());
        checkFeature(200);
        IdentityProviderRepresentation rep = adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).toRepresentation();
        rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, String.valueOf(true));
        rep.getConfig().put(OIDCIdentityProviderConfig.USE_JWKS_URL, String.valueOf(true));
        rep.getConfig().put(OIDCIdentityProviderConfig.JWKS_URL, parentJwksUrl());
        String parentIssuer = UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT).path("/realms").path(PARENT_IDP).build().toString();
        rep.getConfig().put("issuer", parentIssuer);
        adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).update(rep);
        String exchangedUserId = null;
        String exchangedUsername = null;
        {
            // test signature validation
            Response response = exchangeUrl.request().header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password")).post(Entity.form(new Form().param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE).param(OAuth2Constants.SUBJECT_TOKEN, accessToken).param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE).param(OAuth2Constants.SUBJECT_ISSUER, PARENT_IDP).param(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)));
            Assert.assertEquals(200, response.getStatus());
            AccessTokenResponse tokenResponse = response.readEntity(AccessTokenResponse.class);
            String idToken = tokenResponse.getIdToken();
            JWSInput jws = new JWSInput(tokenResponse.getToken());
            AccessToken token = jws.readJsonContent(AccessToken.class);
            response.close();
            exchangedUserId = token.getSubject();
            exchangedUsername = token.getPreferredUsername();
            System.out.println("exchangedUserId: " + exchangedUserId);
            System.out.println("exchangedUsername: " + exchangedUsername);
            // test that we can exchange back to external token
            response = exchangeUrl.request().header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password")).post(Entity.form(new Form().param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE).param(OAuth2Constants.SUBJECT_TOKEN, tokenResponse.getToken()).param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE).param(OAuth2Constants.REQUESTED_ISSUER, PARENT_IDP)));
            Assert.assertEquals(200, response.getStatus());
            tokenResponse = response.readEntity(AccessTokenResponse.class);
            Assert.assertEquals(accessToken, tokenResponse.getToken());
            response.close();
            Assert.assertEquals(1, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
            // test logout
            response = childLogoutWebTarget(httpClient).queryParam("id_token_hint", idToken).request().get();
            response.close();
            Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
            List<FederatedIdentityRepresentation> links = childRealm.users().get(exchangedUserId).getFederatedIdentity();
            Assert.assertEquals(1, links.size());
        }
        {
            // check that we can request an exchange again and that the previously linked user is obtained
            Response response = exchangeUrl.request().header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password")).post(Entity.form(new Form().param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE).param(OAuth2Constants.SUBJECT_TOKEN, accessToken).param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE).param(OAuth2Constants.SUBJECT_ISSUER, PARENT_IDP).param(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)));
            Assert.assertEquals(200, response.getStatus());
            AccessTokenResponse tokenResponse = response.readEntity(AccessTokenResponse.class);
            String idToken = tokenResponse.getIdToken();
            JWSInput jws = new JWSInput(tokenResponse.getToken());
            AccessToken token = jws.readJsonContent(AccessToken.class);
            response.close();
            String exchanged2UserId = token.getSubject();
            String exchanged2Username = token.getPreferredUsername();
            // assert that we get the same linked account as was previously imported
            Assert.assertEquals(exchangedUserId, exchanged2UserId);
            Assert.assertEquals(exchangedUsername, exchanged2Username);
            // test logout
            response = childLogoutWebTarget(httpClient).queryParam("id_token_hint", idToken).request().get();
            response.close();
            Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
            List<FederatedIdentityRepresentation> links = childRealm.users().get(exchangedUserId).getFederatedIdentity();
            Assert.assertEquals(1, links.size());
        }
        {
            // check that we can exchange without specifying an SUBJECT_ISSUER
            Response response = exchangeUrl.request().header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password")).post(Entity.form(new Form().param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE).param(OAuth2Constants.SUBJECT_TOKEN, accessToken).param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE).param(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)));
            Assert.assertEquals(200, response.getStatus());
            AccessTokenResponse tokenResponse = response.readEntity(AccessTokenResponse.class);
            String idToken = tokenResponse.getIdToken();
            JWSInput jws = new JWSInput(tokenResponse.getToken());
            AccessToken token = jws.readJsonContent(AccessToken.class);
            response.close();
            String exchanged2UserId = token.getSubject();
            String exchanged2Username = token.getPreferredUsername();
            // assert that we get the same linked account as was previously imported
            Assert.assertEquals(exchangedUserId, exchanged2UserId);
            Assert.assertEquals(exchangedUsername, exchanged2Username);
            // test logout
            response = childLogoutWebTarget(httpClient).queryParam("id_token_hint", idToken).request().get();
            response.close();
            Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
            List<FederatedIdentityRepresentation> links = childRealm.users().get(exchangedUserId).getFederatedIdentity();
            Assert.assertEquals(1, links.size());
        }
        // cleanup  remove the user
        childRealm.users().get(exchangedUserId).remove();
        {
            // test unauthorized client gets 403
            Response response = exchangeUrl.request().header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(UNAUTHORIZED_CHILD_CLIENT, "password")).post(Entity.form(new Form().param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE).param(OAuth2Constants.SUBJECT_TOKEN, accessToken).param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE).param(OAuth2Constants.SUBJECT_ISSUER, PARENT_IDP)));
            Assert.assertEquals(403, response.getStatus());
        }
    } finally {
        httpClient.close();
    }
}
Also used : AccessTokenResponse(org.keycloak.representations.AccessTokenResponse) Response(javax.ws.rs.core.Response) Form(javax.ws.rs.core.Form) RealmResource(org.keycloak.admin.client.resource.RealmResource) AccessToken(org.keycloak.representations.AccessToken) IdentityProviderRepresentation(org.keycloak.representations.idm.IdentityProviderRepresentation) List(java.util.List) LinkedList(java.util.LinkedList) WebTarget(javax.ws.rs.client.WebTarget) JWSInput(org.keycloak.jose.jws.JWSInput) OAuthClient(org.keycloak.testsuite.util.OAuthClient) ApiUtil.createUserAndResetPasswordWithAdminClient(org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient) Client(javax.ws.rs.client.Client) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse) Test(org.junit.Test) AbstractServletsAdapterTest(org.keycloak.testsuite.adapter.AbstractServletsAdapterTest) UncaughtServerErrorExpected(org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected)

Example 42 with JWSInput

use of org.keycloak.jose.jws.JWSInput in project keycloak by keycloak.

the class AbstractOIDCResponseTypeTest method oidcFlow.

private void oidcFlow(String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
    EventRepresentation loginEvent = loginUser("abcdef123456");
    OAuthClient.AuthorizationEndpointResponse authzResponse = new OAuthClient.AuthorizationEndpointResponse(oauth, isFragment());
    Assert.assertNotNull(authzResponse.getSessionState());
    JWSHeader header = null;
    String idToken = authzResponse.getIdToken();
    String accessToken = authzResponse.getAccessToken();
    if (idToken != null) {
        header = new JWSInput(idToken).getHeader();
        assertEquals(expectedIdTokenAlg, header.getAlgorithm().name());
        assertEquals("JWT", header.getType());
        assertNull(header.getContentType());
    }
    if (accessToken != null) {
        header = new JWSInput(accessToken).getHeader();
        assertEquals(expectedAccessAlg, header.getAlgorithm().name());
        assertEquals("JWT", header.getType());
        assertNull(header.getContentType());
    }
    List<IDToken> idTokens = testAuthzResponseAndRetrieveIDTokens(authzResponse, loginEvent);
    for (IDToken idt : idTokens) {
        Assert.assertEquals("abcdef123456", idt.getNonce());
        Assert.assertEquals(authzResponse.getSessionState(), idt.getSessionState());
    }
}
Also used : OAuthClient(org.keycloak.testsuite.util.OAuthClient) EventRepresentation(org.keycloak.representations.idm.EventRepresentation) IDToken(org.keycloak.representations.IDToken) JWSInput(org.keycloak.jose.jws.JWSInput) JWSHeader(org.keycloak.jose.jws.JWSHeader)

Example 43 with JWSInput

use of org.keycloak.jose.jws.JWSInput in project keycloak by keycloak.

the class OfflineTokenTest method offlineTokenRequest.

private void offlineTokenRequest(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
    oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
    oauth.clientId("offline-client");
    OAuthClient.AccessTokenResponse tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
    JWSHeader header = null;
    String idToken = tokenResponse.getIdToken();
    String accessToken = tokenResponse.getAccessToken();
    String refreshToken = tokenResponse.getRefreshToken();
    if (idToken != null) {
        header = new JWSInput(idToken).getHeader();
        assertEquals(expectedIdTokenAlg, header.getAlgorithm().name());
        assertEquals("JWT", header.getType());
        assertNull(header.getContentType());
    }
    if (accessToken != null) {
        header = new JWSInput(accessToken).getHeader();
        assertEquals(expectedAccessAlg, header.getAlgorithm().name());
        assertEquals("JWT", header.getType());
        assertNull(header.getContentType());
    }
    if (refreshToken != null) {
        header = new JWSInput(refreshToken).getHeader();
        assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
        assertEquals("JWT", header.getType());
        assertNull(header.getContentType());
    }
    AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
    String offlineTokenString = tokenResponse.getRefreshToken();
    RefreshToken offlineToken = oauth.parseRefreshToken(offlineTokenString);
    events.expectClientLogin().client("offline-client").user(serviceAccountUserId).session(token.getSessionState()).detail(Details.TOKEN_ID, token.getId()).detail(Details.REFRESH_TOKEN_ID, offlineToken.getId()).detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE).detail(Details.USERNAME, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client").assertEvent();
    Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
    Assert.assertEquals(0, offlineToken.getExpiration());
    testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
    // Now retrieve another offline token and decode that previous offline token is still valid
    tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
    AccessToken token2 = oauth.verifyToken(tokenResponse.getAccessToken());
    String offlineTokenString2 = tokenResponse.getRefreshToken();
    RefreshToken offlineToken2 = oauth.parseRefreshToken(offlineTokenString2);
    events.expectClientLogin().client("offline-client").user(serviceAccountUserId).session(token2.getSessionState()).detail(Details.TOKEN_ID, token2.getId()).detail(Details.REFRESH_TOKEN_ID, offlineToken2.getId()).detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE).detail(Details.USERNAME, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client").assertEvent();
    // Refresh with both offline tokens is fine
    testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
    testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId);
}
Also used : RefreshToken(org.keycloak.representations.RefreshToken) OAuthClient(org.keycloak.testsuite.util.OAuthClient) AccessToken(org.keycloak.representations.AccessToken) JWSInput(org.keycloak.jose.jws.JWSInput) JWSHeader(org.keycloak.jose.jws.JWSHeader)

Example 44 with JWSInput

use of org.keycloak.jose.jws.JWSInput in project keycloak by keycloak.

the class ServiceAccountTest method clientCredentialsAuthSuccessWithRefreshToken.

// Testing of refresh token is for backwards compatibility. By default, there won't be refresh token for the client credentials grant
private void clientCredentialsAuthSuccessWithRefreshToken(String expectedRefreshAlg, String expectedAccessAlg) throws Exception {
    oauth.clientId("service-account-cl-refresh-on");
    OAuthClient.AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
    assertEquals(200, response.getStatusCode());
    AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
    RefreshToken refreshToken = oauth.parseRefreshToken(response.getRefreshToken());
    JWSHeader header = new JWSInput(response.getAccessToken()).getHeader();
    assertEquals(expectedAccessAlg, header.getAlgorithm().name());
    assertEquals("JWT", header.getType());
    assertNull(header.getContentType());
    header = new JWSInput(response.getRefreshToken()).getHeader();
    assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
    assertEquals("JWT", header.getType());
    assertNull(header.getContentType());
    events.expectClientLogin().client("service-account-cl-refresh-on").user(userId).session(accessToken.getSessionState()).detail(Details.TOKEN_ID, accessToken.getId()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId()).detail(Details.USERNAME, userName).assertEvent();
    assertEquals(accessToken.getSessionState(), refreshToken.getSessionState());
    System.out.println("Access token other claims: " + accessToken.getOtherClaims());
    Assert.assertEquals("service-account-cl-refresh-on", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
    Assert.assertTrue(accessToken.getOtherClaims().containsKey(ServiceAccountConstants.CLIENT_ADDRESS));
    Assert.assertTrue(accessToken.getOtherClaims().containsKey(ServiceAccountConstants.CLIENT_HOST));
    OAuthClient.AccessTokenResponse refreshedResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret1");
    AccessToken refreshedAccessToken = oauth.verifyToken(refreshedResponse.getAccessToken());
    RefreshToken refreshedRefreshToken = oauth.parseRefreshToken(refreshedResponse.getRefreshToken());
    assertEquals(accessToken.getSessionState(), refreshedAccessToken.getSessionState());
    assertEquals(accessToken.getSessionState(), refreshedRefreshToken.getSessionState());
    events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).user(userId).client("service-account-cl-refresh-on").assertEvent();
}
Also used : RefreshToken(org.keycloak.representations.RefreshToken) OAuthClient(org.keycloak.testsuite.util.OAuthClient) AccessToken(org.keycloak.representations.AccessToken) JWSInput(org.keycloak.jose.jws.JWSInput) JWSHeader(org.keycloak.jose.jws.JWSHeader)

Example 45 with JWSInput

use of org.keycloak.jose.jws.JWSInput in project keycloak by keycloak.

the class ResourceOwnerPasswordCredentialsGrantTest method grantRequest.

private void grantRequest(String expectedRefreshAlg, String expectedAccessAlg) throws Exception {
    String clientId = "resource-owner";
    String login = "direct-login";
    oauth.clientId(clientId);
    OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", login, "password", null);
    assertEquals(200, response.getStatusCode());
    AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
    RefreshToken refreshToken = oauth.parseRefreshToken(response.getRefreshToken());
    JWSHeader header = new JWSInput(response.getAccessToken()).getHeader();
    assertEquals(expectedAccessAlg, header.getAlgorithm().name());
    assertEquals("JWT", header.getType());
    assertNull(header.getContentType());
    header = new JWSInput(response.getRefreshToken()).getHeader();
    assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
    assertEquals("JWT", header.getType());
    assertNull(header.getContentType());
    events.expectLogin().client(clientId).user(userId).session(accessToken.getSessionState()).detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD).detail(Details.TOKEN_ID, accessToken.getId()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId()).detail(Details.USERNAME, login).removeDetail(Details.CODE_ID).removeDetail(Details.REDIRECT_URI).removeDetail(Details.CONSENT).assertEvent();
    Assert.assertTrue(login.equals(accessToken.getPreferredUsername()) || login.equals(accessToken.getEmail()));
    assertEquals(accessToken.getSessionState(), refreshToken.getSessionState());
    OAuthClient.AccessTokenResponse refreshedResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret");
    AccessToken refreshedAccessToken = oauth.verifyToken(refreshedResponse.getAccessToken());
    RefreshToken refreshedRefreshToken = oauth.parseRefreshToken(refreshedResponse.getRefreshToken());
    assertEquals(accessToken.getSessionState(), refreshedAccessToken.getSessionState());
    assertEquals(accessToken.getSessionState(), refreshedRefreshToken.getSessionState());
    events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).user(userId).client(clientId).assertEvent();
}
Also used : RefreshToken(org.keycloak.representations.RefreshToken) OAuthClient(org.keycloak.testsuite.util.OAuthClient) AccessToken(org.keycloak.representations.AccessToken) JWSInput(org.keycloak.jose.jws.JWSInput) JWSHeader(org.keycloak.jose.jws.JWSHeader)

Aggregations

JWSInput (org.keycloak.jose.jws.JWSInput)62 AccessToken (org.keycloak.representations.AccessToken)29 OAuthClient (org.keycloak.testsuite.util.OAuthClient)20 JWSInputException (org.keycloak.jose.jws.JWSInputException)16 Test (org.junit.Test)15 JWSHeader (org.keycloak.jose.jws.JWSHeader)11 Response (javax.ws.rs.core.Response)10 RefreshToken (org.keycloak.representations.RefreshToken)10 EventRepresentation (org.keycloak.representations.idm.EventRepresentation)9 ClientRepresentation (org.keycloak.representations.idm.ClientRepresentation)8 IOException (java.io.IOException)7 VerificationException (org.keycloak.common.VerificationException)7 JsonWebToken (org.keycloak.representations.JsonWebToken)7 JsonNode (com.fasterxml.jackson.databind.JsonNode)5 PublicKey (java.security.PublicKey)5 AccessTokenResponse (org.keycloak.representations.AccessTokenResponse)5 Client (javax.ws.rs.client.Client)4 IDToken (org.keycloak.representations.IDToken)4 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)3 List (java.util.List)3