Pure shell implementation of OAuth 2.0 Device Authorization Grant (RFC 8628). No SDK or language runtime required — only curl and jq.
Uses the Device Code flow exclusively. The user authenticates on a separate device (phone, laptop) by visiting a URL and entering a code.
- Bash
curl— HTTP clientjq— JSON processor (install)- An AuthGate server with a configured OAuth client
| Variable | Required | Description |
|---|---|---|
AUTHGATE_URL |
Yes | AuthGate server URL |
CLIENT_ID |
Yes | OAuth 2.0 client identifier |
# Option 1 — use a .env file (run from the bash-cli directory):
cp .env.example .env
# edit .env with your values
bash main.sh
# Option 2 — export variables directly:
export AUTHGATE_URL=https://auth.example.com
export CLIENT_ID=your-client-id
bash main.sh- The
.envfile is loaded from the current working directory, not the script's directory - Variables already set in the environment are not overridden — explicit
exportalways takes precedence - Malformed lines (not matching
KEY=VALUE) are skipped with a warning
- Discovers OIDC endpoints via
/.well-known/openid-configuration - Checks for a cached token in
~/.authgate-tokens.json- If valid and not expired, reuses it
- If expired, attempts a token refresh
- If no valid token, initiates the Device Code flow:
- Requests a device code from the authorization server
- Displays a verification URL and user code
- Polls the token endpoint until the user completes authorization
- Validates the token via
/oauth/userinfo— re-authenticates on 401/403 - Caches the token for future runs
- Prints user info and token metadata
To sign in, open the following URL in a browser:
https://auth.example.com/device
Then enter the code: ABCD-1234
Waiting for authorization....
User: Jane Doe (jane@example.com)
Subject: user-uuid-1234
Access Token: eyJhbGci...
Refresh Token: dGhpcyBp...
Token Type: Bearer
Expires In: 3600
Expires At: 2025-01-01T12:00:00Z
Scope: profile email
ID Token: eyJ0eXAi...
TokenInfo Active: true
TokenInfo UserID: user-uuid-1234
TokenInfo ClientID: your-client-id
TokenInfo Scope: profile email
TokenInfo SubjectType: user
TokenInfo Issuer: https://auth.example.com
TokenInfo Exp: 1735732800
- Symlink protection — refuses to read/write token cache files that are symlinks
- File ownership check — only operates on cache files owned by the current user
- stdin-based secret passing — POST data and headers are passed via stdin to avoid leaking tokens in the process list
- Curl config injection prevention — escapes special characters in HTTP headers
- Safe
.envparsing — only acceptsKEY=VALUEformat; skips malformed lines and never overrides existing environment variables - File permissions — cache files are set to
600(owner-only read/write)
Automatically detects GNU vs BSD date command at startup for portable date formatting (Linux, macOS, FreeBSD).
Tokens are cached to ~/.authgate-tokens.json. This file is shared with the Python CLI example, using the same format for compatibility.