Self-hosted auth backend for SaaS apps, internal tools, and multi-tenant APIs.
UniAuth is an API-first authentication and authorization service written in Go. It gives you organization-scoped JWT auth, RBAC, audit logs, webhook delivery, and API key lifecycle management in a single service backed by PostgreSQL and Redis.
Status: Active development. UniAuth is strongest today as a self-hosted auth backend for your own APIs. OAuth/OIDC provider flows, MFA, social login, SDKs, and an admin UI are roadmap work.
- Single Go service backed by PostgreSQL and Redis
- Multi-tenant by default with isolated organizations, users, roles, and API keys
- JWT sessions with rotation for access and refresh tokens
- Operational visibility with audit logs, health endpoints, and webhook events
- Deployable anywhere with Docker, Helm, Kubernetes manifests, and generated Swagger/OpenAPI docs
Use UniAuth when:
- you want a self-hosted auth service that runs next to your application stack
- your backend already speaks REST/JSON and you want auth to stay API-first
- you need organization isolation, RBAC, auditability, and webhook hooks without adopting a full IAM suite
Consider alternatives when:
- you need hosted auth with wide SDK coverage, social login, and enterprise SSO today
- you need a full identity provider with OIDC/SAML federation or a polished admin console out of the box
- you need protected HTTP routes to accept scoped API keys today; UniAuth currently supports API-key issuance, revocation, and validation primitives, while protected routes are JWT-first
git clone https://github.com/osama1998H/uniauth.git
cd uniauthcp .env.example .env
openssl rand -hex 32Set the generated value as JWT_SECRET in .env.
docker compose up --buildUniAuth will:
- start PostgreSQL, Redis, and MailHog
- connect to the database
- run pending migrations on startup
- serve the API on
http://localhost:8080
curl http://localhost:8080/healthSwagger UI is available at http://localhost:8080/swagger/index.html. MailHog is available at http://localhost:8025.
The example password below satisfies UniAuth's current password policy: minimum 8 characters, at least one uppercase letter, one digit, and one special character.
# Register a new organization + first admin user.
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"org_name":"Acme Corp","email":"admin@acme.com","password":"StrongPass1!"}'
# Login with the same credentials.
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"org_slug":"acme-corp","email":"admin@acme.com","password":"StrongPass1!"}'
# Copy the access_token from the login response, then call a protected route.
curl http://localhost:8080/api/v1/users/me \
-H "Authorization: Bearer <access-token>"- JWT authentication with short-lived access tokens and rotating refresh tokens
- Multi-tenancy with organization-scoped users, roles, sessions, audit logs, webhooks, and API keys
- Role-based access control (RBAC) with built-in permission names such as
users:readandroles:write - API key lifecycle management to issue, scope, revoke, and audit keys
- Audit logs for authentication and authorization events
- Webhooks for auth events with HMAC signatures
- Email verification with one-time token links sent via SMTP
- Password reset via SMTP
- Rate limiting backed by Redis
- Health and readiness endpoints for orchestration
- Scratch-based Docker image and a single Go server binary
- Swagger UI
- OpenAPI spec
- Horizontal scaling guide
- Security report
- Helm chart
- Kubernetes manifests
- Contributing guide
All configuration is via environment variables (see .env.example):
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP listen port |
ENVIRONMENT |
development |
development or production |
TRUSTED_PROXY_CIDRS |
— | Comma-separated proxy CIDRs or IPs allowed to supply forwarded client IP headers |
DATABASE_URL |
— | PostgreSQL connection string (required) |
REDIS_URL |
redis://localhost:6379/0 |
Redis connection string |
JWT_SECRET |
— | HMAC secret for JWTs (required, random 32+ chars) |
ACCESS_TOKEN_DURATION |
15m |
Access token lifetime |
REFRESH_TOKEN_DURATION |
168h |
Refresh token lifetime (7 days) |
RESET_TOKEN_DURATION |
1h |
Password reset token lifetime |
VERIFY_EMAIL_TOKEN_DURATION |
24h |
Email verification token lifetime |
RATE_LIMIT_PER_MINUTE |
60 |
Max requests per IP per minute |
CORS_ORIGINS |
* |
Comma-separated allowed origins |
SMTP_HOST |
— | SMTP server for password reset delivery |
SMTP_PORT |
587 |
SMTP port |
SMTP_FROM |
— | From address for emails |
APP_BASE_URL |
http://localhost:8080 |
Base URL used in password-reset emails; point it at your frontend or recovery UI when applicable |
If UniAuth is deployed behind a reverse proxy or load balancer, set TRUSTED_PROXY_CIDRS to that proxy's CIDR or IP. If it is left empty, UniAuth ignores forwarded IP headers and uses the direct peer IP instead.
Protected routes currently require Authorization: Bearer <access-token>. Refresh tokens are reserved for /api/v1/auth/refresh and the logout request body.
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
None | Liveness probe |
GET |
/ready |
None | Readiness probe |
POST |
/api/v1/auth/register |
None | Register org + admin user |
POST |
/api/v1/auth/login |
None | Login, returns token pair |
POST |
/api/v1/auth/refresh |
None | Exchange refresh token |
POST |
/api/v1/auth/logout |
JWT | Revoke session |
POST |
/api/v1/auth/logout-all |
JWT | Revoke all sessions |
POST |
/api/v1/auth/password/reset-request |
None | Request password reset email |
POST |
/api/v1/auth/password/reset-confirm |
None | Confirm reset with token |
PUT |
/api/v1/auth/password/change |
JWT | Change password |
POST |
/api/v1/auth/email/verify-request |
JWT | Resend verification email |
POST |
/api/v1/auth/email/verify-confirm |
None | Confirm email with token |
GET |
/api/v1/users/me |
JWT | Get own profile |
PUT |
/api/v1/users/me |
JWT | Update own profile |
GET |
/api/v1/users |
JWT | List org users |
GET |
/api/v1/users/{id} |
JWT | Get user |
DELETE |
/api/v1/users/{id} |
JWT | Deactivate user |
POST |
/api/v1/users/{id}/roles |
JWT | Assign role to user |
GET |
/api/v1/organizations/me |
JWT | Get own org |
PUT |
/api/v1/organizations/me |
JWT | Update own org |
GET |
/api/v1/roles |
JWT | List roles |
POST |
/api/v1/roles |
JWT | Create role |
PUT |
/api/v1/roles/{id} |
JWT | Update role |
DELETE |
/api/v1/roles/{id} |
JWT | Delete role |
GET |
/api/v1/roles/permissions |
JWT | List all built-in permissions |
POST |
/api/v1/roles/{id}/permissions |
JWT | Assign permissions to role |
GET |
/api/v1/api-keys |
JWT | List API keys |
POST |
/api/v1/api-keys |
JWT | Create API key |
DELETE |
/api/v1/api-keys/{id} |
JWT | Revoke API key |
GET |
/api/v1/audit |
JWT | List audit logs |
GET |
/api/v1/webhooks |
JWT | List webhooks |
POST |
/api/v1/webhooks |
JWT | Create webhook |
PUT |
/api/v1/webhooks/{id} |
JWT | Update webhook |
DELETE |
/api/v1/webhooks/{id} |
JWT | Delete webhook |
| Layer | Technology |
|---|---|
| Language | Go 1.24.7 |
| HTTP Router | chi |
| Database | PostgreSQL 16 (pgx/v5) |
| Cache | Redis 7 |
| Migrations | golang-migrate |
| JWT | golang-jwt/jwt v5 |
| Password hashing | bcrypt |
| Logging | log/slog |
| Config | Viper |
- Scoped API-key auth on protected routes
- Email verification flow
- Multi-Factor Authentication (TOTP)
- OAuth2 provider (use UniAuth as your OAuth2 server)
- Social login (Google, GitHub)
- Admin dashboard API
- TypeScript + Python SDKs
- Prometheus metrics
- Horizontal scaling guide (see docs/horizontal-scaling.md)
See CONTRIBUTING.md.
MIT