A production-ready OAuth 2.0 toolkit for Go — covering the Authorization Server, a smart CLI client that auto-selects the right flow for the runtime environment, and a Go SDK for secure token storage.
- Three OAuth 2.0 flows — Authorization Code + PKCE, Device Authorization Grant, Client Credentials
- Pluggable auth backends — Local database, HTTP API, GitHub, Gitea, Microsoft Entra ID
- Database support — SQLite (zero-config) and PostgreSQL
- Security hardened — Rate limiting, CSRF protection, session fingerprinting, encrypted cookies
- Audit logging — Async batch writes, sensitive data masking, CSV export
- Observability — Prometheus metrics, health checks, Swagger/OpenAPI docs
- User consent management — Per-app authorization grants with self-service revocation
- Refresh token rotation — Fixed (reusable) or rotation (one-time use) modes
- Single binary — All templates and static assets embedded via
//go:embed
| Repository | Description |
|---|---|
authgate |
OAuth 2.0 Authorization Server — Device Grant + Auth Code Flow with PKCE + Client Credentials |
cli |
Smart OAuth CLI — auto-detects environment and picks the right flow |
oauth-cli |
OAuth 2.0 Authorization Code Flow + PKCE CLI client with browser-based login |
device-cli |
OAuth 2.0 Device Authorization Grant CLI client for headless environments |
sdk-go |
Go SDK — reusable token storage library with OS keyring integration and file-based fallback |
Designed for web apps, SPAs, and mobile apps. PKCE replaces client secrets for public clients, preventing authorization code interception.
sequenceDiagram
participant App as App (SPA / Mobile / CLI)
participant Browser as Browser
participant Server as AuthGate Server
App->>App: Generate code_verifier + code_challenge (S256)
App->>Browser: Redirect to /oauth/authorize?code_challenge=...&state=...
Browser->>Server: GET /oauth/authorize (Login + Consent page)
Server-->>Browser: Render login / consent UI
Browser->>Server: POST /oauth/authorize (user approves)
Server->>Browser: 302 redirect_uri?code=XXXXX&state=...
Browser->>App: Authorization code delivered to callback
App->>App: Verify state parameter (CSRF check)
App->>Server: POST /oauth/token (code + code_verifier + client_id)
Server->>Server: Verify SHA256(code_verifier) == code_challenge
Server-->>App: access_token + refresh_token + expires_in
Security properties:
- PKCE (RFC 7636) — prevents authorization code interception attacks
stateparameter — CSRF protection on the callback- No client secret required for public clients (SPA, mobile, CLI)
- Callback server binds to
127.0.0.1only (CLI mode)
Designed for CLI tools, IoT devices, and headless environments where a browser is not available.
sequenceDiagram
participant CLI as CLI / Device
participant Server as AuthGate Server
participant User as User (Browser on another device)
CLI->>Server: POST /oauth/device/code (client_id + scope)
Server-->>CLI: device_code + user_code + verification_uri + expires_in + interval
Note over CLI: Display to user:<br/>"Visit verification_uri<br/>and enter user_code"
User->>Server: GET /device (enter user_code)
Server-->>User: Render code entry page (login required)
User->>Server: POST /device/verify (user_code)
Server-->>User: ✅ Authorization approved
loop Poll every interval seconds (default 5 s)
CLI->>Server: POST /oauth/token (device_code + grant_type)
alt still waiting
Server-->>CLI: error: authorization_pending
else user approved
Server-->>CLI: access_token + refresh_token + expires_in
else slow down
Server-->>CLI: error: slow_down (increase interval)
else expired / denied
Server-->>CLI: error: expired_token / access_denied
end
end
Polling behavior:
- Respects server-specified interval (default 5 s)
- Exponential backoff on
slow_downresponse (up to 60 s, per RFC 8628) - Device codes expire after 30 minutes
Designed for microservices, daemons, and CI/CD pipelines — machine-to-machine authentication where no user is involved.
sequenceDiagram
participant Service as Service / Daemon
participant Server as AuthGate Server
participant API as Protected API
Service->>Server: POST /oauth/token (client_id + client_secret + grant_type=client_credentials)
Server->>Server: Validate client credentials
Server-->>Service: access_token + expires_in (no refresh_token)
Service->>API: Request with Bearer access_token
API->>Server: GET /oauth/tokeninfo (verify token)
Server-->>API: Token valid + scopes
API-->>Service: Protected resource
Key characteristics:
- No user interaction — the client authenticates with its own credentials
- Requires confidential clients (client_secret must be securely stored server-side)
- No refresh tokens issued — the client requests a new token when the current one expires
- Scoped access — tokens carry only the scopes assigned to the client
sequenceDiagram
participant Client as Client (CLI / Web / IoT)
participant Router as Gin Router + Middleware
participant Handler as Handlers
participant Service as Services
participant Store as Store (GORM)
participant DB as SQLite / PostgreSQL
participant Ext as External Auth (optional)
Client->>Router: HTTP Request
Router->>Router: Rate limiting, CSRF, Session check
Router->>Handler: Route matched
Handler->>Service: Business logic
Service->>Store: Data access
Store->>DB: Query / Write
DB-->>Store: Result
Store-->>Service: Data
opt External auth provider (GitHub / Microsoft / Gitea / HTTP API)
Service->>Ext: Authenticate or validate
Ext-->>Service: User info / Token
end
Service-->>Handler: Result
Handler-->>Client: HTTP Response
| Layer | Technology |
|---|---|
| Language | Go |
| Web Framework | Gin |
| Templates | templ (type-safe Go HTML) |
| ORM | GORM |
| Database | SQLite (default) / PostgreSQL |
| JWT | golang-jwt/jwt |
| Sessions | Encrypted cookies via gin-contrib/sessions |
| API Docs | Swagger/OpenAPI via swaggo |
| Metrics | Prometheus (memory / Redis / Redis-aside cache) |
One binary, two flows, zero configuration — mirrors the strategy used by GitHub CLI, Azure CLI, and Google Cloud SDK.
sequenceDiagram
participant User as User
participant CLI as go-authgate/cli
participant Server as AuthGate Server
User->>CLI: Launch CLI
CLI->>CLI: Detect environment
alt Developer Workstation (browser available)
CLI->>Server: Auth Code + PKCE Flow
Server-->>CLI: access_token + refresh_token
else SSH / Headless (no display)
CLI->>Server: Device Code Flow
Server-->>CLI: access_token + refresh_token
end
CLI-->>User: Authenticated
Service-to-service authentication — no user context needed, no browser involved.
sequenceDiagram
participant CI as CI/CD Pipeline
participant Server as AuthGate Server
participant Deploy as Deploy Service
CI->>Server: POST /oauth/token (client_credentials)
Server-->>CI: access_token
CI->>Deploy: Trigger deploy with Bearer token
Deploy->>Server: Verify token
Server-->>Deploy: Valid (scopes: deploy:trigger)
Deploy-->>CI: Deployment started
No keyboard, no embedded secret — the user authorizes from any nearby device.
sequenceDiagram
participant TV as Smart TV / IoT
participant Server as AuthGate Server
participant Phone as User (Phone / Laptop)
TV->>Server: POST /oauth/device/code
Server-->>TV: user_code + verification_uri
Note over TV: Display short URL + user_code<br/>(or QR code)
Phone->>Server: Visit URL, enter user_code
Server-->>Phone: ✅ Approved
TV->>Server: Poll /oauth/token
Server-->>TV: access_token + refresh_token
Server-side backend holds the client secret; it is never exposed to the browser.
sequenceDiagram
participant User as User (Browser)
participant App as Backend Server
participant Server as AuthGate Server
User->>App: ① Visit app
App->>User: ② Redirect to /oauth/authorize
User->>Server: ③ Login + Consent
Server->>User: ④ 302 redirect_uri?code=XXXXX
User->>App: ⑤ code delivered to callback
App->>Server: ⑥ POST /oauth/token (code + client_secret)
Server-->>App: access_token + refresh_token
No client secret on device — PKCE binds the token exchange to the original requestor.
sequenceDiagram
participant App as SPA / Mobile App
participant Browser as Browser
participant Server as AuthGate Server
App->>App: Generate code_verifier + code_challenge (S256)
App->>Browser: Redirect to /oauth/authorize?code_challenge=...&method=S256
Browser->>Server: Login + Consent
Server->>Browser: 302 redirect_uri?code=XXXXX&state=...
Browser->>App: code delivered to callback
App->>App: Verify state (CSRF check)
App->>Server: POST /oauth/token (code + code_verifier)
Server->>Server: Verify SHA256(code_verifier) == code_challenge
Server-->>App: access_token + refresh_token
| Scenario | Recommended Flow |
|---|---|
| CLI tools, IoT devices, TV apps | Device Code Flow (RFC 8628) |
| Web apps with a server-side backend | Authorization Code Flow (confidential client) |
| Single-page apps (SPA), mobile apps | Authorization Code Flow + PKCE (public client) |
| Microservices, daemons, CI/CD pipelines | Client Credentials Grant (RFC 6749 §4.4) |
| Endpoint | Method | Purpose |
|---|---|---|
/oauth/device/code |
POST | Request device + user codes |
/oauth/authorize |
GET / POST | Authorization consent page |
/oauth/token |
POST | Exchange code / device_code / client_credentials / refresh_token |
/oauth/tokeninfo |
GET | Verify token validity |
/oauth/revoke |
POST | Revoke tokens (RFC 7009) |
/device |
GET | User code entry page (browser) |
/account/sessions |
GET | Manage active sessions |
/account/authorizations |
GET | Manage per-app consent grants |
/health |
GET | Health check with database connectivity test |
/metrics |
GET | Prometheus metrics (optional auth) |
/api/swagger/* |
GET | Interactive API documentation |