README
ΒΆ
πͺͺ IDPlease
A tiny OIDC Identity Provider for development and pilot deployments.
What is this?
IDPlease is a drop-in replacement for Microsoft Entra ID (formerly Azure AD) designed for local development, testing, and small pilot deployments. Instead of configuring a real Entra tenant with app registrations, redirect URIs, and user assignments, just run IDPlease and get a fully functional OIDC provider in seconds.
Why use it?
- ποΈ Local development β No internet, no Azure subscription, no app registration dance
- π§ͺ Integration testing β Spin up a predictable auth server in CI
- π Pilot deployments β Lightweight auth for internal tools before committing to Entra
- π Entra-compatible claims β
oid,upn,roles,groups,tidβ your app won't know the difference - π¦ Single binary β No external dependencies, just one executable with embedded SQLite
Features:
- OpenID Connect Authorization Code flow with PKCE (S256)
- Client Credentials flow (machine-to-machine)
- Refresh token support with rotation
- Client registry with public and confidential clients
- UserInfo endpoint
- Token revocation (RFC 7009)
- End session / logout endpoint
- Standard discovery (
/.well-known/openid-configuration) and JWKS endpoints - RS256-signed JWTs with auto-generated keys
- User and role management via CLI and web-based Admin UI
- Client management via CLI and Admin UI
- Rate limiting on login attempts (per-user and per-IP)
- CORS support with configurable origins
- Health check endpoint
- First-run bootstrap user
- Structured logging (slog)
- SQLite-backed storage (no external database needed)
- Works behind reverse proxies with configurable base path
Quick Start
1. Get the binary
Download from Releases, or build from source:
go install github.com/jclement/idplease@latest
Or use Docker:
docker run -p 8080:8080 -v $(pwd)/data:/data ghcr.io/jclement/idplease:latest
2. Start the server
./idplease server
On first start, IDPlease will:
- Create an
idplease.dbSQLite database - Generate an RSA signing key (
idplease-key.json) - Generate a one-time admin key and print it to the console
- Create a bootstrap
adminuser with a random password (printed to stdout)
Override bootstrap credentials with environment variables:
IDPLEASE_ADMIN_USER=myadmin IDPLEASE_ADMIN_PASSWORD=mypassword ./idplease server
3. Open the Admin UI
Navigate to http://localhost:8080/admin and enter the admin key shown in the console output.
From the admin UI you can:
- Manage users β add, edit, delete users; reset passwords
- Manage roles for each user
- Manage OAuth clients β add, edit, delete clients (public or confidential)
- Configure settings β issuer, token lifetimes, redirect URIs, CORS origins, group mappings, etc.
4. Or use the CLI
# Add a user
./idplease user add bob
# Add some roles
./idplease role add bob Admin
./idplease role add bob Reader
# Add an OAuth client
./idplease client add my-spa
# List clients
./idplease client list
5. Point your app at it
Discovery URL: http://localhost:8080/.well-known/openid-configuration
That's it. Your app can now authenticate users against IDPlease.
OIDC Endpoints
All endpoints are relative to the configured base path (default /).
| Endpoint | Method | Description |
|---|---|---|
/.well-known/openid-configuration |
GET | OpenID Connect Discovery document |
/.well-known/openid-configuration/keys |
GET | JWKS with the RSA public key |
/authorize |
GET/POST | Authorization endpoint (shows login form, processes login) |
/token |
POST | Token endpoint (authorization_code, refresh_token, client_credentials) |
/userinfo |
GET/POST | UserInfo endpoint (returns claims for Bearer token) |
/revoke |
POST | Token revocation (RFC 7009) |
/end-session |
GET | End session / logout |
/health |
GET | Health check ({"status":"ok","version":"..."}) |
/admin |
GET | Admin UI (requires admin key) |
Token Endpoint Grant Types
authorization_codeβ Standard OIDC auth code exchange, optionally with PKCErefresh_tokenβ Refresh token rotation (issues new access + refresh token, revokes old)client_credentialsβ Machine-to-machine (confidential clients only, no id_token/refresh_token)
CORS
CORS headers are applied to /token, /userinfo, /revoke, and JWKS endpoints. Configure allowed origins via Admin UI or config (default: ["*"]).
Rate Limiting
Login attempts are rate-limited:
- Per username: 5 attempts per minute
- Per IP: 20 attempts per minute
Exceeding the limit shows a "too many attempts" error on the login form.
Admin UI
IDPlease includes a built-in web admin interface at {basePath}/admin.
Admin Key
The admin UI is protected by an admin key. You can set it in several ways (in order of priority):
- CLI flag:
./idplease server --admin-key=mysecret - Environment variable:
IDPLEASE_ADMIN_KEY=mysecret - Config file:
"adminKey": "mysecret"inidplease.json - Auto-generated: If none of the above are set, a random key is generated and printed to stdout on startup
Admin Pages
- Dashboard β Overview: user count, client count, configured issuer
- Settings β Edit: display name, issuer URL, client IDs, tenant ID, access/refresh token lifetimes, redirect URIs, CORS origins, group mappings, session secret
- Users β List, add, edit, delete users; reset passwords
- Roles β Per-user role management: add/remove roles
- Clients β List, add, delete OAuth clients (public or confidential)
CLI Reference
All commands support --config <path> to specify an alternate config file (default: idplease.json).
Server
./idplease server
./idplease server --admin-key=mysecretkey
./idplease server --config /etc/idplease/config.json
User Management
./idplease user add alice # Interactive: prompts for email, display name, password
./idplease user list
./idplease user delete alice
./idplease user reset bob # Prompts for new password
Role Management
./idplease role add bob Admin
./idplease role list bob
./idplease role remove bob Admin
Client Management
./idplease client add my-app # Interactive: prompts for name, type, redirect URIs
./idplease client list
./idplease client delete my-app
Configuration
./idplease config set issuer https://idp.example.com
./idplease config get issuer
./idplease config list
Configuration
IDPlease uses a JSON config file (idplease.json) for server-level settings and SQLite for OIDC/user configuration.
Config File (idplease.json)
| Field | Type | Default | Description |
|---|---|---|---|
port |
int |
8080 |
HTTP listen port |
keyFile |
string |
idplease-key.json |
Path to the RSA signing key file |
dbFile |
string |
idplease.db |
Path to the SQLite database |
adminKey |
string |
(auto-generated) | Admin key for the admin UI |
OIDC Settings (in SQLite, managed via Admin UI or CLI)
| Key | Description |
|---|---|
issuer |
The OIDC issuer URL |
display_name |
Display name for the IDP |
base_path |
Base path for all routes |
client_ids |
Allowed OIDC client IDs (JSON array) |
tenant_id |
Tenant ID for the tid claim |
access_token_lifetime |
Access token lifetime in seconds (default: 300) |
refresh_token_lifetime |
Refresh token lifetime in seconds (default: 86400) |
redirect_uris |
Allowed redirect URIs (JSON array) |
cors_origins |
Allowed CORS origins (JSON array, default: ["*"]) |
group_mappings |
Maps group GUIDs to role names (JSON object) |
session_secret |
Secret for session signing |
Example: idplease.json
{
"port": 8080,
"adminKey": "my-secret-admin-key",
"dbFile": "/data/idplease.db",
"keyFile": "/data/idplease-key.json"
}
Environment Variables
| Variable | Description |
|---|---|
IDPLEASE_ADMIN_KEY |
Admin key for the admin UI |
IDPLEASE_ADMIN_USER |
Bootstrap admin username (default: admin) |
IDPLEASE_ADMIN_PASSWORD |
Bootstrap admin password (default: random) |
Token Claims
IDPlease tokens include the following claims, designed for compatibility with Microsoft Entra ID:
| Claim | Type | Description |
|---|---|---|
iss |
string |
Issuer URL |
sub |
string |
User ID (UUID) or Client ID (for client_credentials) |
aud |
string |
Client ID |
exp |
number |
Expiration time |
iat |
number |
Issued at |
oid |
string |
Object ID (same as sub) |
preferred_username |
string |
Username |
upn |
string |
User Principal Name |
name |
string |
Display name |
email |
string |
Email address |
roles |
string[] |
Application roles |
groups |
string[] |
Group GUIDs (via group mappings) |
tid |
string |
Tenant ID (if configured) |
nonce |
string |
Nonce (if provided in auth request) |
First-Run Bootstrap
On first startup with an empty database, IDPlease automatically creates an admin user with:
- A randomly generated 16-character password
- The
IDPlease.Adminrole
Credentials are printed prominently to stdout. Override with IDPLEASE_ADMIN_USER and IDPLEASE_ADMIN_PASSWORD environment variables.
Docker
docker run -p 8080:8080 -v $(pwd)/data:/data ghcr.io/jclement/idplease:latest
The SQLite database, key file, and config are all stored in /data.
Docker Compose with Cloudflare Tunnel
See docker-compose.yml for a complete example pairing IDPlease with a Cloudflare Tunnel.
docker compose up -d
docker compose exec idplease idplease user add bob
docker compose exec idplease idplease role add bob Admin
Data Files
| File | Description |
|---|---|
idplease.json |
Server config (port, key file path, db path, admin key) |
idplease.db |
SQLite database (users, roles, clients, tokens, OIDC config) |
idplease-key.json |
RSA signing key (auto-generated) |
β οΈ Backup
idplease-key.jsonif token continuity matters. Regenerating the key invalidates all previously issued tokens.
Building from Source
git clone https://github.com/jclement/idplease.git
cd idplease
go build -o idplease .
# With version info
go build -ldflags "-X github.com/jclement/idplease/internal/config.Version=1.0.0" -o idplease .
Running Tests
go test ./...
License
MIT β Copyright (c) 2026 Jeff Clement
Documentation
ΒΆ
There is no documentation for this package.