Skip to content

Repository files navigation

minioidc

Go License: MIT

minioidc is a lightweight OpenID Connect (OIDC) / OAuth 2.0 authorization server designed to provide Single Sign-On (SSO) functionality for small or home networks. It is not a complete OIDC implementation, but covers the most common grant types and endpoints.

Supported grant types:

Grant type RFC
Authorization Code + PKCE RFC 7636
Implicit OpenID Connect Core §3.2
Hybrid OpenID Connect Core §3.3
Refresh Token RFC 6749 §6
Client Credentials RFC 6749 §4.4
Resource Owner Password RFC 6749 §4.3
Device Code RFC 8628
JWT Bearer RFC 7523

Features

  • Simple and Lightweight — designed to be a straightforward SSO solution for small or home networks.
  • All major OAuth 2.0 grant types — including Device Code (RFC 8628) and JWT Bearer (RFC 7523).
  • OIDC Discovery + JWKS — fully compliant /.well-known/openid-configuration and JWKS endpoint.
  • Token Revocation — RFC 7009 revocation endpoint for access and refresh tokens.
  • RP-Initiated Logout — OIDC end session endpoint with post_logout_redirect_uri support.
  • Introspection — RFC 7662 token introspection endpoint.
  • PKCE enforcement — required for public clients (no secret).
  • at_hash / c_hash — correct computation in ID tokens for implicit and hybrid flows.
  • Client and User Configuration — configure clients and users directly in the YAML file.
  • Flexible Data Storage — in-memory (default) or SQLite v3 for grants, sessions and MFA.
  • LDAP Users — plug in an LDAP directory as the user store.
  • MFA — TOTP-based MFA (e.g. Google Authenticator / Authy) per user.
  • Customisable Templates — override any HTML page with your own html/template files.
  • Secure by Default — HSTS, CSP, secure cookies, forwarded-header support, all opt-in via config.

Requirements

  • Go 1.22 or later (for running from source or using as a library)
  • Docker (optional, for the containerised setup)

Installation

From source

git clone https://github.com/fernandoescolar/minioidc.git
cd minioidc
make build          # produces ./minioidc binary

Docker

make build-docker   # builds the image
make run-docker     # starts the container on :8000

As a Go library

go get github.com/fernandoescolar/minioidc

Getting Started

  1. Copy the example configuration:
cp example.env .env
cp example1_config.yml config.yml   # simple in-memory setup
# or
cp example2_config.yml config.yml   # SQLite + LDAP setup
  1. Edit config.yml to set your issuer, clients, and users (see Configuration).

  2. Run the server:

MINIOIDC_ADDR=:8000 MINIOIDC_CONFIG=config.yml ./minioidc
# or
make run
  1. Visit http://localhost:8000 — the welcome page confirms the server is running.

  2. The OIDC discovery document is available at:

http://localhost:8000/.well-known/openid-configuration

Endpoints

Endpoint Method Description
/.well-known/openid-configuration GET OIDC discovery document
/.well-known/jwks.json GET JSON Web Key Set
/connect/authorize GET Authorization endpoint
/connect/token POST Token endpoint
/connect/userinfo GET / POST Userinfo endpoint
/connect/introspect POST Token introspection (RFC 7662)
/connect/revoke POST Token revocation (RFC 7009)
/connect/endsession GET / POST RP-Initiated Logout
/connect/deviceauthorization POST Device authorization (RFC 8628)
/connect/device GET / POST Device activation page (user-facing)
/login GET / POST Login page
/mfa/create GET / POST MFA enrolment page
/mfa/verify GET / POST MFA verification page

Makefile Commands

Command Description
make build Build the binary
make run Run the binary
make build-docker Build the Docker image
make run-docker Run the Docker image
make test Run all tests
make clean Remove build artefacts
make lint Run the linter
make hash text=<plaintext> Generate a bcrypt hash for a password or client secret

Configuration

Set the following environment variables before starting the server:

Variable Default Description
MINIOIDC_ADDR :8000 Address and port to listen on
MINIOIDC_CONFIG Path to the YAML configuration file (required)

Full configuration reference

name: My MiniOIDC
masterkey: 12345678901234567890123456789012   # AES master key for internal encryption
issuer: http://example.com
audience: http://example.com
require_mfa: false
reuse_refresh_tokens: false
private_rsa_key_path: private_key.pem         # omit to generate a random key on startup

middlewares:
  hsts: true            # HTTP Strict Transport Security
  csp: true             # Content Security Policy
  secure_cookies: true  # Secure flag on session cookies
  forward_headers: true # Trust X-Forwarded-* headers (use behind a reverse proxy)
  log_requests: true    # Log every request with duration

ttl:
  access: 20        # Access token TTL in minutes (default 20)
  refresh: 129600   # Refresh token TTL in minutes (default 90 days)
  session: 129600   # Session TTL in minutes (default 90 days)
  code: 5           # Authorization / device code TTL in minutes (default 5)
  csrf: 5           # CSRF token TTL in minutes (default 5)

sqlite:
  filepath: db.sqlite3
  use_in_grants: true
  use_in_sessions: true
  use_in_mfa: true

ldap:
  server: localhost:389
  bind: uid=admin,cn=users,dc=example,dc=com
  password: password
  base_dn: dc=example,dc=com
  filter_dn: (&(uid={username})(objectClass=person))
  attributes:
    subject: uidNumber
    name: uid
    email: mail
    phone: phone
    address: address

templates:
  base: templates/base.html
  login: templates/login.html
  mfa_create: templates/mfa_create.html
  mfa_verify: templates/mfa_verify.html
  device: templates/device.html   # Device Code activation page

clients:
  - id: myclient
    secret_hash: $2a$06$L6/zALdtbkYajjHTZUW29ePBEb/hwhgjhXC4YpHANavvKDJl69ctK  # "secret"
    redirect_uris:
      - http://myapi.com/callback
  - id: publicclient
    secret_hash: ""   # public client — no secret, PKCE required
    redirect_uris:
      - http://myapp.com/callback

users:
  - subject: "1"
    email: user@mail.com
    email_verified: true
    preferred_username: user
    password_hash: $2a$06$03dduqc0lMbsb5go/l6RI.cRb03Hos9CMpgm5/yYuRsSQPHtrFwSq  # "password"
    phone: +1234567890
    address: 1 Main St. City, State 12345
    groups:
      - admin

Root settings

Key Default Description
issuer OIDC issuer URL (required)
audience JWT audience (required)
require_mfa false Enforce TOTP MFA for all users
reuse_refresh_tokens false Allow refresh tokens to be used more than once
private_rsa_key_path Path to a PEM-encoded RSA private key; a 2048-bit key is generated if omitted

middlewares

Key Default Description
hsts false Add Strict-Transport-Security header
csp false Add Content-Security-Policy header
secure_cookies false Mark session cookie as Secure
forward_headers false Trust X-Forwarded-Proto / X-Forwarded-Host
log_requests false Log each request with method, path, status and duration

ttl — all values are in minutes

sqlite — omit the section entirely to use in-memory stores

ldap — if set, the users section is ignored

Static assets (CSS, JS, images) go in the static/ directory and are served at /static/. Reference them from templates as <link rel="stylesheet" href="/static/styles.css">.

Environment variable substitution is supported anywhere in the YAML:

masterkey: ${MINIOIDC_MASTER_KEY}
ldap:
  password: $LDAP_PASSWORD

Use in your projects

package main

import (
	"log"
	"net/http"

	"github.com/fernandoescolar/minioidc/pkg/api"
)

func main() {
	builder := &api.Builder{
		Issuer:   "https://minioidc.example.com",
		Audience: "https://api.example.com",
		Clients: []api.Client{
			{
				ID:           "myclient",
				SecretHash:   "$2a$06$L6/zALdtbkYajjHTZUW29ePBEb/hwhgjhXC4YpHANavvKDJl69ctK", // "secret"
				RedirectURIs: []string{"https://api.example.com/callback"},
			},
		},
		Users: []api.User{
			{
				Subject:           "0000001",
				PreferredUsername: "user",
				PasswordHash:      "$2a$06$03dduqc0lMbsb5go/l6RI.cRb03Hos9CMpgm5/yYuRsSQPHtrFwSq", // "password"
			},
		},
	}

	minioidc, err := builder.Build()
	if err != nil {
		log.Fatal(err)
	}

	mux := http.NewServeMux()
	handler := minioidc.Wrap(mux)

	log.Println("Listening on http://localhost:8000")
	if err := http.ListenAndServe(":8000", handler); err != nil && err != http.ErrServerClosed {
		log.Fatal(err)
	}
}

Builder fields

Field Type Default Description
Name string "minioidc" Display name shown on login/device pages
MasterKey string random AES key for internal encryption (auto-generated if empty)
Issuer string OIDC issuer URL (required)
Audience string JWT audience (required)
RequireMFA bool false Enforce TOTP MFA for all users
ReuseRefreshTokens bool false Allow reuse of refresh tokens
PrivateRSAKey *rsa.PrivateKey RSA private key (generated if neither key nor filepath is set)
PrivateRSAKeyFilepath string Path to a PEM RSA private key file
UseHSTS bool false Enable HSTS middleware
UseCSP bool false Enable CSP middleware
UseSecureCookie bool false Set Secure flag on cookies
UseForwardedHeaders bool false Trust X-Forwarded-* headers
LogRequests bool false Log all HTTP requests
AccessTTL time.Duration 20 min Access token lifetime
RefreshTTL time.Duration 90 days Refresh token lifetime
SessionTTL time.Duration 90 days Session cookie lifetime
CodeTTL time.Duration 5 min Authorization / device code lifetime
CSRFTTL time.Duration 5 min CSRF token lifetime
BaseTemplateFilepath string templates/base.html Base layout template
LoginTemplateFilepath string templates/login.html Login page template
MFACreateTemplateFilepath string templates/mfa_create.html MFA enrolment template
MFAVerifyTemplateFilepath string templates/mfa_verify.html MFA verification template
DeviceTemplateFilepath string templates/device.html Device Code activation template
ClientStore domain.ClientStore in-memory Custom client store implementation
UserStore domain.UserStore in-memory Custom user store implementation
GrantStore domain.GrantStore in-memory Custom grant store implementation
SessionStore domain.SessionStore in-memory Custom session store implementation
MFACodeStore domain.MFACodeStore in-memory Custom MFA code store implementation
DeviceCodeStore domain.DeviceCodeStore in-memory Custom device code store implementation
Clients []Client Seed clients
Users []User Seed users

Builder methods

Method Description
UseSQLite(filepath string, databases SqliteDatabases) Persist grants, sessions and/or MFA in a SQLite file. Flags: Grants, Sessions, MFA, All.
UseLDAP(server string, config LDAPConfig) Use an LDAP directory as the user store.

LDAPConfig fields

Field Description
Bind LDAP bind DN
Password LDAP bind password
BaseDN Base DN for user searches
FilterDN Search filter; use {username} as placeholder
SubjectAttribute Attribute mapped to sub claim
NameAttribute Attribute mapped to preferred_username
EmailAttribute Attribute mapped to email
PhoneAttribute Attribute mapped to phone_number
AddressAttribute Attribute mapped to address

Roadmap

  • OIDC Discovery endpoint with JWKS
  • Authorization endpoint
    • Authorization Code with PKCE
    • Implicit flow
    • Hybrid flow
  • Device Code authorization endpoint (RFC 8628)
  • Token endpoint
    • Refresh Token
    • Client Credentials
    • Password
    • JWT Bearer (RFC 7523)
    • Device Code (RFC 8628)
  • Userinfo endpoint
  • Introspection endpoint
  • Revocation endpoint (RFC 7009)
  • End session endpoint (RP-Initiated Logout)
  • CSRF protection
  • YAML config file with ENV variable substitution
  • SQLite database for grants, sessions and MFA
  • MFA with TOTP (e.g. Google Authenticator)
  • LDAP users integration
  • MFA via email
  • Change password
  • Forgot password
  • MFA management UI
  • MongoDB store
  • Groups / roles
  • Management API

Testing

Run the full test suite:

make test
# or
go test ./... -timeout 120s

Run only the integration tests:

go test ./tests/integration/... -v -timeout 120s

Run a specific subset:

go test ./tests/integration/... -run "Test_DeviceCode|Test_JWTBearer" -v

Security

minioidc is intended for small / home networks and has not undergone a formal security audit. For production use, please review the following:

  • Always run behind a TLS-terminating reverse proxy (nginx, Caddy, Traefik) and enable secure_cookies: true and hsts: true.
  • Use a strong, random masterkey (32+ bytes) and keep it secret.
  • Rotate the RSA private key periodically; store it outside the repository.
  • Public clients (no secret_hash) require PKCE — this is enforced by the server.
  • Refresh tokens and device codes are single-use by default.

To report a vulnerability, please open a GitHub issue marked [SECURITY] or contact the maintainer directly.

Contributing

Contributions are welcome. Please:

  1. Fork the repository and create a feature branch.
  2. Add tests for any new functionality.
  3. Run make lint and make test and ensure both pass.
  4. Open a pull request with a clear description of the change.

License

This project is licensed under the MIT License — see the LICENSE file for details.

About

A lightweight OpenID Connect (OIDC) server

Topics

Resources

Stars

10 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages