Spring Boot service that bridges RFID-based identification to Keycloak token exchange and role extraction. It allows clients to authenticate either via an RFID attribute stored on a Keycloak user or via username/password, and to decode roles from a JWT.
- Authenticates by RFID using Keycloak token exchange (client-credentials -> user token).
- Authenticates by username/password using the password grant.
- Extracts realm and client roles from a JWT and returns them in a simple DTO.
Authenticate using either RFID or username/password.
Request body (JSON):
{
"rfid": "1234567890"
}or
{
"username": "alice",
"password": "secret"
}Response: raw token response from Keycloak (map of token fields).
Error behavior:
403if RFID is not found or password is invalid.409if RFID maps to multiple users.401if user is disabled or credentials are missing.
RFID lookup uses a Keycloak user attribute named RFID.
Refresh an access token using a refresh token.
Request body (JSON):
{
"refreshToken": "<refresh_token>"
}Response: raw token response from Keycloak (map of token fields).
Extract roles and basic profile fields from a JWT.
Headers:
Authorization: Bearer <jwt>
Response (JSON):
{
"username": "alice",
"name": "Alice",
"lastName": "Doe",
"email": "alice@example.com",
"realmRoles": ["user", "admin"],
"clientRoles": {
"account": ["view-profile"],
"my-client": ["read", "write"]
}
}Configured via application.yaml with environment variable overrides:
keycloak:
base-url: ${KEYCLOAK_AUTH}
realm: ${KEYCLOAK_REALM}
client-id: ${KEYCLOAK_CLIENT}
client-secret: ${KEYCLOAK_CLIENT_SECRET}
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS}Required environment variables:
KEYCLOAK_AUTH(e.g.,http://localhost:8180/auth)KEYCLOAK_REALMKEYCLOAK_CLIENTKEYCLOAK_CLIENT_SECRETCORS_ALLOWED_ORIGINS(e.g.,http://localhost:3000,http://localhost:8080)
Optional profile:
devprofile usessrc/main/resources/application-dev.yamlwith sample values.
Additional features:
- Virtual threads enabled via Spring Boot 4.0.2
The service requires a Keycloak client with service account enabled. The service account must have the following realm-management client roles assigned:
Required Keycloak server feature:
- Token exchange must be enabled at Keycloak startup:
KC_FEATURES=token-exchange
Required service account roles:
query-users- Required to search for users by RFID attributeview-users- Required to read user detailsimpersonation- Required to perform token exchange and impersonate users
To configure in Keycloak:
- Ensure Keycloak is started with token exchange enabled:
KC_FEATURES=token-exchange - Create a new client (e.g.,
rfid-adapter) - Enable Service accounts roles in the client settings
- Go to the Service Account Roles tab
- Add the client role filter for
realm-management - Assign the roles:
query-users,view-users, andimpersonation - Add an
RFIDuser attribute to users for RFID-based authentication:- Navigate to Users → Select a user → Attributes tab
- Add attribute key:
RFID, value: the RFID number (e.g.,1234567890) - The service queries users by this attribute to match RFID cards to user accounts
Prerequisites:
- Java 25 (configured in
pom.xml)
Run locally:
./mvnw spring-boot:runRun with dev profile:
./mvnw spring-boot:run -Dspring-boot.run.profiles=devBuild jar:
./mvnw packageRun jar:
java -jar target/keycloak-rfid-adapter-0.0.1-SNAPSHOT.jarBuild the jar first, then build the image:
./mvnw package
docker build --build-arg JAR_FILE=target/keycloak-rfid-adapter-0.0.1-SNAPSHOT.jar -t keycloak-rfid-adapter:local .Run:
docker run --rm -p 8080:8080 \
-e KEYCLOAK_AUTH=http://host.docker.internal:8180/auth \
-e KEYCLOAK_REALM=REALM \
-e KEYCLOAK_CLIENT=rfid-adapter \
-e KEYCLOAK_CLIENT_SECRET=change-me \
keycloak-rfid-adapter:localsrc/main/java/stylepatrick/keycloakrfidadapterSpring Boot app, controllers, services.src/main/resources/application.yamlbase configuration.Dockerfileruntime image (Temurin JRE 25).
- Token exchange uses the Keycloak Admin client to resolve user ID by RFID and then exchanges for a user token.
/auth/detailsdoes not validate signature; it decodes the JWT payload and extracts claims.- CORS is configured via
CORS_ALLOWED_ORIGINSenvironment variable.