Document every exported symbol and how callers use the library.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,66 +4,38 @@ Short-lived RS256 user tokens from a Nextcloud ExApp to Go Microservices.
|
||||
|
||||
Import: `gitea.neitzel.de/konrad/go-usertoken` (package `usertoken`).
|
||||
|
||||
```bash
|
||||
go get gitea.neitzel.de/konrad/go-usertoken
|
||||
```
|
||||
|
||||
The ExApp mints a token after AppAPI has named the user. Each Microservice checks that token. One process trusts either a static public key or an OIDC issuer. The same bearer is forwarded when a Microservice calls another.
|
||||
|
||||
Procedure and claim rules: Knowledge `platforms/nextcloud/exapps/authentication.md`. Domain words: [CONTEXT.md](./CONTEXT.md).
|
||||
- [Guide](docs/guide.md) — how to mint and how to verify
|
||||
- [API](docs/api.md) — every exported symbol
|
||||
- [Domain language](CONTEXT.md)
|
||||
|
||||
This module does not speak AppAPI and does not see `APP_SECRET`.
|
||||
Procedure and claim rules: Knowledge `platforms/nextcloud/exapps/authentication.md`.
|
||||
|
||||
## ExApp
|
||||
## Included
|
||||
|
||||
```go
|
||||
signer, err := usertoken.NewSignerFromEnv()
|
||||
raw, err := signer.Mint(time.Now(), usertoken.MintInput{
|
||||
Subject: userID,
|
||||
Groups: &groupIDs, // nil omits groups
|
||||
})
|
||||
req.Header.Set("Authorization", "Bearer "+raw)
|
||||
```
|
||||
- [ExApp](docs/guide.md#exapp) — mint a token, including key generation
|
||||
- [Microservice](docs/guide.md#microservice) — verify a bearer and forward it
|
||||
|
||||
| Variable | Role |
|
||||
| --- | --- |
|
||||
| `USER_TOKEN_PRIVATE_KEY_FILE` | RSA private key PEM |
|
||||
| `USER_TOKEN_ISSUER` | `iss` string |
|
||||
| `USER_TOKEN_AUDIENCE` | `aud` string |
|
||||
| `USER_TOKEN_TTL` | optional, default `5m` |
|
||||
## Excluded
|
||||
|
||||
## Microservice
|
||||
- AppAPI and `APP_SECRET`
|
||||
- Choosing the Nextcloud user (the ExApp already has that id)
|
||||
|
||||
```go
|
||||
auth, err := usertoken.FromEnv(ctx)
|
||||
handler = auth.Middleware()(handler)
|
||||
## Testing
|
||||
|
||||
caller, _ := usertoken.CallerFromContext(r.Context())
|
||||
raw, _ := usertoken.BearerFromContext(r.Context())
|
||||
out.Header.Set("Authorization", "Bearer "+raw)
|
||||
```
|
||||
Unit tests cover minting, static verification, and the HTTP and gRPC interceptors. OIDC tests do not need a live issuer unless a test starts one.
|
||||
|
||||
`/health` is not authenticated. Any other path without a valid bearer is 401.
|
||||
|
||||
Set one of these. Setting both, or neither, makes `FromEnv` fail.
|
||||
|
||||
Static public key (ExApp-minted tokens):
|
||||
|
||||
| Variable | Role |
|
||||
| --- | --- |
|
||||
| `USER_TOKEN_PUBLIC_KEY_FILE` | RSA public key PEM |
|
||||
| `USER_TOKEN_ISSUER` | expected `iss` |
|
||||
| `USER_TOKEN_AUDIENCE` | expected `aud` |
|
||||
| `USER_TOKEN_SKEW` | optional, default `1m` |
|
||||
|
||||
OIDC issuer (Keycloak or another identity server):
|
||||
|
||||
| Variable | Role |
|
||||
| --- | --- |
|
||||
| `OIDC_ISSUER` | issuer URL that serves discovery |
|
||||
| `OIDC_AUDIENCE` | expected `aud`; empty skips the check |
|
||||
| `OIDC_GROUPS_CLAIM` | group array claim, default `groups` (`identity_groups` for current Keycloak tokens) |
|
||||
| `USER_TOKEN_SKEW` | optional, default `1m` |
|
||||
`go test ./...` fails when [`docs/api.md`](docs/api.md) does not match the exported API. Regenerate it with:
|
||||
|
||||
```bash
|
||||
openssl genrsa -out user-token.key 2048
|
||||
openssl rsa -in user-token.key -pubout -out user-token.pub
|
||||
UPDATE_API_DOCS=1 go test -run TestAPIDoc -count=1
|
||||
```
|
||||
|
||||
The private key stays on the ExApp. Static mode copies `user-token.pub` to each Microservice.
|
||||
## Related
|
||||
|
||||
- Knowledge `platforms/nextcloud/exapps/authentication.md` — procedure and claims
|
||||
|
||||
Reference in New Issue
Block a user