konradandCursor bffc0c1a4d Assert the gRPC user token includes the username.
The interceptor already copies preferred_username; the test now checks it.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 18:05:44 +02:00
2026-09-28 13:35:12 +02:00

go-usertoken

Short-lived RS256 user tokens from a Nextcloud ExApp to Go Microservices.

Import: gitea.neitzel.de/konrad/go-usertoken (package 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.

This module does not speak AppAPI and does not see APP_SECRET.

ExApp

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)
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

Microservice

auth, err := usertoken.FromEnv(ctx)
handler = auth.Middleware()(handler)

caller, _ := usertoken.CallerFromContext(r.Context())
raw, _ := usertoken.BearerFromContext(r.Context())
out.Header.Set("Authorization", "Bearer "+raw)

/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
openssl genrsa -out user-token.key 2048
openssl rsa -in user-token.key -pubout -out user-token.pub

The private key stays on the ExApp. Static mode copies user-token.pub to each Microservice.

S
Description
No description provided
Readme Unlicense
57 KiB
Languages
Go 100%