Careeroza
Keycloak and Auth0 Setup
Run Keycloak Locally
Start Keycloak
Keycloak is a free, open-source authorization server. Run it with Docker on port 8180 so it does not clash with your Spring app:
docker run -d --name keycloak -p 8180:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.0 start-devExample output
Unable to find image 'quay.io/keycloak/keycloak:26.0' locally 26.0: Pulling from keycloak/keycloak Status: Downloaded newer image for quay.io/keycloak/keycloak:26.0 3f6c2a9b8e1d4c7f0a5b2e8d9c1f4a7b6e3d0c9f8a2b5e1d4c7f0a3b6e9d2c5f $ docker logs keycloak | tail -2 2026-03-01 04:30:12,118 INFO [io.quarkus] (main) Keycloak 26.0.7 on JVM (powered by Quarkus 3.15.1) started in 9.412s. Listening on: http://0.0.0.0:8080 2026-03-01 04:30:12,119 INFO [io.quarkus] (main) Profile dev activated.
Configure the Realm
- Open
http://localhost:8180, log in as admin, and create a realm shopease. - Create a client shopease-web: OpenID Connect, Client authentication OFF (public client), Standard flow ON, valid redirect URI
http://localhost:5173/*for dev only. PKCE: Advanced → "Proof Key for Code Exchange Code Challenge Method" = S256. - Create a client shopease-api (bearer-only use) and add client roles such as
seller. - Add Client scopes
orders.readandorders.write, and an Audience mapper that addsshopease-apito theaudclaim (otherwiseaudis onlyaccountand the API rejects the token). - Create realm roles
customerandadmin, and a test user asha with rolecustomer.
Get a Test Token
Password grant (dev only)
For local testing only, enable "Direct access grants" on a separate shopease-cli client and get a token from the command line:
TOKEN=$(curl -s -X POST http://localhost:8180/realms/shopease/protocol/openid-connect/token \
-d grant_type=password -d client_id=shopease-cli \
-d username=asha -d password=asha-dev-pass -d scope="openid orders.read" | jq -r .access_token)
curl -s http://localhost:8081/api/orders -H "Authorization: Bearer $TOKEN"Example output
[{"id":1042,"status":"SHIPPED","total":2499.00,"placedAt":"2026-02-27T18:22:05"},
{"id":1057,"status":"PLACED","total":899.00,"placedAt":"2026-03-01T09:41:30"}]Using Auth0 Instead
| Setting | Keycloak | Auth0 |
|---|---|---|
| issuer-uri | http://localhost:8180/realms/shopease | https://shopease.us.auth0.com/ (note the trailing slash) |
| Audience | Audience mapper on a client scope | Create an API with identifier https://api.shopease.in; clients must request audience=... |
| Roles | realm_access.roles, resource_access.*.roles | Enable RBAC → permissions claim, or add roles with an Action |
| Cost | Free, you run it | Managed; free tier, then paid |
application.yml for Auth0
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://shopease.us.auth0.com/
audiences: https://api.shopease.inBefore Spring Boot added the audiences property, you wrote the audience check yourself. It is still useful when you need custom rules:
@Bean
JwtDecoder jwtDecoder(@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}") String issuer) {
NimbusJwtDecoder decoder = JwtDecoders.fromIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> audience = new JwtClaimValidator<List<String>>(
JwtClaimNames.AUD, aud -> aud != null && aud.contains("https://api.shopease.in"));
decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefaultWithIssuer(issuer), audience));
return decoder;
}