diff --git a/CLAUDE.md b/CLAUDE.md index b9dff93..9b7e5d1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,16 @@ go build ./... # Build (library — no main package) go vet ./... # Vet ``` +## Authentik Integration + +go-api integrates with [Authentik](https://goauthentik.io/) for identity management: + +- **Forward auth mode**: Reads `X-authentik-*` headers from Traefik (`TrustedProxy: true`) +- **OIDC mode**: Validates JWT Bearer tokens via OIDC discovery (`Issuer` + `ClientID`) +- **Permissive middleware**: `WithAuthentik()` extracts user but doesn't block requests +- **Route guards**: `RequireAuth()` (401) and `RequireGroup("admins")` (403) for protected routes +- **Coexists with `WithBearerAuth()`** for service-to-service tokens + ## Coding Standards - **UK English** in comments and user-facing strings (colour, organisation, unauthorised) diff --git a/README.md b/README.md index 3665ae0..4024ce9 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,44 @@ func (r *Routes) ListItems(c *gin.Context) { } ``` +## Authentik Integration + +```go +engine, _ := api.New( + api.WithAuthentik(api.AuthentikConfig{ + Issuer: "https://auth.host.uk.com/application/o/core-api/", + ClientID: "core-api", + TrustedProxy: true, // Read X-authentik-* headers from Traefik + }), +) +``` + +In handlers, use `GetUser()` to access the authenticated user: + +```go +func (r *Routes) ListItems(c *gin.Context) { + user := api.GetUser(c) // nil if unauthenticated + if user != nil { + // user.Username, user.Email, user.Groups, user.UID + } +} +``` + +For protected routes, use `RequireAuth()` or `RequireGroup()`: + +```go +func (r *Routes) RegisterRoutes(rg *gin.RouterGroup) { + rg.GET("/public", r.PublicEndpoint) + rg.GET("/private", api.RequireAuth(), r.PrivateEndpoint) + rg.GET("/admin", api.RequireGroup("admins"), r.AdminEndpoint) +} +``` + ## Features - **Response envelope** — `api.OK()`, `api.Fail()`, `api.Paginated()` for consistent JSON responses - **Middleware** — Bearer auth, CORS, request ID generation, panic recovery +- **Authentik** — Forward auth headers + OIDC JWT validation, `RequireAuth()` and `RequireGroup()` guards - **WebSocket** — `WithWSHandler()` mounts any `http.Handler` at `/ws` - **Swagger UI** — `WithSwagger()` serves interactive API docs at `/swagger/` - **Health check** — Built-in `GET /health` endpoint