Authflow
Authflow is an API-first identity and token service written in Go. It issues application-level JWTs, supports OAuth-based identity verification, and enforces secure logout using Redis-backed token revocation.
Designed for platforms that want a single JWT issuer for all downstream services, while keeping authentication methods flexible. Applications can integrate with Authflow using standard HTTP redirects and API calls — no client SDK is required.
What Authflow Is
Authflow acts as a centralized trust layer for modern applications and microservices.
AUTHORIZATION SERVICE — PRIMARY ROLE
- Issues signed JWT access tokens (RS256)
- Enforces token revocation using Redis
- Acts as the single JWT authority for the system
- Exposes a JWKS endpoint for downstream verification
AUTHENTICATION INTEGRATOR — SECONDARY ROLE
- Supports OAuth 2.0 login for identity verification
- Accepts upstream-authenticated identity via
/mint - Keeps authentication method decoupled from token authority
DESIGN GOALS
- Single JWT authority across the system
- Provider-agnostic authentication inputs — OAuth, local login, or SSO
- Stateless downstream verification using JWKS
- Immediate logout semantics using Redis-backed revocation
- SDK-less integration through standard HTTP endpoints
Why Authflow
Most authentication products focus primarily on login UI, sessions, and frontend SDKs. Authflow is centered on a different responsibility:
No matter how a user authenticates, Authflow becomes the single service responsible for issuing, validating, and revoking application JWTs.
This gives downstream services a stable and simple trust model:
- Trust one issuer
- Verify signatures locally
- Check revocation centrally
- Stay independent of the original login method
Key Architecture Principle
Authentication and token authority are intentionally separated.
- Platforms decide how users authenticate — OAuth, username/password, SSO, or another trusted method
- Authflow decides how access is granted and revoked
This lets applications evolve their login methods without changing downstream authorization logic.
Centralized JWT Issuance
Single /mint endpoint issues signed JWT access tokens. One authority for your entire system.
RS256 Signing
Asymmetric signing with public key distribution via JWKS (/.well-known/jwks.json).
OAuth 2.0 Login Support
Use Authflow as the OAuth entry point for browser-based login.
Redis-backed Revocation
Immediate logout via Redis. Stores revoked:<jti> with TTL matching token expiry.
Stateless Verification
Downstream services verify JWT signatures locally. No auth server call per request.
Multi-client Registration
Register multiple clients with strict redirect URI validation.
SDK-less Integration
API-first model. Integrates directly using standard HTTP endpoints (no heavy client library).
Microservice-friendly
Clean provider-agnostic design. Keeps your application roles independent from the identity provider.
SDK-less Integration Model
Authflow does not require platform-specific SDKs. Applications integrate directly with Authflow using:
- Browser redirects for OAuth flows
- HTTP API calls for token issuance and logout
- Bearer tokens for authenticated requests
- JWKS for downstream JWT verification
This makes Authflow usable from React, MERN, Django, Spring Boot, FastAPI, mobile apps, and other HTTP-capable clients without framework lock-in.
Supported Flows
Authflow supports two broad integration patterns.
1. OAUTH LOGIN FLOW
Use Authflow as the OAuth entry point for browser-based login. Useful for browser apps that want Authflow to handle OAuth and return an application token.
- Client redirects to
GET /auth/{provider} - OAuth provider authenticates the user
- Authflow validates the callback
- Authflow issues an application JWT
- Authflow redirects user to registered redirect_uri
2. TOKEN ISSUANCE (AFTER AUTH)
Mint an application JWT after identity has already been verified by the calling platform. Delegates token authority to Authflow without yielding the login experience.
- Client platform authenticates user using its own method
- Platform calls
POST /mint - Authflow issues an application JWT
- Downstream services trust only the Authflow-issued token
Issuance & Revocation
Token Issuance (/mint)
POST /mint issues an Authflow JWT for an already-authenticated user. Important: This is a token issuance endpoint, not an authentication endpoint. It assumes the caller has already verified the user's identity using a trusted flow.
JWKS Endpoint
Downstream services verify Authflow-issued JWTs using the public key served atGET /.well-known/jwks.json. No shared secret is required. Each downstream service fetches the public key from Authflow and verifies JWT signatures locally.
Logout and Token Revocation
Authflow implements logout as JWT revocation, not just browser session destruction.
- Client calls
POST /logoutwith a Bearer token - Authflow validates the JWT and extracts the
jticlaim - Authflow stores
revoked:{jti}in Redis - The Redis entry expires automatically when the token would normally expire
JWT Claims
| CLAIM | DESCRIPTION |
|---|---|
| sub | Subject (user identifier) |
| iss | Issuer (authflow-go) |
| jti | Unique token ID |
| iat | Issued at |
| exp | Expiration time |
| User email | |
| provider | Authentication source (google, local, sso) |
Authentication & Authorization Model
Authflow focuses on token trust, not application-specific permission logic. Authflow answers:
- Who issued this token?
- Is the token valid?
- Has the token been revoked?
The client platform still owns business authorization decisions (e.g., Is this user an admin? Can this user access tenant A?). This separation keeps application roles independent from the identity provider.
Two Integration Roles
A single service can be both a client and a downstream service.
| ROLE | WHAT YOU DO |
|---|---|
| Client | Authenticate your user, then call /mint to get an Authflow JWT |
| Downstream service | Accept Authflow JWTs on protected routes and verify them via JWKS |
Step 0 — Register Your Application
Before calling /mint, register your app with Authflow to get a client_id and client_secret.
curl -X POST https://authflow-go.onrender.com/clients \
-H "Content-Type: application/json" \
-d '{
"name": "my-app",
"redirect_uri": "https://my-app.com/callback"
}'Response — save these, the secret is shown only once:
{
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"client_secret": "7f3d9a1b-...",
"redirect_uri": "https://my-app.com/callback"
}Store AUTHFLOW_CLIENT_ID and AUTHFLOW_CLIENT_SECRET as environment variables. Never hardcode them.
How /mint Works
/mint is a token issuance endpoint, not an authentication endpoint. It assumes your service has already verified the user's identity. You are responsible for authentication — Authflow is responsible for the token.
Your service Authflow
| |
|-- verify user ---→ | (your logic: check password, OAuth, SSO)
| |
|-- POST /mint ----→ | (with client_id + client_secret + sub)
| |
|←-- JWT ----------- |
| |
|-- return JWT --→ clientHow JWT Verification Works
Downstream services verify Authflow JWTs using the public key served at:
GET https://authflow-go.onrender.com/.well-known/jwks.json
No shared secret is needed. Each service fetches the public key and verifies signatures locally — no call to Authflow per request.
Important: Cache the public key with a TTL (e.g. 1 hour) and retry the JWKS fetch on verification failure. This handles key rotation without breaking your service.
API Reference
| METHOD | ENDPOINT | DESCRIPTION |
|---|---|---|
| GET | /auth/{provider} | Initiate OAuth flow. Redirects to provider authorization endpoint (e.g., Google). |
| GET | /auth/{provider}/callback | OAuth callback. Validates authorization code & state, exchanges for credentials, issues JWT. |
| POST | /mint | Issue a JWT. Accepts sub, provider, and optional email. Does not authenticate — assumes identity is already verified. |
| POST | /logout | Revoke a JWT. Validates token, extracts jti, writes revoked:<jti> to Redis with TTL. |
| GET | /.well-known/jwks.json | Public key distribution for downstream JWT verification. No secret is shared. |
Core JWT Flows
# Issue a JWT after authentication
POST /mint
Content-Type: application/json
{
"sub": "username",
"provider": "google | local | sso",
"email": "user@example.com"
}
# Response
{
"access_token": "eyJhbGciOiJIUzI1NiIs..."
}
# ⚠️ /mint does not authenticate users
# It assumes the caller has already verified identityPlugging Into Your Service
Downstream services only need two things: The Authflow issuer (`authflow-go`) and the Authflow JWKS URL. No shared secret is required.
import os
import time
import requests
from flask import Flask, request, jsonify
from flask_jwt_extended import JWTManager, jwt_required, get_jwt_identity
from jwt.algorithms import RSAAlgorithm
app = Flask(__name__)
# Flask session secret — not the JWT key
app.config["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]
# Tell flask-jwt-extended to use RS256 and Authflow's public key
app.config["JWT_ALGORITHM"] = "RS256"
app.config["JWT_PUBLIC_KEY"] = _get_public_key()
jwt = JWTManager(app)
# --- JWKS with TTL cache ---
_jwks_cache = {"key": None, "fetched_at": 0}
JWKS_URL = "https://authflow-go.onrender.com/.well-known/jwks.json"
JWKS_TTL = 3600 # 1 hour
def _get_public_key():
now = time.time()
if _jwks_cache["key"] and (now - _jwks_cache["fetched_at"]) < JWKS_TTL:
return _jwks_cache["key"]
jwks = requests.get(JWKS_URL).json()
key = RSAAlgorithm.from_jwk(jwks["keys"][0])
_jwks_cache.update({"key": key, "fetched_at": now})
return key
# --- Login: authenticate, then mint ---
@app.route("/login", methods=["POST"])
def login():
data = request.json
username = data.get("username")
password = data.get("password")
if not username or not password:
return jsonify({"error": "Missing credentials"}), 400
user = User.query.filter_by(username=username).first()
if not user or not user.check_password(password):
return jsonify({"error": "Invalid credentials"}), 401
resp = requests.post(
"https://authflow-go.onrender.com/mint",
json={
"sub": username,
"provider": "local",
"client_id": os.environ["AUTHFLOW_CLIENT_ID"],
"client_secret": os.environ["AUTHFLOW_CLIENT_SECRET"],
}
)
if resp.status_code != 200:
return jsonify({"error": "Auth service unavailable"}), 500
return resp.json(), 200
# --- Protected route ---
@app.route("/upload", methods=["POST"])
@jwt_required()
def upload():
current_user = get_jwt_identity() # returns sub claim
# your logic here
return jsonify({"user": current_user})Environment Variables
| VARIABLE | USED BY | DESCRIPTION |
|---|---|---|
| AUTHFLOW_CLIENT_ID | Client services | Issued by POST /clients |
| AUTHFLOW_CLIENT_SECRET | Client services | Issued by POST /clients, shown once |
| AUTHFLOW_ISSUER | Downstream services | Always authflow-go |
| AUTHFLOW_JWKS_URL | Downstream services | https://authflow-go.onrender.com/.well-known/jwks.json |
Security Model
| PROPERTY | STATUS |
|---|---|
| Single JWT issuer | Authflow only |
| Asymmetric signing (RS256) | Private key never leaves Authflow |
| Public key distribution | Via JWKS |
| Immediate logout | Redis-backed revocation |
| Stateless downstream validation | No auth server call per request |
| Downstream services mint tokens | Never |
| Shared secret across services | Not required |
Good Fit For
- Microservice architectures
- MERN and SPA backends that need one JWT authority
- Polyglot systems using Node, Python, Java, or Go
- Platforms that want provider-agnostic authentication inputs
- Teams that want local JWT verification with central revocation
- Products that prefer API-first auth over SDK-heavy integration
Live Integration
Authflow is currently used in production as the authentication and token layer for:
rag-works.vercel.app →You can test the full flow by creating an account with Google OAuth or username/password, logging in, accessing protected routes, and logging out.