Repository navigation
How can I see the devices of user sessions in the admin API? #26959
|
The admin REST API has this endpoint to access a list of sessions for a user:
However, it seems like this list is different to the list in the account console ( |
Replies: 2 comments
|
I just found out:
For me, this shouldn't have this as default behavior since this endpoint requires no request body nor should make difference when matching the path 🤷, and it should be in the docs. Example of request: curl --location 'http://localhost:8080/realms/my-realm/account/sessions/devices' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ.....' |
|
According to admin documentation there is no such way to achieve that. But you can add your custom endpoint with admin authorization public class SessionDeviceResourceProvider {
private final KeycloakSession session;
private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEventBuilder;
private final RealmModel realm;
private final Logger logger = Logger.getLogger(SessionDeviceResourceProvider.class);
public SessionDeviceResourceProvider(KeycloakSession keycloakSession, RealmModel realmModel, AdminPermissionEvaluator adminPermissionEvaluator, AdminEventBuilder adminEventBuilder) {
this.session = keycloakSession;
this.auth = adminPermissionEvaluator;
this.realm = realmModel;
this.adminEventBuilder = adminEventBuilder;
}
// Example: https://localhost/admin/realms/<realm-name>/<factory-id>/<user-id:guid>/sessions/devices
@GET
@Path("{userId}/sessions/devices")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response listUserSessions(@PathParam("userId") String userId) {
final var admin = auth.adminAuth();
var isAdmin = admin.hasRealmRole("realm-admin");
if(!isAdmin){
return Response.status(
Response.Status.FORBIDDEN.getStatusCode(),
"Requires admin role")
.build();
}
UserModel user = session.users().getUserById(session.getContext().getRealm(), userId);
if (user == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
var userSessions = session.sessions().getUserSessionsStream(realm, user)
.map(session -> {
var deviceRep = DeviceActivityManager.getCurrentDevice(session);
if (deviceRep == null) return new CustomUserSessionReprisentation(session);
var nativeRep = createSessionRepresentation(session, deviceRep);
var clients = nativeRep.getClients().stream().map(ClientRepresentation::getClientName).toList();
return new CustomUserSessionReprisentation(
session.getId(),
session.getLoginUsername(),
user.getId(),
session.getIpAddress(),
session.getStarted(),
session.getLastSessionRefresh(),
clients,
deviceRep.getOs(),
deviceRep.getOsVersion(),
deviceRep.getBrowser(),
deviceRep.getDevice(),
deviceRep.isMobile()
);
});
return Response.ok(userSessions).build();
}
private SessionRepresentation createSessionRepresentation(UserSessionModel s, DeviceRepresentation device) {
SessionRepresentation sessionRep = new SessionRepresentation();
sessionRep.setId(s.getId());
sessionRep.setIpAddress(s.getIpAddress());
sessionRep.setStarted(s.getStarted());
sessionRep.setLastAccess(s.getLastSessionRefresh());
int maxLifespan = s.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe() : realm.getSsoSessionMaxLifespan();
int expires = s.getStarted() + maxLifespan;
sessionRep.setExpires(expires);
sessionRep.setBrowser(device.getBrowser());
sessionRep.setClients(new LinkedList());
for (String clientUUID : s.getAuthenticatedClientSessions().keySet()) {
ClientModel client = realm.getClientById(clientUUID);
ClientRepresentation clientRep = new ClientRepresentation();
clientRep.setClientId(client.getClientId());
clientRep.setClientName(client.getName());
sessionRep.getClients().add(clientRep);
}
return sessionRep;
}
}Factory: public class SessionDeviceResourceProviderFactory implements AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
public static final String ID = "user-session-device";
@Override
public AdminRealmResourceProvider create(KeycloakSession keycloakSession) {
return this;
}
@Override
public void init(Config.Scope scope) {
}
@Override
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return ID;
}
@Override
public Object getResource(KeycloakSession keycloakSession, RealmModel realmModel, AdminPermissionEvaluator adminPermissionEvaluator, AdminEventBuilder adminEventBuilder) {
return new SessionDeviceResourceProvider(keycloakSession, realmModel, adminPermissionEvaluator, adminEventBuilder);
}
} |
According to admin documentation there is no such way to achieve that.
But you can add your custom endpoint with admin authorization