Keycloak: authenticate user with custom JWT

Situation: We use keycloak to authenticate users in our web application (A) using a normal browser authentication flow using a JavaScript adapter. It works great!

Objective: . Now the new user group should have access to A. But they are registered with the username and password in a trusted third-party application (B) without Keycloak. In B, they have a reference to A with a custom JWT (essentially containing the username and role) as the request parameter. Therefore, when the user clicks on the link, he lands on the entry point to the application, where we can read the JWT from the URL. Now you need something like a token exchange. We want to send this custom JWT to Keycloak, which sends the analog access token to the normal registration process.

Question: Is there built-in Keycloak support for this use?

Attempts:

I tried to create a confidential client with "Signed JWT" as " Client Authenticator " as suggested in the docs. After some testing, I don't think this is the right way, even if the name is promising.

Another track was the “ Client offered by the identity provider , by implementing my own identity provider. But I don’t see how I can send the JWT to the request.

I'm currently trying to use Autentication SPI to extend the authentication flow with a custom authenticator.

Perhaps this is a lot easier than I think. Can someone lead me in the right direction?

+4
source share
1 answer

, , , SPI Authentication, .

Keycloak ( ) "Portal JWT" (. ). " " ""

User Authentication Stream

" JWT" - , JWT urid , . keyclay "isExternal". :

public class JwtAuthenticator implements Authenticator {

private final JwtReader reader;

JwtAuthenticator(JwtReader reader) {
    this.reader = reader;
}

@Override
public void authenticate(AuthenticationFlowContext context) {
    Optional<String> externalCredential = hasExternalCredential(context);
    if (externalCredential.isPresent()) {
        ExternalUser externalUser = reader.read(context.getAuthenticatorConfig(), externalCredential.get());
        String username = externalUser.getUsername();
        UserModel user = context.getSession().users().getUserByUsername(username, context.getRealm());
        if (user == null) {
            user = context.getSession().users().addUser(context.getRealm(), username);
            user.setEnabled(true);
            user.setSingleAttribute("isExternal", "true");
        }
        for (String roleName : externalUser.getRoles()) {
            RoleModel role = context.getRealm().getRole(roleName);
            if (role == null) {
                role = context.getRealm().addRole(roleName);
            }
            user.grantRole(role);
        }
        context.setUser(user);
        context.success();
    } else {
        context.attempted();
    }
}

private Optional<String> hasExternalCredential(AuthenticationFlowContext context) {
    String redirectUri = context.getUriInfo().getQueryParameters().getFirst("redirect_uri);
    try {
        List<NameValuePair> queryParams = URLEncodedUtils.parse(new URI(redirectUri), "UTF-8");
        Optional<NameValuePair> jwtParam = queryParams.stream()
                .filter(nv -> "jwt".equalsIgnoreCase(nv.getName())).findAny();
        if (jwtParam.isPresent()) {
            String jwt = jwtParam.get().getValue();
            if (LOG.isDebugEnabled()) {
                LOG.debug("JWT found: " + jwt);
            }
            return Optional.of(jwt);
        }
    } catch (URISyntaxException e) {
        LOG.error("Redirect URL not as expected: " + redirectUri);
    }
    return Optional.empty();
}
+1

Source: https://habr.com/ru/post/1693232/


All Articles