A full-stack task management application (Jira-lite) built with Spring Boot 3, Keycloak OAuth2/OIDC, PostgreSQL, and Angular — with role-based and resource-based authorization.
Status: backend complete and verified end-to-end with real Keycloak tokens. Angular frontend in progress.
- OAuth2/OIDC authentication with Keycloak — realms, clients, and JWT validation
- Role-based access control (
@PreAuthorize) and resource-based ownership checks - Just-in-time user provisioning from JWT claims
- JPA relationships with proper cascading, lazy loading, and optimistic locking
- Liquibase-managed schema migrations, isolated from Keycloak's own database
- Full-stack ownership: backend, auth, database, and frontend (Angular)
┌─────────────┐ tokens ┌──────────────┐
│ Angular │ ──────────────► │ Keycloak │
│ (port 4200)│ │ (port 8180) │
└──────┬──────┘ └──────────────┘
│ API calls ▲
▼ with Bearer token │ validate token (local, cached public key)
┌─────────────┐ │
│ Spring Boot │ ───────────────────────►│
│ (port 8080)│
└──────┬──────┘
▼
┌─────────────┐
│ PostgreSQL │
│ (port 5432)│
└─────────────┘
Angular authenticates directly against Keycloak (Authorization Code Flow + PKCE) and never sees user credentials. Spring Boot validates JWTs locally using Keycloak's cached public key — no network call to Keycloak on every request.
| Role | Permissions |
|---|---|
ADMIN |
Full access to all projects and tasks |
PROJECT_MANAGER |
Create projects, create and assign tasks |
DEVELOPER |
View assigned work, update status of their own tasks |
| Area | Technology |
|---|---|
| Backend | Java 17, Spring Boot 3.4, Spring Security (OAuth2 Resource Server) |
| Auth | Keycloak 24 (OIDC, JWT, realm-based roles) |
| Database | PostgreSQL 16, Liquibase (versioned migrations) |
| Frontend | Angular (in progress) |
| Build / Infra | Maven, Docker Compose |
# 1. Start Postgres and Keycloak (realm auto-imported)
docker compose up -d
# 2. Run the backend
./mvnw spring-boot:run
# 3. Access Keycloak Admin Console
open http://localhost:8180 # admin / adminThe realm, clients, and roles are pre-configured via
keycloak/taskmanager-realm.json, imported automatically on first startup.
curl -X POST http://localhost:8080/api/projects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Website Redesign", "description": "..."}'curl http://localhost:8080/api/projects -H "Authorization: Bearer $TOKEN"curl -X POST http://localhost:8080/api/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Design homepage", "priority": "HIGH", "projectId": "..."}'curl "http://localhost:8080/api/tasks?projectId=..." -H "Authorization: Bearer $TOKEN"curl http://localhost:8080/api/tasks/my -H "Authorization: Bearer $TOKEN"curl -X PATCH http://localhost:8080/api/tasks/{id}/status \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '"IN_PROGRESS"'- Docker Compose: Postgres (multi-database) + Keycloak with realm import
- Entities, Liquibase migrations, repositories
- Spring Security JWT validation with custom Keycloak role converter
- Just-in-time user provisioning
- Role-based authorization (
@PreAuthorize) - Resource-based authorization (task ownership checks)
- Delete/update endpoints for projects and tasks
- Angular frontend with Keycloak-Angular integration
- Integration tests with Testcontainers
- GitHub Actions CI
Built to prove full-stack ownership with a real, non-trivial auth integration — Keycloak realm configuration, JWT validation, role-based and resource-based authorization — going beyond typical CRUD-only portfolio projects.