BlackBird is a lightweight authentication service written in Go. It provides JWT-based access tokens, refresh token rotation, and role-based authorization for your applications. It uses Postgres for persistence, Redis for token blocklisting, and exposes a JWKS endpoint so your apps can verify tokens locally.
- Email/password registration and login
- RS256 JWT access tokens with roles embedded in claims
- Refresh token rotation with hashed storage
- JWKS endpoint for public key distribution
- Admin role management and user banning
- Basic rate limiting and request logging
- Users register and login to receive a short-lived access token and a refresh token (stored as an HttpOnly cookie).
- Your app includes the access token in the
Authorization: Bearer <token>header for API requests. - When the access token expires, call
/auth/refreshto rotate the refresh token and get a new access token. - Your app verifies access tokens using the JWKS endpoint.
- Go 1.25+
- Postgres
- Redis
Update the connection string in bin/db-create.sh if needed, then run:
./bin/db-create.shInstall migrate and apply migrations:
go install github.com/golang-migrate/migrate/v4/cmd/migrate@latest
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
./bin/db-migrate.shCreate a private key for signing tokens:
mkdir -p keys
openssl genrsa -out keys/private.pem 2048Minimal example:
export DATABASE_URL="postgresql://user:pass@localhost:5432/blackbird"Recommended full set:
export ADDR=":8080"
export DATABASE_URL="postgresql://user:pass@localhost:5432/blackbird"
export REDIS_URL="redis://localhost:6379"
export RSA_PRIVATE_KEY_PATH="./keys/private.pem"
export RATE_LIMIT_REQUESTS="100"
export RATE_LIMIT_WINDOW="60s"
export ENV="development"
export JWT_ISSUER="auth.example.com"
export ACCESS_TOKEN_TTL="15m"
export REFRESH_TOKEN_TTL="720h"go run ./cmd/serverPublic endpoints:
POST /auth/register- create a new userPOST /auth/login- login and get access tokenPOST /auth/refresh- rotate refresh token and get new access tokenGET /auth/jwks- public keys for token verificationGET /healthz- health check
Authenticated endpoints:
GET /users/me- current user profilePOST /users/me/password- change password
Admin-only endpoints (requires global role admin):
GET /users/{id}POST /users/{id}/banPOST /users/{id}/unbanGET /users/{id}/rolesPOST /users/{id}/roles/globalDELETE /users/{id}/roles/global/{role}POST /users/{id}/roles/appDELETE /users/{id}/roles/app/{appID}/{role}GET /roles/global
- Call
POST /auth/loginwith email and password to get an access token. - Store the refresh token cookie (HttpOnly) returned by the server.
- Attach the access token to your app requests:
Authorization: Bearer <access_token>- Verify tokens in your app using the JWKS endpoint:
- Fetch keys from
GET /auth/jwks - Verify
RS256signatures - Validate
iss(issuer),exp(expiration), andsub(user ID) - Use
global_rolesandappsclaims for authorization
- Refresh cookies are marked
Secure. For local HTTP testing, use HTTPS or adjust cookie settings. - Rate limiting is in-memory per instance; for multi-instance deployments, use a shared limiter (e.g. Redis).
- Seed
global_roleswith roles likeadminanduserbefore assigning them.
MIT