### Run the hooks example Source: https://github.com/glincker/theauth-go/blob/main/examples/hooks/README.md Starts the example server that logs every lifecycle hook. ```bash go run ./examples/hooks ``` -------------------------------- ### Setup: copy environment and start Postgres Source: https://github.com/glincker/theauth-go/blob/main/examples/echo-app/README.md Copy the .env.example file to .env and optionally start Postgres on port 5434 using make up. ```bash cp .env.example .env make up # optional: starts Postgres on port 5434 ``` -------------------------------- ### Setup: copy .env and start Postgres Source: https://github.com/glincker/theauth-go/blob/main/examples/stdlib-app/README.md Copies the .env.example file to .env and optionally starts Postgres on port 5435 using make up. ```bash cp .env.example .env make up # optional: starts Postgres on port 5435 ``` -------------------------------- ### Setup commands: copy .env and start Postgres Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-multi-provider/README.md Copy the environment template, fill in the CLIENT_ID and CLIENT_SECRET pairs, and optionally start Postgres on port 5436. ```bash cp .env.example .env # fill in the CLIENT_ID / CLIENT_SECRET pairs you have make up # optional Postgres on port 5436 ``` -------------------------------- ### Run the server with SETUP_TOKEN Source: https://github.com/glincker/theauth-go/blob/main/examples/single-binary-sqlite/README.md Starts the server with a setup token for first admin creation. Without SETUP_TOKEN, a random one is generated and logged once at startup. Environment variables: BASE_URL (default http://localhost:8080), ADDR (:8080), DB_PATH (app.db). ```sh cd examples/single-binary-sqlite SETUP_TOKEN=change-me go run . ``` -------------------------------- ### Run the Twitch OAuth example Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-twitch/README.md Sets the required environment variables and starts the server. The server listens on localhost:8089 by default. ```bash TWITCH_CLIENT_ID=xxx TWITCH_CLIENT_SECRET=yyy go run . ``` -------------------------------- ### Install theauth-go with go get Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Installs the theauth-go library version 2. Requires Go 1.25 or later. ```bash go get github.com/glincker/theauth-go/v2 ``` -------------------------------- ### Setup commands: copy environment and start Postgres Source: https://github.com/glincker/theauth-go/blob/main/examples/gin-app/README.md Copy the environment file and optionally start Postgres on port 5433 using Docker. The Postgres variant is optional; in-memory storage is the default. ```bash cp .env.example .env make up # optional: starts Postgres on port 5433 ``` -------------------------------- ### Run the LinkedIn OAuth example Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-linkedin/README.md Starts the server with LinkedIn client credentials as environment variables. The server listens on localhost:8090. ```bash LINKEDIN_CLIENT_ID=xxx LINKEDIN_CLIENT_SECRET=yyy go run . ``` -------------------------------- ### Quick start: net/http with SQLite storage Source: https://github.com/glincker/theauth-go/blob/main/README.md Complete server setup for net/http and SQLite: it initializes the auth library, migrates the DB, and registers auth and protected routes. Requires Go 1.26 for the sqlite storage module. The database connection string uses pragmas for foreign keys, busy timeout, WAL mode, and immediate transactions. ```go package main import ( "context" "database/sql" "log" "net/http" "github.com/glincker/theauth-go/v2" sqlitestore "github.com/glincker/theauth-go/storage/sqlite" _ "modernc.org/sqlite" ) func main() { ctx := context.Background() db, err := sql.Open("sqlite", "file:app.db?_pragma=foreign_keys(1)&_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_txlock=immediate") if err != nil { log.Fatal(err) } if err := sqlitestore.Migrate(ctx, db); err != nil { log.Fatal(err) } store, err := sqlitestore.New(db) if err != nil { log.Fatal(err) } a, err := theauth.New(theauth.Config{CoreStorage: store, BaseURL: "http://localhost:8080"}) if err != nil { log.Fatal(err) } defer a.Close() mux := http.NewServeMux() mux.Handle("/auth/", a.Handler()) // sign-up, sign-in, magic link, sessions mux.Handle("GET /me", a.RequireAuth()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user, _ := theauth.UserFromContext(r.Context()) w.Write([]byte("hello " + user.Email)) }))) log.Fatal(http.ListenAndServe(":8080", mux)) } ``` -------------------------------- ### Install theauth-go CLI Source: https://github.com/glincker/theauth-go/blob/main/docs/cli.md Installs the theauth-go CLI binary using go install. ```bash go install github.com/glincker/theauth-go/v2/cmd/theauth-go@latest ``` -------------------------------- ### Run the oauth-x example with X_CLIENT_ID and X_CLIENT_SECRET Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-x/README.md Run the example with environment variables for the X client ID and secret. The server listens on http://localhost:8091 and provides a sign-in link. For a public client, leave X_CLIENT_SECRET empty. ```bash X_CLIENT_ID=xxx X_CLIENT_SECRET=yyy go run . ``` -------------------------------- ### Run the oauth-apple example with environment variables Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-apple/README.md Run the example with the required Apple credentials as environment variables. Set APPLE_TEAM_ID, APPLE_KEY_ID, APPLE_BUNDLE_ID, and APPLE_KEY_FILE to your Apple Developer console values. Then open http://localhost:8092 and click the sign-in link. ```bash APPLE_TEAM_ID=A1B2C3D4E5 \ APPLE_KEY_ID=ABCDE12345 \ APPLE_BUNDLE_ID=com.example.app.signin \ APPLE_KEY_FILE=/path/to/AuthKey_ABCDE12345.p8 \ go run . ``` -------------------------------- ### Minimal working example with theauth-go and chi Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md A complete Go program that sets up theauth with in-memory storage, mounts auth routes on a chi router, and protects a /me endpoint with RequireAuth. Uses theauth.UserFromContext to get the current user. ```go package main import ( "net/http" "github.com/glincker/theauth-go/v2" "github.com/glincker/theauth-go/v2/storage/memory" "github.com/go-chi/chi/v5" ) func main() { a, _ := theauth.New(theauth.Config{ Storage: memory.New(), BaseURL: "http://localhost:8080", }) r := chi.NewRouter() a.Mount(r) // wires /auth/* endpoints r.With(a.RequireAuth()).Get("/me", func(w http.ResponseWriter, r *http.Request) { user, _ := theauth.UserFromContext(r.Context()) w.Write([]byte("hello " + user.Email)) }) http.ListenAndServe(":8080", r) } ``` -------------------------------- ### Run the Slack OAuth example Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-slack/README.md Command to run the example with required environment variables SLACK_CLIENT_ID and SLACK_CLIENT_SECRET. The server listens on http://localhost:8086. ```bash SLACK_CLIENT_ID=xxx SLACK_CLIENT_SECRET=yyy go run . ``` -------------------------------- ### Run oauth-gitlab example Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-gitlab/README.md Run the example with GitLab credentials. For self-hosted GitLab, set GITLAB_BASE_URL to your instance URL. ```bash GITLAB_CLIENT_ID=xxx GITLAB_CLIENT_SECRET=yyy go run . # self-hosted: add GITLAB_BASE_URL=https://git.example.com ``` -------------------------------- ### Run the chi-app example Source: https://github.com/glincker/theauth-go/blob/main/examples/chi-app/README.md Commands to run the example with in-memory storage or Postgres storage. For Postgres, run migrations from ../../storage/postgres/migrations first. The default email sender is 'noop', which prints magic-link URLs to stdout. ```bash # in-memory storage (no DB required) go run main.go # Postgres storage (run migrations from ../../storage/postgres/migrations first) DATABASE_URL=postgres://user:pass@localhost:5432/yourdb go run main.go ``` -------------------------------- ### Run the Facebook OAuth example Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-facebook/README.md Run the example with the required Facebook credentials. The app listens on localhost:8085 and state is held in memory, so users and sessions are lost on restart. ```bash FACEBOOK_CLIENT_ID=xxx FACEBOOK_CLIENT_SECRET=yyy go run . ``` -------------------------------- ### GET /auth/providers/github/start Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Starts the GitHub OAuth flow. Redirects to GitHub for user authorization. ```APIDOC ## GET /auth/providers/github/start ### Description Starts the GitHub OAuth flow. Redirects to GitHub for user authorization. ### Method GET ### Endpoint /auth/providers/github/start ### Parameters None ### Response #### Success Response (302) - Redirect to GitHub authorization page. ``` -------------------------------- ### Run the server Source: https://github.com/glincker/theauth-go/blob/main/examples/echo-app/README.md Starts the Echo server on port 8082. Auth routes are mounted under /auth/. ```bash make run ``` -------------------------------- ### GET /auth/providers/{name}/start Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Starts the OAuth flow for the specified provider (google, microsoft, discord). Redirects to the provider's authorization page. ```APIDOC ## GET /auth/providers/{name}/start ### Description Starts the OAuth flow for the specified provider (google, microsoft, discord). Redirects to the provider's authorization page. ### Method GET ### Endpoint /auth/providers/{name}/start ### Parameters #### Path Parameters - **name** (string) - Required - The provider name (e.g., google, microsoft, discord). ### Response #### Success Response (302) - Redirect to provider authorization page. ``` -------------------------------- ### Create admin, mint token, call API Source: https://github.com/glincker/theauth-go/blob/main/examples/single-binary-sqlite/README.md Commands to create the first admin with the setup token, mint a scoped API token, and call the protected /api/whoami endpoint with a bearer token. The token is printed once when minted. ```sh curl -c jar -X POST localhost:8080/auth/email-password/signup \ -H 'X-Setup-Token: change-me' -H 'Content-Type: application/json' \ -d '{"email":"admin@example.com","password":"correct horse battery staple"}' curl -b jar -X POST localhost:8080/auth/tokens -H 'Content-Type: application/json' \ -d '{"name":"ci","abilities":["read"]}' # prints the token once curl -H "Authorization: Bearer $TOKEN" localhost:8080/api/whoami ``` -------------------------------- ### Create a built-in provider with generic.NewByName Source: https://github.com/glincker/theauth-go/blob/main/docs/provider-catalog.md Creates a provider by name from the built-in table, using environment variables for credentials. The example uses the 'spotify' provider. ```go p, err := generic.NewByName("spotify", generic.Config{ ClientID: os.Getenv("SPOTIFY_ID"), ClientSecret: os.Getenv("SPOTIFY_SECRET"), }) ``` -------------------------------- ### Email OTP example Source: https://github.com/glincker/theauth-go/blob/main/docs/otp.md Shows how to create an email OTP with `otp.New`, issue a code, and verify it. Uses `kv.NewMemory()` cache and `email.Noop` sender. The secret should be 32 or more random bytes. `Verify` returns `ErrInvalid` for wrong or expired codes and `ErrLocked` for too many wrong guesses. ```go package main import ( "context" "errors" "log" "github.com/glincker/theauth-go/v2/email" "github.com/glincker/theauth-go/v2/kv" "github.com/glincker/theauth-go/v2/otp" ) func main() { ctx := context.Background() secret := []byte("replace-with-32-or-more-random-bytes") emailOTP, err := otp.New(otp.Config{ Channel: otp.ChannelEmail, Cache: kv.NewMemory(), Sender: otp.EmailSender{Mailer: email.Noop{}}, Secret: secret, }) if err != nil { log.Fatal(err) } if err := emailOTP.Issue(ctx, "login", "ann@example.com"); err != nil { log.Fatal(err) } err = emailOTP.Verify(ctx, "login", "ann@example.com", "123456") switch { case errors.Is(err, otp.ErrInvalid): // wrong or expired code case errors.Is(err, otp.ErrLocked): // too many wrong guesses } } ``` -------------------------------- ### Run the MCP resource server with environment variables Source: https://github.com/glincker/theauth-go/blob/main/examples/mcp-server/README.md Starts the server with the required MCP_RESOURCE, MCP_JWKS_URI, MCP_INTROSPECT_URI, MCP_CLIENT_ID, and MCP_CLIENT_SECRET environment variables. The server listens on :8090 by default, overridable with MCP_LISTEN_ADDR. ```bash MCP_RESOURCE=https://mcp.example.com \ MCP_JWKS_URI=https://as.example.com/oauth/jwks \ MCP_INTROSPECT_URI=https://as.example.com/oauth/introspect \ MCP_CLIENT_ID=mcp-resource-client \ MCP_CLIENT_SECRET=change-me \ go run . ``` -------------------------------- ### SMS OTP example Source: https://github.com/glincker/theauth-go/blob/main/docs/otp.md Shows how to create an SMS OTP with `otp.New` using `otp.TwilioSender`. Requires environment variables `TWILIO_SID`, `TWILIO_TOKEN`, and `TWILIO_FROM`. Phone numbers are normalized by removing spaces, dashes, and parentheses; send in E.164 form. ```go smsOTP, _ := otp.New(otp.Config{ Channel: otp.ChannelSMS, Cache: cache, Secret: secret, Length: 6, Sender: otp.TwilioSender{ AccountSID: os.Getenv("TWILIO_SID"), AuthToken: os.Getenv("TWILIO_TOKEN"), From: os.Getenv("TWILIO_FROM"), }, }) ``` -------------------------------- ### Run doctor-admin server Source: https://github.com/glincker/theauth-go/blob/main/examples/doctor-admin/README.md Starts the doctor-admin server on :8080 with in-memory storage and the bootstrap flow enabled. BASE_URL overrides the origin. ```bash go run . ``` -------------------------------- ### Quick start: Mount theAuth into a chi router Source: https://github.com/glincker/theauth-go/blob/main/llms.txt Initializes theAuth with in-memory storage and a base URL, then mounts it onto a chi router. The mount exposes /auth/* endpoints for magic-link, email-password, OAuth, passkeys, TOTP, and SAML. Requires Go 1.25+ (storage/sqlite requires Go 1.26). ```go a, _ := theauth.New(theauth.Config{ Storage: memory.New(), BaseURL: "http://localhost:8080", }) r := chi.NewRouter() a.Mount(r) // /auth/* magic-link, email-password, OAuth, passkeys, TOTP, SAML ``` -------------------------------- ### Run CLI login commands with MYCLI_SERVER Source: https://github.com/glincker/theauth-go/blob/main/examples/cli-login/README.md Run the CLI login, whoami, get, and logout commands against a theauth-go server. Set the MYCLI_SERVER environment variable to the server URL. The server must run theauth-go with Config.APITokens. ```bash MYCLI_SERVER=https://auth.example.com go run . login MYCLI_SERVER=https://auth.example.com go run . whoami MYCLI_SERVER=https://auth.example.com go run . get /api/things MYCLI_SERVER=https://auth.example.com go run . logout ``` -------------------------------- ### Token Response with authorization_details Source: https://github.com/glincker/theauth-go/blob/main/docs/rich-authorization-requests.md Example of a token response that includes the `authorization_details` claim. ```APIDOC ## Token Response ### Description The token response includes the `authorization_details` claim with the details the user approved. ### Response Example ```json {"access_token": "...", "token_type": "Bearer", "scope": "pay", "authorization_details": [{"type": "payment", "actions": ["initiate"], "...": "..."}]} ``` ``` -------------------------------- ### Go: Run security doctor and check severity Source: https://github.com/glincker/theauth-go/blob/main/docs/SECURITY-DOCTOR.md Calls auth.Doctor(ctx) to get a Report, then checks if the maximum severity is at least SeverityHigh to trigger an alert. ```go rep := auth.Doctor(ctx) if rep.MaxSeverity().Rank() >= theauth.SeverityHigh.Rank() { /* alert */ } ``` -------------------------------- ### Skip annotation for noisy benchmark in benchgate/curated.txt Source: https://github.com/glincker/theauth-go/blob/main/docs/BENCHMARKS.md Add a line starting with `# gate:skip` in `benchgate/curated.txt` to exclude a benchmark from the regression gate. The benchmark still runs with `go test -bench=.` directly. A tracking issue reference is required. ```text # gate:skip BenchmarkFoo -- high variance on shared runners; tracked in issue #NNN ``` -------------------------------- ### Apple redirect URL for local testing Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-apple/README.md Configure Sign In with Apple on the Services ID with this redirect URL. Apple requires HTTPS outside of production-like setups, so use a tunnel such as ngrok for local testing and set BASE_URL to the tunnel origin. ```text http://localhost:8092/auth/providers/apple/callback ``` -------------------------------- ### GET /oauth/device Source: https://github.com/glincker/theauth-go/blob/main/docs/device-authorization.md Renders the verification page or returns JSON when the client sends Accept: application/json or a JSON body. Anonymous browsers are redirected to LoginURL with a next parameter; anonymous JSON callers get 401. ```APIDOC ## GET /oauth/device ### Description Renders the verification page or returns JSON when the client sends Accept: application/json or a JSON body. Anonymous browsers are redirected to LoginURL with a next parameter; anonymous JSON callers get 401. ### Method GET ### Endpoint /oauth/device ### Parameters #### Query Parameters - **user_code** (string) - Required - The user code to look up. ### Response #### Success Response (200) - **client_name** (string) - The client name. - **scope** (string) - The requested scope. - **expires_at** (string) - The expiration timestamp. #### Response Example { "client_name": "...", "scope": "deploy", "expires_at": "..." } ``` -------------------------------- ### Get authenticator display name for a stored passkey Source: https://github.com/glincker/theauth-go/blob/main/docs/passkey-attestation.md Calls PasskeyAuthenticatorName with a credential's AAGUID to get a human-readable name, returning an empty string when the AAGUID is unknown. The AAGUID is already stored on the credential row, so no migration is needed. ```go label := a.PasskeyAuthenticatorName(cred.AAGUID) // "" when unknown ``` -------------------------------- ### POST /auth/webauthn/register/begin Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Begins WebAuthn registration. Returns a challenge for the client to sign. ```APIDOC ## POST /auth/webauthn/register/begin ### Description Begins WebAuthn registration. Returns a challenge for the client to sign. ### Method POST ### Endpoint /auth/webauthn/register/begin ### Parameters None ### Response #### Success Response (200) - **challenge** (string) - The WebAuthn challenge. - **rp** (object) - Relying party info. - **user** (object) - User info. ``` -------------------------------- ### POST /auth/webauthn/login/begin Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Begins WebAuthn login. Returns a challenge for the client to sign. ```APIDOC ## POST /auth/webauthn/login/begin ### Description Begins WebAuthn login. Returns a challenge for the client to sign. ### Method POST ### Endpoint /auth/webauthn/login/begin ### Parameters None ### Response #### Success Response (200) - **challenge** (string) - The WebAuthn challenge. - **rp** (object) - Relying party info. - **user** (object) - User info. ``` -------------------------------- ### Use SQL stores with Postgres via pgx Source: https://github.com/glincker/theauth-go/blob/main/docs/pluggable-stores.md Sets up SQL-backed stores using Postgres through pgx. Requires a *sql.DB from stdlib.OpenDBFromPool. EnsureSchema creates the theauth_kv table and is safe to repeat. ```go db := stdlib.OpenDBFromPool(pool) // github.com/jackc/pgx/v5/stdlib store, _ := sqlkv.New(db, sqlkv.Postgres) _ = store.EnsureSchema(ctx) // one table, theauth_kv; safe to repeat cfg.Stores = kv.FromCache(store) ``` -------------------------------- ### GET /auth/webauthn/credentials Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Lists the WebAuthn credentials for the authenticated user. ```APIDOC ## GET /auth/webauthn/credentials ### Description Lists the WebAuthn credentials for the authenticated user. ### Method GET ### Endpoint /auth/webauthn/credentials ### Parameters None ### Response #### Success Response (200) - **credentials** (array) - List of credential objects. ``` -------------------------------- ### Go: Try enumeration-safe passwordless endpoints Source: https://github.com/glincker/theauth-go/blob/main/docs/enumeration-safe-passwordless.md Initializes theauth with a config, mounts it on a chi router, and shows a curl command to test that both endpoints return 200 {"sent":true} for unknown emails. ```go a, _ := theauth.New(theauth.Config{ /* storage, sender, signing key */ }) r := chi.NewRouter() a.Mount(r) // Both calls return 200 {"sent":true}, registered or not: // curl -X POST localhost:8080/auth/email-password/forgot \ // -d '{"email":"nobody@example.com"}' ``` -------------------------------- ### Teardown: stop Postgres Source: https://github.com/glincker/theauth-go/blob/main/examples/echo-app/README.md Stops the Postgres container started with make up. ```bash make down ``` -------------------------------- ### Use Redis stores with go-redis adapter Source: https://github.com/glincker/theauth-go/blob/main/docs/pluggable-stores.md Sets up Redis-backed stores using a minimal adapter that implements the Eval method required by kv/redis. Every operation is a single Lua script, atomic and one round trip. ```go type goRedis struct{ c redis.Scripter } // github.com/redis/go-redis/v9 func (g goRedis) Eval(ctx context.Context, script string, keys []string, args ...any) (any, error) { v, err := g.c.Eval(ctx, script, keys, args...).Result() if errors.Is(err, redis.Nil) { return nil, nil } return v, err } cfg.Stores = kv.FromCache(kvredis.New(goRedis{rdb}, "theauth:")) ``` -------------------------------- ### Authorize Request with authorization_details Source: https://github.com/glincker/theauth-go/blob/main/docs/rich-authorization-requests.md Example of an authorize request that includes the `authorization_details` parameter. ```APIDOC ## Authorize Request ### Description Include `authorization_details` in the authorize request to carry structured authorization details. ### Method GET ### Endpoint /oauth/authorize ### Parameters #### Query Parameters - **response_type** (string) - Required - Must be `code`. - **client_id** (string) - Required - Client identifier. - **resource** (string) - Required - Resource identifier. - **scope** (string) - Required - Scope value. - **code_challenge** (string) - Required - PKCE challenge. - **code_challenge_method** (string) - Required - PKCE method (e.g., S256). - **authorization_details** (string) - Required - JSON array of objects, each with a `type` from `RAR.Types`. ### Request Example ``` GET /oauth/authorize?response_type=code&client_id=...&resource=https://bank.example.com &scope=pay&code_challenge=...&code_challenge_method=S256 &authorization_details=[{"type":"payment","actions":["initiate"], "locations":["https://bank.example.com"],"instructedAmount":{"currency":"EUR","amount":"123.50"}}] ``` ``` -------------------------------- ### Generate OpenAPI from custom router Source: https://github.com/glincker/theauth-go/blob/main/docs/cli.md Generates an OpenAPI 3.1 document for the routes mounted on a chi router, including optional features. Uses chi.Walk to read routes, so the document reflects the actual mounted routes. Well-known auth routes get summaries, tags, and security requirements; other routes get generated summaries and operation IDs. ```go r := chi.NewRouter() auth.Mount(r) doc, err := openapi.Generate(r, openapi.Info{ Title: "My API", Version: "1.0.0", ServerURL: "https://auth.example.com", }) ``` -------------------------------- ### Run the oauth-bitbucket server Source: https://github.com/glincker/theauth-go/blob/main/examples/oauth-bitbucket/README.md Run the server with Bitbucket client credentials. The server listens on http://localhost:8088 and state is held in memory, so users and sessions are lost on restart. ```bash BITBUCKET_CLIENT_ID=xxx BITBUCKET_CLIENT_SECRET=yyy go run . ``` -------------------------------- ### Sign CLI in with device grant Source: https://github.com/glincker/theauth-go/blob/main/examples/single-binary-sqlite/README.md Builds the mycli binary, sets the server URL, and performs device login, whoami, API call, and logout. The logout revokes the token server side and deletes it locally. ```sh go build -o mycli ./cmd/mycli export MYCLI_SERVER=http://localhost:8080 ./mycli login # prints a code; approve it at http://localhost:8080/device ./mycli whoami ./mycli get /api/whoami ./mycli logout # revokes the token server side and deletes it locally ``` -------------------------------- ### GET /auth/providers/github/callback Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Handles the GitHub OAuth callback. Exchanges the authorization code for tokens and sets a session cookie. ```APIDOC ## GET /auth/providers/github/callback ### Description Handles the GitHub OAuth callback. Exchanges the authorization code for tokens and sets a session cookie. ### Method GET ### Endpoint /auth/providers/github/callback ### Parameters #### Query Parameters - **code** (string) - Required - The authorization code from GitHub. - **state** (string) - Required - The state parameter for CSRF protection. ### Response #### Success Response (200) - Session cookie set. #### Error Response - **code** (string) - Error code. - **message** (string) - Error message. ``` -------------------------------- ### Configure shared stores for multi-replica deployments Source: https://github.com/glincker/theauth-go/blob/main/README.md Shares rate limits, DPoP replay records, and the CIMD cache across replicas via the existing database. Uses pgx stdlib with sqlkv.Postgres and wraps the store with kv.FromCache; Redis or a custom kv.RateLimiter / kv.ReplayCache / kv.Cache can be substituted. Requires calling kvStore.EnsureSchema(ctx) before use. ```go db := stdlib.OpenDBFromPool(pool) // pgx stdlib kvStore, _ := sqlkv.New(db, sqlkv.Postgres) _ = kvStore.EnsureSchema(ctx) auth, _ := theauth.New(theauth.Config{ // ... Stores: kv.FromCache(kvStore), // or kv/redis, or your own kv.RateLimiter / kv.ReplayCache / kv.Cache }) ``` -------------------------------- ### GET /auth/admin/doctor Source: https://github.com/glincker/theauth-go/blob/main/docs/SECURITY-DOCTOR.md Returns the security report as JSON to a caller holding the root ability, by session or bearer token. Requires Config.APITokens to be set. ```APIDOC ## GET /auth/admin/doctor ### Description Returns the security doctor report as JSON. Requires the caller to hold the root ability, authenticated by session or bearer token, and Config.APITokens must be set. ### Method GET ### Endpoint /auth/admin/doctor ### Authentication - Requires Config.APITokens to be set - Caller must hold the `root` ability - Authenticate by session or bearer token ### Response #### Success Response (200) Returns the security report as JSON: a summary count per severity and a list of findings, most serious first. Findings carry counts and setting names only, never secrets, tokens or user identifiers. #### Response Example { "summary": { "critical": 0, "high": 1, "medium": 2, "low": 1, "info": 3 }, "findings": [ { "id": "csrf.disabled", "severity": "high", "message": "CSRF protection disabled" } ] } ``` -------------------------------- ### Tag root module and sub-modules in order Source: https://github.com/glincker/theauth-go/blob/main/docs/RELEASING.md Run from an up to date checkout of the default branch. First fetch and list existing tags to check the next number, then tag the root module with a signed, annotated tag, push it, and wait for proxy.golang.org to serve it before continuing. ```bash git fetch origin git tag -l 'mcpresource/*' 'storage/*' 'audit/*' # check the next number first # 1. root module (signed, annotated) git tag -s v2.6.0 -m "v2.6.0" git push origin v2.6.0 # 2. wait for proxy.golang.org to serve it GOWORK=off GOPROXY=https://proxy.golang.org go list -m github.com/glincker/theauth-go/v2@v2.6.0 ``` -------------------------------- ### GET /.well-known/openid-configuration{/path} Source: https://github.com/glincker/theauth-go/blob/main/docs/mcp-metadata-paths.md Retrieves the OpenID Connect configuration document for a given issuer. The path component of the issuer is appended after the well-known segment. ```APIDOC ## [GET] /.well-known/openid-configuration{/path} ### Description Retrieves the OpenID Connect configuration document for a given issuer. The path component of the issuer is appended after the well-known segment. ### Method GET ### Endpoint `/.well-known/openid-configuration{/path}` ### Parameters #### Path Parameters - **path** (string) - Optional - The path component of the issuer. If the issuer has no path, the bare `/.well-known/openid-configuration` is used. ### Response #### Success Response (200) - **openid_configuration** (object) - The OpenID Connect configuration document. #### Response Example ```json { "openid_configuration": { "issuer": "https://auth.example.com/tenant1" } } ``` ### Notes - A suffix that does not equal the issuer path returns 404. - Returns the same document as the OAuth metadata route. ``` -------------------------------- ### GET /.well-known/oauth-authorization-server{/path} Source: https://github.com/glincker/theauth-go/blob/main/docs/mcp-metadata-paths.md Retrieves the authorization server metadata document for a given issuer. The path component of the issuer is appended after the well-known segment. ```APIDOC ## [GET] /.well-known/oauth-authorization-server{/path} ### Description Retrieves the authorization server metadata document for a given issuer. The path component of the issuer is appended after the well-known segment. ### Method GET ### Endpoint `/.well-known/oauth-authorization-server{/path}` ### Parameters #### Path Parameters - **path** (string) - Optional - The path component of the issuer. If the issuer has no path, the bare `/.well-known/oauth-authorization-server` is used. ### Response #### Success Response (200) - **authorization_server_metadata** (object) - The metadata document for the authorization server. #### Response Example ```json { "authorization_server_metadata": { "issuer": "https://auth.example.com/tenant1" } } ``` ### Notes - A suffix that does not equal the issuer path returns 404. - The `openid-configuration` routes return the same document as the OAuth metadata route. ``` -------------------------------- ### Use the observability-otel bridge with theauth-go Source: https://github.com/glincker/theauth-go/blob/main/examples/observability-otel/README.md Import the bridge and OpenTelemetry packages, create a tracer, wrap it with otelbridge.New, and pass it via theauth.Hooks to theauth.New. The bridge lives in its own go.mod so importing theauth-go does not pull in the OpenTelemetry SDK. ```go import ( "github.com/glincker/theauth-go/v2" otelbridge "github.com/glincker/theauth-go/examples/observability-otel" "go.opentelemetry.io/otel" ) tracer := otel.Tracer("my-service") hooks := &theauth.Hooks{Tracer: otelbridge.New(tracer)} auth, _ := theauth.New(theauth.Config{ Storage: store, BaseURL: "https://example.com", Observability: hooks, }) ``` -------------------------------- ### GET /auth/providers/{name}/callback Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Handles the OAuth callback for the specified provider. Exchanges the authorization code for tokens and sets a session cookie. ```APIDOC ## GET /auth/providers/{name}/callback ### Description Handles the OAuth callback for the specified provider. Exchanges the authorization code for tokens and sets a session cookie. ### Method GET ### Endpoint /auth/providers/{name}/callback ### Parameters #### Path Parameters - **name** (string) - Required - The provider name. #### Query Parameters - **code** (string) - Required - The authorization code from the provider. - **state** (string) - Required - The state parameter for CSRF protection. ### Response #### Success Response (200) - Session cookie set. #### Error Response - **code** (string) - Error code. - **message** (string) - Error message. ``` -------------------------------- ### GET /admin/v1/organizations/{orgID}/registration-tokens Source: https://github.com/glincker/theauth-go/blob/main/docs/registration-tokens.md Lists all registration tokens for the specified organization. Requires the `agents:admin` permission. The response does not include token secrets. ```APIDOC ## GET /admin/v1/organizations/{orgID}/registration-tokens ### Description Lists all registration tokens for the specified organization. Requires the `agents:admin` permission. The response does not include token secrets. ### Method GET ### Endpoint /admin/v1/organizations/{orgID}/registration-tokens ### Parameters #### Path Parameters - **orgID** (string) - Required - The organization ID. ### Response #### Success Response (200) - **data** (array of objects) - List of registration tokens. Each token object contains metadata but no secrets. #### Response Example ```json { "data": [ { "id": "...", "label": "build agent fleet", "scopes": ["files.read"], "grant_types": ["client_credentials"], "max_uses": 1, "expires_at": "..." } ] } ``` ``` -------------------------------- ### Use in-memory stores (default) Source: https://github.com/glincker/theauth-go/blob/main/docs/pluggable-stores.md Creates the default in-memory stores for a single process. ```go cfg.Stores = kv.NewMemory().Stores() ``` -------------------------------- ### Run smoke test Source: https://github.com/glincker/theauth-go/blob/main/examples/single-binary-sqlite/README.md Runs go vet and go test. TestSmoke boots the server on a random port with a temporary SQLite file and checks signup, whoami, and device flow over real HTTP. ```sh go vet ./... && go test ./... ``` -------------------------------- ### GET /.well-known/oauth-protected-resource{/path} Source: https://github.com/glincker/theauth-go/blob/main/docs/mcp-metadata-paths.md Retrieves the protected resource metadata document for a given resource identifier. The path component of the resource identifier is appended after the well-known segment. ```APIDOC ## [GET] /.well-known/oauth-protected-resource{/path} ### Description Retrieves the protected resource metadata document for a given resource identifier. The path component of the resource identifier is appended after the well-known segment. ### Method GET ### Endpoint `/.well-known/oauth-protected-resource{/path}` ### Parameters #### Path Parameters - **path** (string) - Optional - The path component of the resource identifier. If the resource has no path, the bare `/.well-known/oauth-protected-resource` is used. ### Response #### Success Response (200) - **resource_metadata** (object) - The metadata document for the protected resource. #### Response Example ```json { "resource_metadata": { "resource": "https://mcp.example.com/api/mcp" } } ``` ### Notes - A query component in the resource identifier is preserved after the inserted path. - A trailing slash on the request is ignored. - The authorization server matches the suffix against the path of each configured resource, allowing resources on different origins. ``` -------------------------------- ### theauth-go CLI and OpenAPI Source: https://github.com/glincker/theauth-go/blob/main/docs/cli.md Documentation for the theauth-go CLI commands and the openapi.Generate library function. ```APIDOC ## [CLI] theauth-go secret ### Description Prints random key material for use as secrets. Supports configurable byte length and output formats. ### Command `theauth-go secret` ### Flags - `--bytes` (integer) - Optional - Number of bytes to generate, from 16 to 128. Default is 32. - `--format` (string) - Optional - Output format: `base64url`, `hex`, or `env`. - `--name` (string) - Optional - Variable name to use when `--format` is `env`. ### Example ``` theauth-go secret --bytes 64 --format hex ``` ``` ```APIDOC ## [CLI] theauth-go openapi ### Description Prints an OpenAPI 3.1 document for the default route set. Useful for generating client documentation or gateway configuration. ### Command `theauth-go openapi` ### Flags - `--base-url` (string) - Optional - Base URL to use in the OpenAPI document. - `--title` (string) - Optional - Title for the OpenAPI document. - `--version` (string) - Optional - Version for the OpenAPI document. - `--out` (string) - Optional - Output file path for the document. ### Example ``` theauth-go openapi --base-url https://auth.example.com --title "My API" --version 1.0.0 --out openapi.json ``` ``` ```APIDOC ## [Go] openapi.Generate ### Description Generates an OpenAPI 3.1 document from a chi router, including optional features. Routes are read with `chi.Walk`, ensuring the document matches the mounted routes. ### Function Signature `func Generate(r chi.Router, info openapi.Info) (*openapi.Document, error)` ### Parameters - `r` (chi.Router) - Required - The router to document. - `info` (openapi.Info) - Required - Configuration for the document, including `Title`, `Version`, and `ServerURL`. ### Example ```go r := chi.NewRouter() auth.Mount(r) doc, err := openapi.Generate(r, openapi.Info{ Title: "My API", Version: "1.0.0", ServerURL: "https://auth.example.com", }) ``` ``` -------------------------------- ### POST /auth/totp/enroll/begin Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Begins TOTP enrollment. Returns a secret and QR code URI for the user to scan. ```APIDOC ## POST /auth/totp/enroll/begin ### Description Begins TOTP enrollment. Returns a secret and QR code URI for the user to scan. ### Method POST ### Endpoint /auth/totp/enroll/begin ### Parameters None ### Response #### Success Response (200) - **secret** (string) - The TOTP secret. - **qr_uri** (string) - The otpauth:// URI for QR code generation. ``` -------------------------------- ### Try the magic link flow Source: https://github.com/glincker/theauth-go/blob/main/examples/stdlib-app/README.md Sends a POST request to /auth/magic-link with a JSON body, then uses the verify URL from server logs to authenticate and access /me. ```bash curl -X POST http://localhost:8083/auth/magic-link \ -H 'Content-Type: application/json' \ -d '{"email":"you@example.com"}' # paste the verify URL from the server logs into your browser, then: curl --cookie-jar c.txt --cookie c.txt http://localhost:8083/me ``` -------------------------------- ### Apply migration bundle Source: https://github.com/glincker/theauth-go/blob/main/docs/MIGRATING-FROM-KEYCLOAK.md Applies the migration bundle to the storage. This is the actual migration step after validation and dry-run. ```bash theauth-migrate keycloak --input bundle.json --apply --storage postgres --dsn "$DSN" ``` -------------------------------- ### Run benchmark suite Source: https://github.com/glincker/theauth-go/blob/main/internal/bench/BASELINES.md Command to re-run all benchmarks with memory profiling, excluding tests, with a 3-second benchtime. ```bash go test -bench=. -benchmem -run=^$ -benchtime=3s ./internal/bench/... ``` -------------------------------- ### theauth.New Source: https://github.com/glincker/theauth-go/blob/main/docs/AGENTS.md Creates a new theauth instance with the provided configuration. Returns an error if configuration is invalid. ```APIDOC ## theauth.New ### Description Creates a new theauth instance with the provided configuration. Returns an error if configuration is invalid. ### Method Constructor ### Parameters - **config** (theauth.Config) - Required - Configuration for the auth instance. ### Returns - **a** (*theauth.Auth) - The auth instance. - **err** (error) - Error if creation fails. ### Example ```go a, _ := theauth.New(theauth.Config{ Storage: memory.New(), BaseURL: "http://localhost:8080", }) ``` ``` -------------------------------- ### Device authorization JSON API endpoints Source: https://github.com/glincker/theauth-go/blob/main/docs/device-authorization.md JSON API for apps that bring their own UI. Send Accept: application/json or a JSON body. The GET endpoint returns client info and scope; the POST endpoint approves, denies, or looks up a user code. ```http GET /oauth/device?user_code=BCDF-GHJK -> {"client_name": "...", "scope": "...", "expires_at": "..."} POST /oauth/device {"user_code": "BCDF-GHJK", "action": "approve" | "deny" | "lookup"} -> {"status": "approved"} ``` -------------------------------- ### Admin API endpoints for registration tokens Source: https://github.com/glincker/theauth-go/blob/main/docs/registration-tokens.md Admin API endpoints under /admin/v1/organizations/{orgID} for managing registration tokens. Requires Config.Admin and RBAC with agents:admin permission. POST returns 201 with the token once; GET lists tokens without secrets; DELETE revokes a token. ```http POST /registration-tokens {"label","scopes","grant_types","max_uses","ttl_seconds"} -> 201, includes "token" once GET /registration-tokens -> {"data": [...]} (this organization's tokens, no secrets) DELETE /registration-tokens/{id} -> 204 ``` -------------------------------- ### Enable RAR in AuthorizationServerConfig Source: https://github.com/glincker/theauth-go/blob/main/docs/rich-authorization-requests.md Configure the authorization server to accept authorization_details. Requires RAR.Types; MaxDetails is optional and defaults to 16. Without RAR, requests carrying authorization_details are refused with invalid_authorization_details. ```go AuthorizationServer: &theauth.AuthorizationServerConfig{ Issuer: "https://auth.example.com", Resources: []theauth.ProtectedResource{{Identifier: "https://bank.example.com", Scopes: []string{"pay"}}}, RAR: &theauth.RARConfig{ Types: []string{"payment", "account_information"}, // required MaxDetails: 16, // optional, default 16 }, } ``` -------------------------------- ### Enable legacy hash support in theauth.Config Source: https://github.com/glincker/theauth-go/blob/main/docs/MIGRATING-FROM-KEYCLOAK.md Turns on legacy bcrypt/PBKDF2 hash support for the migration window. The setting name is historical; it covers bcrypt (Auth0) and PBKDF2 (Keycloak). It also rejects PBKDF2 hashes with more than 5,000,000 iterations. ```go theauth.Config{ // ... PasswordPolicy: theauth.PasswordPolicyConfig{AllowLegacyBcrypt: true}, } ``` -------------------------------- ### Register and delegate an agent identity in Go Source: https://github.com/glincker/theauth-go/blob/main/README.md Initialize theauth with an authorization server and agent identity, then register an agent, obtain a client-credentials token, and exchange a user token for a delegated token. Requires storage with agent identity support (memory, Postgres, MySQL) and a 32-byte EncryptionKey. The agent secret is shown once; revocation is available via RevokeDelegation, SuspendAgent, or RevokeAgent. ```go package main import ( "context" "log" "github.com/glincker/theauth-go/v2" "github.com/glincker/theauth-go/v2/storage/memory" ) func main() { ctx := context.Background() a, err := theauth.New(theauth.Config{ Storage: memory.New(), BaseURL: "https://as.example.com", EncryptionKey: []byte("0123456789abcdef0123456789abcdef"), // 32 bytes, load from your secrets manager AuthorizationServer: &theauth.AuthorizationServerConfig{ Issuer: "https://as.example.com", Resources: []theauth.ProtectedResource{ {Identifier: "https://api.example.com", Scopes: []string{"read", "write"}}, }, }, AgentIdentity: &theauth.AgentConfig{}, }) if err != nil { log.Fatal(err) } defer a.Close() // The human who owns the agent. In a real app this is a signed-in user. user, err := a.UserByID(ctx, ownerID) if err != nil { log.Fatal(err) } // 1. Create the agent and a delegation grant for "read" on one resource. reg, err := a.RegisterAgent(ctx, theauth.RegisterAgentInput{ OwnerID: user.ID, Name: "report-bot", Scope: []string{"read"}, Resource: "https://api.example.com", }) if err != nil { log.Fatal(err) } // reg.Secret.ClientID and reg.Secret.Secret are the agent's credential. // The secret is shown once and only its Argon2id hash is stored. // 2. The agent authenticates as itself (client credentials). self, err := a.ClientCredentialsToken(ctx, theauth.TokenRequest{ GrantType: "client_credentials", ClientID: reg.Secret.ClientID, ClientSecret: reg.Secret.Secret, Resource: "https://api.example.com", Scope: []string{"read"}, }) if err != nil { log.Fatal(err) } _ = self.AccessToken // sub is "agent:" // 3. The agent trades a user's access token for a delegated one // (RFC 8693). The result carries the user as sub and the agent in act. delegated, err := a.ExchangeToken(ctx, theauth.TokenExchangeRequest{ ClientID: reg.Secret.ClientID, ClientSecret: reg.Secret.Secret, SubjectToken: userAccessToken, // issued to the user by this authorization server SubjectTokenType: "urn:ietf:params:oauth:token-type:access_token", Resource: "https://api.example.com", Scope: []string{"read"}, }) if err != nil { log.Fatal(err) } log.Println("delegated token expires in", delegated.ExpiresIn, "seconds") } ```