0

Using the keycloak-admin-client in Java, I've successfully created a new client and a new user. However, I can't seem to use the user to obtain a JWT for the client.

Here's my code:

        // Create a keycloak instance with admin privs
        Keycloak keycloak =
                KeycloakBuilder.builder()
                        .serverUrl("http://localhost:8080/auth")
                        .grantType(OAuth2Constants.PASSWORD)
                        .realm("master")
                        .username("admin")
                        .password("adminPass")
                        .clientId("admin-cli")
                        .resteasyClient(new ResteasyClientBuilderImpl().connectionPoolSize(10).build())
                        .build();

        // Edit: Create the realm "test-realm"
        RealmRepresentation realmRep = new RealmRepresentation();
        realmRep.setRealm("test-realm");
        realmRep.setEnabled(true);
        keycloak.realms().create(realmRep);

        // Create the client "test-client"
        ClientRepresentation clientRep = new ClientRepresentation();
        clientRep.setClientId("test-client");
        clientRep.setSecret(UUID.randomUUID().toString());
        clientRep.setProtocol("openid-connect");
        // ❌ Edit: This is the current problem, I need the client to be confidential.
        clientRep.setBearerOnly(true);
        //  Edit: Added this but still getting 400
        clientRep.setDirectAccessGrantsEnabled(true);
        //  Edit: Added this and it fixed part of the problem
        clientRep.setEnabled(true);

        keycloak.realm("test-realm").clients().create(clientRep);

        // Create the user "test-user"
        CredentialRepresentation credRep = new CredentialRepresentation();
        credRep.setType(CredentialRepresentation.PASSWORD);
        credRep.setValue("test-user-pass");
        UserRepresentation userRep = new UserRepresentation();
        userRep.setUsername("test-user");
        userRep.setCredentials(Collections.singletonList(credRep));
        //  Edit: Added this and it fixed part of the problem
        userRep.setEnabled(true);

        keycloak.realm("test-realm").users().create(userRep);

        // Create another Keycloak instance with the new user's credentials
        keycloak =
                KeycloakBuilder.builder()
                        .serverUrl("http://localhost:8080/auth")
                        .grantType(OAuth2Constants.PASSWORD)
                        .realm("test-realm")
                        .username("test-user")
                        .password("test-user-pass")
                        .clientId("test-client")
                        .clientSecret(clientRep.getSecret())
                        .resteasyClient(new ResteasyClientBuilderImpl().connectionPoolSize(10).build())
                        .build();

        // Try and obtain a JWT
        AccessTokenResponse atr = keycloak.tokenManager().getAccessToken(); //  Fails with 400
        DecodedJWT jwt =  JWT.decode(atr.getToken());

This gives me a javax.ws.rs.BadRequestException: HTTP 400 Bad Request but, unfortunately, I can't seem to get anything more helpful than that. I've debugged and can't discern any messages that would tell me what exactly is bad about my request.

I've also tried removing the client secret from creating the client and from passing it to the second Keycloak instance, but I still get the 400.

Edit: I've tried looking at the logs (I'm running Keycloak in a Docker container) and nothing appears to be getting logged when this issue occurs.

Edit: I've also added a basic role to the client and user (not with any permissions, just the most basic role) but that still hasn't helped. I would expected if this was a role/permissions issue that I'd be getting an unauthorized, though, instead of a 400.

Edit: It turns out part of my problem was that my user (and problem my client) were not enabled. Enabling both fixed part of the problem. My final problem is that I need the client to be confidential, but I cannot find how to configure this via the Java client API.

Currently I don't know how to configure the access type to be confidential with the Java client. enter image description here

Austin Brown
  • 830
  • 12
  • 24
  • 1
    `.grantType(OAuth2Constants.PASSWORD)` sounds like you want to use a direct access grant flow, but I don't see allowed that flow in your `test-client` (`clientRep`). – Jan Garaj Mar 24 '21 at 23:28
  • Good suggestion, but I tried this and I'm still getting the `400` unfortunately. I may still need it later, so I'll keep it just in case. – Austin Brown Mar 24 '21 at 23:31
  • https://stackoverflow.com/questions/63721080/keycloak-admin-client-responds-with-bad-request-to-attempt-to-list-realms I found this and my guess is that I might need to create a new realm. If I understand the issue with the linked answer, you can not get a JWT from the `master` realm. – Austin Brown Mar 25 '21 at 03:27
  • I tried using a realm other than `master` and there was a brief moment where I thought I was on the right track, but I was mistaken and I'm still getting a `400`. Whether or not it is for the same reasons I'm not sure, but I'll update my code snippet soon to reflect what I've currently got. – Austin Brown Mar 25 '21 at 04:01
  • When creating the realm you could set the following stuff:\ `realmRep.eventsEnabled(true);` `realmRep.setEnabledEventTypes(List.of("LOGIN_ERROR","CODE_TO_TOKEN_ERROR","CLIENT_LOGIN_ERROR"));` `realmRep.setEventsExpiration(172800);` Maybe in the Admin GUI some event pops up which helps you. – bender316 Mar 25 '21 at 15:53
  • Thanks, that was really helpful. It turns out my user was not enabled, so I had to fix that. Now the one final issue is that the client is not confidential. I did some manual editing and it worked when I manually made my client confidential, but does not work while it isn't. Do you know how to, using the Java API, make the client confidential? I've looked over the methods available for the ClientRepresentation, but nothing looks right. And the documentation for the Java client is pretty bad/nonexistent. – Austin Brown Mar 25 '21 at 17:37
  • Finally figured out how to make my client confidential: `clientRep.setPubliClient(false);` – Austin Brown Mar 25 '21 at 18:50

1 Answers1

1

Alright, so my code had multiple issues. These are all the things I had to do to fix my issue:

  1. I had to create a new realm, as opposed to trying to obtain a JWT access token from the default master realm.
  2. My user/client were not enabled.
    • clientRep.setEnabled(true);
    • userRep.setEnabled(true);
  3. My client was bearer-only, which only verifies tokens, but does not grant them.
Austin Brown
  • 830
  • 12
  • 24