Getting started¶
This walkthrough verifies a credential, reads the resulting Identity, and passes it
down the call stack — the core loop you wire into an HTTP middleware or gRPC
interceptor.
Install¶
The module pulls in only cockroachdb/errors and golang-jwt/jwt/v5.
Verify a credential¶
A Verifier
authenticates a single credential string — the bearer token or API key already
extracted from the transport — and returns a verified
Identity:
package main
import (
"context"
"fmt"
"gitlab.com/phpboyscout/go/authn"
)
func main() {
verifier, err := authn.NewAPIKeyVerifier(
authn.KeyEntry{Key: "s3cr3t", Subject: "ci-bot"},
)
if err != nil {
panic(err)
}
id, err := verifier.Verify(context.Background(), "s3cr3t")
if err != nil {
// Authentication failed. Map to 401 / Unauthenticated on the wire and
// log err server-side (redacted) — never return err to the caller.
return
}
fmt.Println(id.Subject) // "ci-bot"
fmt.Println(id.Method) // "apikey"
}
Handle failure correctly¶
Every non-nil error from Verify means the same thing to the client: a generic
401 / Unauthenticated. The wrapped detail is for your server log only. The
ErrUnauthenticated
sentinel lets you distinguish "bad credential" from an infrastructure error (e.g. a
JWKS fetch failure) if you want to log them differently:
id, err := verifier.Verify(ctx, credential)
switch {
case err == nil:
// authenticated
case errors.Is(err, authn.ErrUnauthenticated):
// a rejected credential — expected, log at info/debug
default:
// an operational failure (network, config) — log at error
}
Either way, the response to the client is identical: do not tell an attacker why authentication failed.
Carry the identity down the stack¶
Once authenticated, put the Identity on the context so downstream handlers — and
your authorization check — can read it:
ctx = authn.ContextWithIdentity(ctx, id)
// ... later, in a handler:
if id, ok := authn.IdentityFromContext(ctx); ok {
_ = id.Subject
}
Next steps¶
- Choose & configure a verifier — JWT/OIDC and mTLS, not just API keys.
- Authorize verified requests — turn an
Identityinto an allow/deny decision.