Skip to content

Commit

Permalink
add util module for helper classes
Browse files Browse the repository at this point in the history
  • Loading branch information
dasniko committed Oct 12, 2023
1 parent fbac262 commit a236bda
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 1 deletion.
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<packaging>pom</packaging>

<modules>
<module>utils</module>
<module>event-listener</module>
<module>tokenmapper</module>
<module>flintstones-userprovider</module>
Expand All @@ -23,7 +24,7 @@
<module>conditional-authenticators</module>
<module>mfa-authenticator</module>
<module>passkey</module>
</modules>
</modules>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
51 changes: 51 additions & 0 deletions utils/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>dasniko.keycloak</groupId>
<artifactId>keycloak-extensions-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>keycloak-utils</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-legacy</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>

<build>
<finalName>${project.groupId}-${project.artifactId}</finalName>
</build>

</project>
171 changes: 171 additions & 0 deletions utils/src/main/java/dasniko/keycloak/util/TokenUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package dasniko.keycloak.util;

import org.keycloak.common.ClientConnection;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.services.Urls;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;

import java.util.function.Consumer;

import static org.keycloak.models.UserSessionModel.SessionPersistenceState.TRANSIENT;

@SuppressWarnings("unused")
public class TokenUtils {

public static String generateServiceAccountAccessToken(KeycloakSession session, String clientId, String scope, Consumer<AccessToken> tokenAdjuster) {

var context = session.getContext();
var realm = context.getRealm();
var client = session.clients().getClientByClientId(realm, clientId);

if (client == null) {
throw new IllegalStateException("client not found");
}

if (!client.isServiceAccountsEnabled()) {
throw new IllegalStateException("service account not enabled");
}

var clientUser = session.users().getServiceAccount(client);
var clientUsername = clientUser.getUsername();

// we need to remember the current authSession since createAuthenticationSession changes the current authSession in the context
var currentAuthSession = context.getAuthenticationSession();

try {
var rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
var authSession = rootAuthSession.createAuthenticationSession(client);

authSession.setAuthenticatedUser(clientUser);
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);

var clientConnection = context.getConnection();
var sessionId = authSession.getParentSession().getId();
var remoteAddr = clientConnection.getRemoteAddr();
var userSession = session.sessions().createUserSession(sessionId, realm, clientUser, clientUsername, //
remoteAddr, ServiceAccountConstants.CLIENT_AUTH, false, null, null, TRANSIENT);

AuthenticationManager.setClientScopesInSession(authSession);
var clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);

// Notes about client details
userSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());
userSession.setNote(ServiceAccountConstants.CLIENT_HOST, clientConnection.getRemoteHost());
userSession.setNote(ServiceAccountConstants.CLIENT_ADDRESS, remoteAddr);

var tokenManager = new TokenManager();
var event = new EventBuilder(realm, session, clientConnection);
var responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx);
responseBuilder.generateAccessToken();

if (tokenAdjuster != null) {
tokenAdjuster.accept(responseBuilder.getAccessToken());
}

var accessTokenResponse = responseBuilder.build();
return accessTokenResponse.getToken();
} finally {
// reset current authentication session
context.setAuthenticationSession(currentAuthSession);
}
}

public static String generateAccessToken(KeycloakSession session, UserSessionModel userSession, String clientId, String scope, Consumer<AccessToken> tokenAdjuster) {

KeycloakContext context = session.getContext();
RealmModel realm = userSession.getRealm();
ClientModel client = session.clients().getClientByClientId(realm, clientId);
String issuer = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());

RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
AuthenticationSessionModel iamAuthSession = rootAuthSession.createAuthenticationSession(client);

iamAuthSession.setAuthenticatedUser(userSession.getUser());
iamAuthSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
iamAuthSession.setClientNote(OIDCLoginProtocol.ISSUER, issuer);
iamAuthSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);

ClientConnection connection = context.getConnection();
UserSessionModel iamUserSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, userSession.getUser(), userSession.getUser().getUsername(), connection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null, TRANSIENT);

AuthenticationManager.setClientScopesInSession(iamAuthSession);
ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, iamUserSession, iamAuthSession);

// Notes about client details
userSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());
userSession.setNote(ServiceAccountConstants.CLIENT_HOST, connection.getRemoteHost());
userSession.setNote(ServiceAccountConstants.CLIENT_ADDRESS, connection.getRemoteAddr());

TokenManager tokenManager = new TokenManager();

EventBuilder eventBuilder = new EventBuilder(realm, session, connection);
TokenManager.AccessTokenResponseBuilder tokenResponseBuilder = tokenManager.responseBuilder(realm, client, eventBuilder, session, iamUserSession, clientSessionCtx);
AccessToken accessToken = tokenResponseBuilder.generateAccessToken().getAccessToken();

if (tokenAdjuster != null) {
tokenAdjuster.accept(accessToken);
}

AccessTokenResponse tokenResponse = tokenResponseBuilder.build();

return tokenResponse.getToken();
}

public static String generateAccessToken(KeycloakSession session, RealmModel realm, UserModel user, String clientId, String scope, Consumer<AccessToken> tokenAdjuster) {

KeycloakContext context = session.getContext();
ClientModel client = session.clients().getClientByClientId(realm, clientId);
String issuer = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());

RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
AuthenticationSessionModel iamAuthSession = rootAuthSession.createAuthenticationSession(client);

iamAuthSession.setAuthenticatedUser(user);
iamAuthSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
iamAuthSession.setClientNote(OIDCLoginProtocol.ISSUER, issuer);
iamAuthSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);

ClientConnection connection = context.getConnection();
UserSessionModel iamUserSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), connection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null, TRANSIENT);

AuthenticationManager.setClientScopesInSession(iamAuthSession);
ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, iamUserSession, iamAuthSession);

// Notes about client details
iamUserSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());
iamUserSession.setNote(ServiceAccountConstants.CLIENT_HOST, connection.getRemoteHost());
iamUserSession.setNote(ServiceAccountConstants.CLIENT_ADDRESS, connection.getRemoteAddr());

TokenManager tokenManager = new TokenManager();

EventBuilder eventBuilder = new EventBuilder(realm, session, connection);
TokenManager.AccessTokenResponseBuilder tokenResponseBuilder = tokenManager.responseBuilder(realm, client, eventBuilder, session, iamUserSession, clientSessionCtx);
AccessToken accessToken = tokenResponseBuilder.generateAccessToken().getAccessToken();

if (tokenAdjuster != null) {
tokenAdjuster.accept(accessToken);
}

AccessTokenResponse tokenResponse = tokenResponseBuilder.build();

return tokenResponse.getToken();
}
}

0 comments on commit a236bda

Please sign in to comment.