{"id":25913075,"url":"https://github.com/fernandoescolar/minioidc","last_synced_at":"2026-06-11T07:31:43.698Z","repository":{"id":196894985,"uuid":"696871517","full_name":"fernandoescolar/minioidc","owner":"fernandoescolar","description":"A lightweight OpenID Connect (OIDC) server","archived":false,"fork":false,"pushed_at":"2026-05-19T18:26:53.000Z","size":283,"stargazers_count":10,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-19T21:13:55.623Z","etag":null,"topics":["golang","lightweight","oidc","server"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fernandoescolar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-09-26T15:33:54.000Z","updated_at":"2026-05-19T18:26:53.000Z","dependencies_parsed_at":"2023-10-01T19:27:37.714Z","dependency_job_id":"b9b8476e-a0d1-4d03-8df0-83c4db550bb6","html_url":"https://github.com/fernandoescolar/minioidc","commit_stats":null,"previous_names":["fernandoescolar/minioidc"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/fernandoescolar/minioidc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoescolar%2Fminioidc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoescolar%2Fminioidc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoescolar%2Fminioidc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoescolar%2Fminioidc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fernandoescolar","download_url":"https://codeload.github.com/fernandoescolar/minioidc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoescolar%2Fminioidc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34188272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","lightweight","oidc","server"],"created_at":"2025-03-03T10:18:07.292Z","updated_at":"2026-06-11T07:31:43.692Z","avatar_url":"https://github.com/fernandoescolar.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# minioidc\n\n[![Go](https://img.shields.io/badge/go-1.26.3+-00ADD8?logo=go)](https://go.dev/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nminioidc 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.\n\nSupported grant types:\n\n| Grant type | RFC |\n|---|---|\n| Authorization Code + PKCE | [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) |\n| Implicit | [OpenID Connect Core §3.2](https://openid.net/specs/openid-connect-core-1_0.html) |\n| Hybrid | [OpenID Connect Core §3.3](https://openid.net/specs/openid-connect-core-1_0.html) |\n| Refresh Token | [RFC 6749 §6](https://datatracker.ietf.org/doc/html/rfc6749#section-6) |\n| Client Credentials | [RFC 6749 §4.4](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) |\n| Resource Owner Password | [RFC 6749 §4.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) |\n| Device Code | [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628) |\n| JWT Bearer | [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) |\n\n## Features\n\n- **Simple and Lightweight** — designed to be a straightforward SSO solution for small or home networks.\n- **All major OAuth 2.0 grant types** — including Device Code (RFC 8628) and JWT Bearer (RFC 7523).\n- **OIDC Discovery + JWKS** — fully compliant `/.well-known/openid-configuration` and JWKS endpoint.\n- **Token Revocation** — RFC 7009 revocation endpoint for access and refresh tokens.\n- **RP-Initiated Logout** — OIDC end session endpoint with `post_logout_redirect_uri` support.\n- **Introspection** — RFC 7662 token introspection endpoint.\n- **PKCE enforcement** — required for public clients (no secret).\n- **at_hash / c_hash** — correct computation in ID tokens for implicit and hybrid flows.\n- **Client and User Configuration** — configure clients and users directly in the YAML file.\n- **Flexible Data Storage** — in-memory (default) or SQLite v3 for grants, sessions and MFA.\n- **LDAP Users** — plug in an LDAP directory as the user store.\n- **MFA** — TOTP-based MFA (e.g. Google Authenticator / Authy) per user.\n- **Customisable Templates** — override any HTML page with your own `html/template` files.\n- **Secure by Default** — HSTS, CSP, secure cookies, forwarded-header support, all opt-in via config.\n\n## Requirements\n\n- Go 1.22 or later (for running from source or using as a library)\n- Docker (optional, for the containerised setup)\n\n## Installation\n\n### From source\n\n```bash\ngit clone https://github.com/fernandoescolar/minioidc.git\ncd minioidc\nmake build          # produces ./minioidc binary\n```\n\n### Docker\n\n```bash\nmake build-docker   # builds the image\nmake run-docker     # starts the container on :8000\n```\n\n### As a Go library\n\n```bash\ngo get github.com/fernandoescolar/minioidc\n```\n\n## Getting Started\n\n1. Copy the example configuration:\n\n```bash\ncp example.env .env\ncp example1_config.yml config.yml   # simple in-memory setup\n# or\ncp example2_config.yml config.yml   # SQLite + LDAP setup\n```\n\n2. Edit `config.yml` to set your issuer, clients, and users (see [Configuration](#configuration)).\n\n3. Run the server:\n\n```bash\nMINIOIDC_ADDR=:8000 MINIOIDC_CONFIG=config.yml ./minioidc\n# or\nmake run\n```\n\n4. Visit [http://localhost:8000](http://localhost:8000) — the welcome page confirms the server is running.\n\n5. The OIDC discovery document is available at:\n\n```\nhttp://localhost:8000/.well-known/openid-configuration\n```\n\n## Endpoints\n\n| Endpoint | Method | Description |\n|---|---|---|\n| `/.well-known/openid-configuration` | GET | OIDC discovery document |\n| `/.well-known/jwks.json` | GET | JSON Web Key Set |\n| `/connect/authorize` | GET | Authorization endpoint |\n| `/connect/token` | POST | Token endpoint |\n| `/connect/userinfo` | GET / POST | Userinfo endpoint |\n| `/connect/introspect` | POST | Token introspection (RFC 7662) |\n| `/connect/revoke` | POST | Token revocation (RFC 7009) |\n| `/connect/endsession` | GET / POST | RP-Initiated Logout |\n| `/connect/deviceauthorization` | POST | Device authorization (RFC 8628) |\n| `/connect/device` | GET / POST | Device activation page (user-facing) |\n| `/login` | GET / POST | Login page |\n| `/mfa/create` | GET / POST | MFA enrolment page |\n| `/mfa/verify` | GET / POST | MFA verification page |\n\n## Makefile Commands\n\n| Command | Description |\n|---|---|\n| `make build` | Build the binary |\n| `make run` | Run the binary |\n| `make build-docker` | Build the Docker image |\n| `make run-docker` | Run the Docker image |\n| `make test` | Run all tests |\n| `make clean` | Remove build artefacts |\n| `make lint` | Run the linter |\n| `make hash text=\u003cplaintext\u003e` | Generate a bcrypt hash for a password or client secret |\n\n## Configuration\n\nSet the following environment variables before starting the server:\n\n| Variable | Default | Description |\n|---|---|---|\n| `MINIOIDC_ADDR` | `:8000` | Address and port to listen on |\n| `MINIOIDC_CONFIG` | — | Path to the YAML configuration file (required) |\n\n### Full configuration reference\n\n```yaml\nname: My MiniOIDC\nmasterkey: 12345678901234567890123456789012   # AES master key for internal encryption\nissuer: http://example.com\naudience: http://example.com\nrequire_mfa: false\nreuse_refresh_tokens: false\nprivate_rsa_key_path: private_key.pem         # omit to generate a random key on startup\n\nmiddlewares:\n  hsts: true            # HTTP Strict Transport Security\n  csp: true             # Content Security Policy\n  secure_cookies: true  # Secure flag on session cookies\n  forward_headers: true # Trust X-Forwarded-* headers (use behind a reverse proxy)\n  log_requests: true    # Log every request with duration\n\nttl:\n  access: 20        # Access token TTL in minutes (default 20)\n  refresh: 129600   # Refresh token TTL in minutes (default 90 days)\n  session: 129600   # Session TTL in minutes (default 90 days)\n  code: 5           # Authorization / device code TTL in minutes (default 5)\n  csrf: 5           # CSRF token TTL in minutes (default 5)\n\nsqlite:\n  filepath: db.sqlite3\n  use_in_grants: true\n  use_in_sessions: true\n  use_in_mfa: true\n\nldap:\n  server: localhost:389\n  bind: uid=admin,cn=users,dc=example,dc=com\n  password: password\n  base_dn: dc=example,dc=com\n  filter_dn: (\u0026(uid={username})(objectClass=person))\n  attributes:\n    subject: uidNumber\n    name: uid\n    email: mail\n    phone: phone\n    address: address\n\ntemplates:\n  base: templates/base.html\n  login: templates/login.html\n  mfa_create: templates/mfa_create.html\n  mfa_verify: templates/mfa_verify.html\n  device: templates/device.html   # Device Code activation page\n\nclients:\n  - id: myclient\n    secret_hash: $2a$06$L6/zALdtbkYajjHTZUW29ePBEb/hwhgjhXC4YpHANavvKDJl69ctK  # \"secret\"\n    redirect_uris:\n      - http://myapi.com/callback\n  - id: publicclient\n    secret_hash: \"\"   # public client — no secret, PKCE required\n    redirect_uris:\n      - http://myapp.com/callback\n\nusers:\n  - subject: \"1\"\n    email: user@mail.com\n    email_verified: true\n    preferred_username: user\n    password_hash: $2a$06$03dduqc0lMbsb5go/l6RI.cRb03Hos9CMpgm5/yYuRsSQPHtrFwSq  # \"password\"\n    phone: +1234567890\n    address: 1 Main St. City, State 12345\n    groups:\n      - admin\n```\n\n**Root settings**\n\n| Key | Default | Description |\n|---|---|---|\n| `issuer` | — | OIDC issuer URL (required) |\n| `audience` | — | JWT audience (required) |\n| `require_mfa` | `false` | Enforce TOTP MFA for all users |\n| `reuse_refresh_tokens` | `false` | Allow refresh tokens to be used more than once |\n| `private_rsa_key_path` | — | Path to a PEM-encoded RSA private key; a 2048-bit key is generated if omitted |\n\n**`middlewares`**\n\n| Key | Default | Description |\n|---|---|---|\n| `hsts` | `false` | Add `Strict-Transport-Security` header |\n| `csp` | `false` | Add `Content-Security-Policy` header |\n| `secure_cookies` | `false` | Mark session cookie as `Secure` |\n| `forward_headers` | `false` | Trust `X-Forwarded-Proto` / `X-Forwarded-Host` |\n| `log_requests` | `false` | Log each request with method, path, status and duration |\n\n**`ttl`** — all values are in **minutes**\n\n**`sqlite`** — omit the section entirely to use in-memory stores\n\n**`ldap`** — if set, the `users` section is ignored\n\n\u003e Static assets (CSS, JS, images) go in the `static/` directory and are served at `/static/`.\n\u003e Reference them from templates as `\u003clink rel=\"stylesheet\" href=\"/static/styles.css\"\u003e`.\n\nEnvironment variable substitution is supported anywhere in the YAML:\n\n```yaml\nmasterkey: ${MINIOIDC_MASTER_KEY}\nldap:\n  password: $LDAP_PASSWORD\n```\n\n## Use in your projects\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/fernandoescolar/minioidc/pkg/api\"\n)\n\nfunc main() {\n\tbuilder := \u0026api.Builder{\n\t\tIssuer:   \"https://minioidc.example.com\",\n\t\tAudience: \"https://api.example.com\",\n\t\tClients: []api.Client{\n\t\t\t{\n\t\t\t\tID:           \"myclient\",\n\t\t\t\tSecretHash:   \"$2a$06$L6/zALdtbkYajjHTZUW29ePBEb/hwhgjhXC4YpHANavvKDJl69ctK\", // \"secret\"\n\t\t\t\tRedirectURIs: []string{\"https://api.example.com/callback\"},\n\t\t\t},\n\t\t},\n\t\tUsers: []api.User{\n\t\t\t{\n\t\t\t\tSubject:           \"0000001\",\n\t\t\t\tPreferredUsername: \"user\",\n\t\t\t\tPasswordHash:      \"$2a$06$03dduqc0lMbsb5go/l6RI.cRb03Hos9CMpgm5/yYuRsSQPHtrFwSq\", // \"password\"\n\t\t\t},\n\t\t},\n\t}\n\n\tminioidc, err := builder.Build()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tmux := http.NewServeMux()\n\thandler := minioidc.Wrap(mux)\n\n\tlog.Println(\"Listening on http://localhost:8000\")\n\tif err := http.ListenAndServe(\":8000\", handler); err != nil \u0026\u0026 err != http.ErrServerClosed {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n### Builder fields\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `Name` | `string` | `\"minioidc\"` | Display name shown on login/device pages |\n| `MasterKey` | `string` | random | AES key for internal encryption (auto-generated if empty) |\n| `Issuer` | `string` | — | OIDC issuer URL **(required)** |\n| `Audience` | `string` | — | JWT audience **(required)** |\n| `RequireMFA` | `bool` | `false` | Enforce TOTP MFA for all users |\n| `ReuseRefreshTokens` | `bool` | `false` | Allow reuse of refresh tokens |\n| `PrivateRSAKey` | `*rsa.PrivateKey` | — | RSA private key (generated if neither key nor filepath is set) |\n| `PrivateRSAKeyFilepath` | `string` | — | Path to a PEM RSA private key file |\n| `UseHSTS` | `bool` | `false` | Enable HSTS middleware |\n| `UseCSP` | `bool` | `false` | Enable CSP middleware |\n| `UseSecureCookie` | `bool` | `false` | Set `Secure` flag on cookies |\n| `UseForwardedHeaders` | `bool` | `false` | Trust `X-Forwarded-*` headers |\n| `LogRequests` | `bool` | `false` | Log all HTTP requests |\n| `AccessTTL` | `time.Duration` | 20 min | Access token lifetime |\n| `RefreshTTL` | `time.Duration` | 90 days | Refresh token lifetime |\n| `SessionTTL` | `time.Duration` | 90 days | Session cookie lifetime |\n| `CodeTTL` | `time.Duration` | 5 min | Authorization / device code lifetime |\n| `CSRFTTL` | `time.Duration` | 5 min | CSRF token lifetime |\n| `BaseTemplateFilepath` | `string` | `templates/base.html` | Base layout template |\n| `LoginTemplateFilepath` | `string` | `templates/login.html` | Login page template |\n| `MFACreateTemplateFilepath` | `string` | `templates/mfa_create.html` | MFA enrolment template |\n| `MFAVerifyTemplateFilepath` | `string` | `templates/mfa_verify.html` | MFA verification template |\n| `DeviceTemplateFilepath` | `string` | `templates/device.html` | Device Code activation template |\n| `ClientStore` | `domain.ClientStore` | in-memory | Custom client store implementation |\n| `UserStore` | `domain.UserStore` | in-memory | Custom user store implementation |\n| `GrantStore` | `domain.GrantStore` | in-memory | Custom grant store implementation |\n| `SessionStore` | `domain.SessionStore` | in-memory | Custom session store implementation |\n| `MFACodeStore` | `domain.MFACodeStore` | in-memory | Custom MFA code store implementation |\n| `DeviceCodeStore` | `domain.DeviceCodeStore` | in-memory | Custom device code store implementation |\n| `Clients` | `[]Client` | — | Seed clients |\n| `Users` | `[]User` | — | Seed users |\n\n### Builder methods\n\n| Method | Description |\n|---|---|\n| `UseSQLite(filepath string, databases SqliteDatabases)` | Persist grants, sessions and/or MFA in a SQLite file. Flags: `Grants`, `Sessions`, `MFA`, `All`. |\n| `UseLDAP(server string, config LDAPConfig)` | Use an LDAP directory as the user store. |\n\n### LDAPConfig fields\n\n| Field | Description |\n|---|---|\n| `Bind` | LDAP bind DN |\n| `Password` | LDAP bind password |\n| `BaseDN` | Base DN for user searches |\n| `FilterDN` | Search filter; use `{username}` as placeholder |\n| `SubjectAttribute` | Attribute mapped to `sub` claim |\n| `NameAttribute` | Attribute mapped to `preferred_username` |\n| `EmailAttribute` | Attribute mapped to `email` |\n| `PhoneAttribute` | Attribute mapped to `phone_number` |\n| `AddressAttribute` | Attribute mapped to `address` |\n\n## Roadmap\n\n- [x] OIDC Discovery endpoint with JWKS\n- [x] Authorization endpoint\n  - [x] Authorization Code with PKCE\n  - [x] Implicit flow\n  - [x] Hybrid flow\n- [x] Device Code authorization endpoint (RFC 8628)\n- [x] Token endpoint\n  - [x] Refresh Token\n  - [x] Client Credentials\n  - [x] Password\n  - [x] JWT Bearer (RFC 7523)\n  - [x] Device Code (RFC 8628)\n- [x] Userinfo endpoint\n- [x] Introspection endpoint\n- [x] Revocation endpoint (RFC 7009)\n- [x] End session endpoint (RP-Initiated Logout)\n- [x] CSRF protection\n- [x] YAML config file with ENV variable substitution\n- [x] SQLite database for grants, sessions and MFA\n- [x] MFA with TOTP (e.g. Google Authenticator)\n- [x] LDAP users integration\n- [ ] MFA via email\n- [ ] Change password\n- [ ] Forgot password\n- [ ] MFA management UI\n- [ ] MongoDB store\n- [ ] Groups / roles\n- [ ] Management API\n\n## Testing\n\nRun the full test suite:\n\n```bash\nmake test\n# or\ngo test ./... -timeout 120s\n```\n\nRun only the integration tests:\n\n```bash\ngo test ./tests/integration/... -v -timeout 120s\n```\n\nRun a specific subset:\n\n```bash\ngo test ./tests/integration/... -run \"Test_DeviceCode|Test_JWTBearer\" -v\n```\n\n## Security\n\nminioidc is intended for **small / home networks** and has not undergone a formal security audit. For production use, please review the following:\n\n- Always run behind a TLS-terminating reverse proxy (nginx, Caddy, Traefik) and enable `secure_cookies: true` and `hsts: true`.\n- Use a strong, random `masterkey` (32+ bytes) and keep it secret.\n- Rotate the RSA private key periodically; store it outside the repository.\n- Public clients (no `secret_hash`) **require PKCE** — this is enforced by the server.\n- Refresh tokens and device codes are single-use by default.\n\nTo report a vulnerability, please open a GitHub issue marked **[SECURITY]** or contact the maintainer directly.\n\n## Contributing\n\nContributions are welcome. Please:\n\n1. Fork the repository and create a feature branch.\n2. Add tests for any new functionality.\n3. Run `make lint` and `make test` and ensure both pass.\n4. Open a pull request with a clear description of the change.\n\n## License\n\nThis project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffernandoescolar%2Fminioidc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffernandoescolar%2Fminioidc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffernandoescolar%2Fminioidc/lists"}