Your data, your control. Fully open source, authentication and authorization. No lock-ins. Deployment in Railway in 120 seconds || Spin a docker image as a mic
Your data, your control. Fully open source, authentication and authorization. No lock-ins. Deployment in Railway in 120 seconds || Spin a docker image as a mic
Open-source authentication and authorization for your applications.
Bring your own database and stay in control of user data.
Documentation · OAuth 2.0 / OIDC · v1 → v2 migration · Contributing · Discord
Authorizer is an open-source authentication and authorization server you can self-host. Connect any supported database (13+ backends including Postgres, MySQL, SQLite, SQL Server, YugaByte, MariaDB, Cassandra, ScyllaDB, MongoDB, ArangoDB, DynamoDB, and Couchbase) and run OAuth2/OIDC, social login, MFA, magic links, RBAC, webhooks, and email templates from one place.
v2 note: Authorizer v2 uses CLI arguments for all configuration. The server does not read from
.envor OS environment variables. Pass config when starting the binary (e.g../authorizer --client-id=... --client-secret=...). See MIGRATION.md.
Prerequisites: Go ≥ 1.24 (see go.mod).
git clone https://github.com/authorizerdev/authorizer.git
cd authorizer
make devmake dev runs the server with SQLite and development defaults (RS256 keys, sample client credentials). Open the URL printed in the logs (default port 8080) and sign in with --admin-secret (admin in dev).
For production builds, tests, and Docker, see Getting Started below.
client_credentials grant and secretless workload identity (RFC 7523 client_assertion, Kubernetes projected ServiceAccount tokens, TokenReview). Works out of the box on EKS, GKE and AKS, which publish a public OIDC issuer and JWKS by default. Clusters left on the default issuer (kubernetes.default.svc, e.g. kubeadm/kind) publish private addresses that Authorizer's SSRF guard refuses — point jwks_url at a reachable mirror of /openid/v1/jwks (key_source_type: static_jwks_url); issuer_url only has to match the token's iss and is never fetched. Verified by make test-k8s. SPIFFE JWT-SVID is preview: its draft (draft-schwenkschuster-oauth-spiffe-client-auth-00) expired 2026-01-02, is not WG-adopted, and its assertion-type URN is not IANA-registered, so the value may changeact chains and scope attenuationShipped
authorizer-py v0.2.0 (v0.3.0 in pre-release: pip install --pre authorizer-py); sync + async clients, admin APIrc tag); protocol prop, pre-built login/signup componentsPlanned
Deploy production ready Authorizer instance using one click deployment options available below
| Infra provider | One-click link | Additional information |
|---|---|---|
| Railway.app | docs | |
| Heroku | docs | |
| Render | docs | |
| Koyeb | docs | |
| RepoCloud | docs | |
| Alibaba Cloud | docs |
This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in local or standalone mode.
go.mod)git clone https://github.com/authorizerdev/authorizer.git (or your fork URL)cd authorizermake dev — SQLite, RS256 dev keys, sample OAuth client (see Quick start)make build (or go build -o build/authorizer .); optionally make build-app and make build-dashboardmake dev:./build/authorizer \
--database-type=sqlite \
--database-url=test.db \
--url=http://localhost:8080 \
--jwt-type=HS256 \
--jwt-secret=test \
--encryption-key=test-encryption-key \
--admin-secret=admin \
--client-id=123456 \
--client-secret=secretv2: The server does not read from
.env. All configuration must be passed as CLI arguments. See MIGRATION.md for the full mapping of env vars to flags.
The default image runs as non-root (UID 65532). Writable mounts (SQLite under /authorizer/data, etc.) are usually root-owned, so pick one of:
Run as root for that container (simplest for local SQLite + volumes):
docker run -p 8080:8080 -u root \
-v authorizer_data:/authorizer/data \
quay.io/authorizer/authorizer \
--database-type=sqlite \
--database-url=/authorizer/data/data.db \
--url=http://localhost:8080 \
--client-id=123456 \
--client-secret=secret \
--admin-secret=admin \
--jwt-type=HS256 \
--jwt-secret=test \
--encryption-key=test-encryption-keyKeep non-root and make the mount writable by 65532 (good for production-style bind mounts):
mkdir -p ./data && sudo chown -R 65532:65532 ./data
docker run -p 8080:8080 \
-v "$(pwd)/data:/authorizer/data" \
quay.io/authorizer/authorizer \
--database-type=sqlite \
--database-url=/authorizer/data/data.db \
--url=http://localhost:8080 \
...Build from source with the root target (no -u at run time):
docker build --target final-root -t authorizer:root .
docker run -p 8080:8080 -v authorizer_data:/authorizer/data authorizer:root \
--database-type=sqlite --database-url=/authorizer/data/data.db ...-p 8080:8080 to expose it.authorizer_data persists the SQLite DB; use a named volume or a host path (e.g. -v $(pwd)/data:/authorizer/data).ENTRYPOINT ["./authorizer"] so args after the image name go to the binary). See MIGRATION.md for the full list of flags.Inside a container, localhost / 127.0.0.1 is the container itself, not your machine. Use a host alias instead:
No open issues yet, or sync has not completed.