59 lines
2.2 KiB
Markdown
59 lines
2.2 KiB
Markdown
|
|
# auth
|
||
|
|
|
||
|
|
Shared Go library with authentication primitives for all Shiny backend services.
|
||
|
|
|
||
|
|
## Shared Documentation
|
||
|
|
|
||
|
|
@../docs/claude/architecture.md
|
||
|
|
@../docs/claude/go-services.md
|
||
|
|
@../docs/claude/conventions.md
|
||
|
|
|
||
|
|
## Library Information
|
||
|
|
|
||
|
|
### Purpose
|
||
|
|
|
||
|
|
Single home for the `user`-header auth and secret-startup-guard code that was
|
||
|
|
previously byte-identical-copied into every backend (the `auth` package and
|
||
|
|
`cmd/service/secrets_guard.go`). Enforces ADR-0005 (HMAC-signed `user` header,
|
||
|
|
keyless fail-open only in acctest) and ADR-0006 (fail closed when required
|
||
|
|
secrets are missing in `staging`/`production`).
|
||
|
|
|
||
|
|
### Usage
|
||
|
|
|
||
|
|
```go
|
||
|
|
import "gitea.unbound.se/shiny/auth"
|
||
|
|
|
||
|
|
// Fail closed on missing deployed secrets before serving (ADR-0005/0006).
|
||
|
|
if missing := auth.MissingDeployedSecrets(environment, map[string]string{
|
||
|
|
"USER_SIGNING_KEY": cfg.UserSigningKey,
|
||
|
|
"INTERNAL_API_KEY": cfg.InternalAPIKey,
|
||
|
|
}); len(missing) > 0 {
|
||
|
|
log.Fatalf("refusing to start: missing secrets in %s: %v", environment, missing)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify the gateway's signed user header and inject *User into the context.
|
||
|
|
handler = auth.UserMiddleware([]byte(cfg.UserSigningKey))(handler)
|
||
|
|
|
||
|
|
// Read the authenticated user downstream.
|
||
|
|
user := auth.FromContext(ctx)
|
||
|
|
if user.HasRole("admin") { /* ... */ }
|
||
|
|
```
|
||
|
|
|
||
|
|
### Exported API
|
||
|
|
|
||
|
|
- `UserMiddleware(signingKey []byte)` — HTTP middleware verifying the
|
||
|
|
HMAC-signed `user` header; injects `*User` into the request context.
|
||
|
|
- `FromContext(ctx) *User`, `User.HasRole(...) bool`, `ContextKey`/`UserKey`.
|
||
|
|
- `MissingDeployedSecrets(environment string, secrets map[string]string) []string`
|
||
|
|
— returns the sorted names of secrets that are empty in `staging`/`production`
|
||
|
|
(nil for any other environment, e.g. `development`/acctest).
|
||
|
|
|
||
|
|
### Conventions
|
||
|
|
|
||
|
|
Standard Shiny library scaffolding: `gofumpt`/`goimports -local`, golangci-lint,
|
||
|
|
gitleaks and conventional-commit checks via pre-commit; coverage-regression gate
|
||
|
|
in CI (`.testcoverage.yml`); releases auto-tagged from conventional commits by
|
||
|
|
the shared Release workflow. Bump the consuming services' `go.mod` after a
|
||
|
|
release. A breaking change to the signed-header or secret-guard contract is a
|
||
|
|
cross-service change — see ADR-0005/0006 before changing it.
|