Skip to content

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

go get gitlab.com/phpboyscout/go/authn

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