npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

strapi-plugin-oidc

v1.10.9

Published

A Strapi plugin that provides OpenID Connect (OIDC) authentication functionality for the Strapi Admin Panel.

Readme

A Strapi plugin that provides OpenID Connect (OIDC) authentication for the Strapi Admin Panel. Supports Keycloak, Auth0, Okta, Azure AD, Authentik, Authelia, and any other OpenID Connect provider.

Installation

npm install strapi-plugin-oidc

Configuration

Add the plugin to config/plugins.js (or .ts):

module.exports = ({ env }) => ({
  'strapi-plugin-oidc': {
    enabled: true,
    config: {
      // Required
      OIDC_PUBLIC_URL: env('PUBLIC_URL', 'https://strapi.example.com'), // origin only — we append /strapi-plugin-oidc/oidc/callback
      OIDC_ISSUER: env('OIDC_ISSUER'), // https://your-provider or https://your-provider/realms/your-realm
      OIDC_CLIENT_ID: env('OIDC_CLIENT_ID'),
      OIDC_CLIENT_SECRET: env('OIDC_CLIENT_SECRET'),

      // Optional — defaults shown
      OIDC_SCOPE: 'openid profile email', // space-separated scopes
      OIDC_FAMILY_NAME_FIELD: 'family_name',
      OIDC_GIVEN_NAME_FIELD: 'given_name',
      OIDC_SSO_BUTTON_TEXT: 'Login via SSO',
      OIDC_ENFORCE: null, // null = use Admin UI toggle; true/false = override in config
      OIDC_SKIP_LOGIN_PAGE: null, // null = use Admin UI toggle; true/false = override in config
      REMEMBER_ME: false, // Persist session across browser restarts
      AUDIT_LOG_RETENTION_DAYS: 90, // Set to 0 to disable audit logging; otherwise entries older than this many days are purged daily at midnight
      OIDC_GROUP_FIELD: 'groups', // OIDC claim field containing group membership
      OIDC_GROUP_ROLE_MAP: '{}', // JSON map of group names to Strapi role names
      OIDC_REQUIRE_EMAIL_VERIFIED: true, // Reject logins when provider does not report email_verified=true (set false to disable)
      OIDC_TRUSTED_IP_HEADER: '', // Optional: header set by your CDN/proxy containing the real client IP (see note below); only honoured when Koa proxy mode is enabled (see below)
      OIDC_FORCE_SECURE_COOKIES: false, // Set true when behind a trusted HTTPS proxy that Strapi can't auto-detect
    },
  },
});

OIDC_PUBLIC_URL is your Strapi instance's origin (e.g. https://myapp.com). The plugin appends /strapi-plugin-oidc/oidc/callback to form the full OIDC redirect URI. If unset, falls back to the PUBLIC_URL environment variable. Only provide the scheme + host + port — no trailing slash or path.

OIDC_ISSUER is your provider's issuer URL (e.g. https://auth.example.com or https://auth.example.com/realms/myrealm). The plugin appends /.well-known/openid-configuration automatically if not present, and fetches the discovery document at startup to configure all endpoints, JWKS URI, and canonical issuer.

Security features

  • ID token verification — Enabled automatically when the discovery document includes a jwks_uri. Validates signature, issuer, audience, and expiry via jose
  • Email verificationOIDC_REQUIRE_EMAIL_VERIFIED: true (default) rejects unverified emails
  • CSRF protection — OIDC state/nonce and POST-only logout endpoint
  • Rate limiting — 1 000 req/min per IP+UA (in-process; use a reverse-proxy-level limiter for multi-node)
  • Secure cookiesOIDC_FORCE_SECURE_COOKIES ensures cookies are marked Secure

Client IP attribution and reverse proxies

The plugin logs client IPs for rate-limit buckets and audit logs. When Strapi runs behind a reverse proxy, enable Koa proxy mode so Strapi trusts X-Forwarded-For; otherwise all IPs will be the proxy's internal address.

In config/server.ts:

proxy: {
  koa: true,
},

Set OIDC_TRUSTED_IP_HEADER to the header your CDN or proxy uses to forward the real client IP. The header is only honoured when Koa proxy mode is enabled. Accepted values (all others are silently ignored):

| Header | Provider | | --------------------------- | ------------------------------------------------- | | cf-connecting-ip | Cloudflare | | true-client-ip | Cloudflare Enterprise, Akamai | | fastly-client-ip | Fastly | | fly-client-ip | Fly.io | | x-nf-client-connection-ip | Netlify | | x-real-ip | nginx (proxy_set_header X-Real-IP $remote_addr) |

Only headers that CDN/proxy vendors guarantee to strip from inbound client requests are accepted, preventing IP spoofing via forged headers.

Login

Navigate to /strapi-plugin-oidc/oidc to start the OIDC flow, or click the Login via SSO button injected into the Strapi login page.

Logout

When the discovery document includes an end_session_endpoint, clicking logout redirects to the provider's end-session URL (RP-initiated logout). If the provider session has already expired, Strapi skips the redirect and goes straight to the login page.

The logout endpoint is POST /strapi-plugin-oidc/logout. Using POST instead of GET prevents CSRF-forced-logout attacks.

Admin Settings

Manage the plugin under Settings → OIDC Plugin.

Default Roles — Strapi admin role(s) assigned to new users on first login.

Whitelist — Restrict access to specific email addresses. When empty, any authenticated OIDC user gets an account. Supports:

  • Individual emails with optional role overrides
  • JSON import / export
  • Bulk delete with confirmation

Audit Logs — Authentication events recorded and visible in the settings page. Filter by action, email, IP, and date. Download exports the current view as NDJSON. Set AUDIT_LOG_RETENTION_DAYS to 0 to disable. Records older than the configured value (default: 90 days) are purged daily.

Enforce OIDC Login — Removes email/password fields from the login page and blocks direct login API calls. Automatically disabled when the whitelist is empty to prevent lockout.

The toggle is grayed out when OIDC_ENFORCE is set in config. Lockout recovery: set OIDC_ENFORCE: false in your plugin config and restart Strapi.

Skip Login Page — Redirects unauthenticated users straight to the OIDC provider without showing the Strapi login page. Toggle under Settings → Login Settings.

The toggle is grayed out when OIDC_SKIP_LOGIN_PAGE is set in config. Set OIDC_SKIP_LOGIN_PAGE: false in your plugin config to disable and restart Strapi.

Group-to-Role Mapping

When your OIDC provider includes group membership in the userinfo response (e.g. a groups claim containing ["strapi-admins", "strapi-editors"]), you can automatically assign Strapi roles based on group membership.

| Setting | Default | Description | | --------------------- | ---------- | --------------------------------------------------------- | | OIDC_GROUP_FIELD | 'groups' | OIDC claim field that contains the group membership array | | OIDC_GROUP_ROLE_MAP | '{}' | JSON map of group names → Strapi role names |

Example configuration

module.exports = ({ env }) => ({
  'strapi-plugin-oidc': {
    enabled: true,
    config: {
      // ... other OIDC config ...
      OIDC_GROUP_FIELD: 'groups',
      OIDC_GROUP_ROLE_MAP: JSON.stringify({
        'strapi-admins': ['Super Admin'],
        'strapi-editors': ['Editor'],
        'strapi-authors': ['Editor', 'Author'],
      }),
    },
  },
});

Role names are the display names shown in Settings → Roles (e.g. "Editor", "Super Admin", "Author"). IDs are not supported — use names for clarity.

Role assignment precedence

  1. OIDC groups match OIDC_GROUP_ROLE_MAP → mapped Strapi roles
  2. No match or no mapping → default OIDC roles (new users only)

Role updates on subsequent logins

  • New users — Roles assigned on first login (group-mapped or default).
  • Existing users with group match — Roles updated to reflect current mapping.
  • Existing users without group match — Roles left unchanged. Manually-assigned roles are never overwritten.

Whitelist API

The whitelist can be managed programmatically using a Strapi API token. All endpoints are under /api/strapi-plugin-oidc and require Authorization: Bearer <token>.

Full-access tokens can call all routes. Custom tokens must be granted one of the following scopes (Settings → API Tokens → Custom → plugin permissions):

| Scope | Routes | | --------------------------------------------- | ----------------------------------------------- | | plugin::strapi-plugin-oidc.whitelist.read | GET /whitelist, GET /whitelist/export | | plugin::strapi-plugin-oidc.whitelist.write | POST /whitelist, POST /whitelist/import | | plugin::strapi-plugin-oidc.whitelist.delete | DELETE /whitelist, DELETE /whitelist/:email |

| Method | Path | Description | | -------- | ------------------------------------------ | ---------------------- | | GET | /api/strapi-plugin-oidc/whitelist | List all entries | | GET | /api/strapi-plugin-oidc/whitelist/export | Export as JSON | | POST | /api/strapi-plugin-oidc/whitelist | Add one or more emails | | POST | /api/strapi-plugin-oidc/whitelist/import | Bulk import | | DELETE | /api/strapi-plugin-oidc/whitelist/:email | Remove by email | | DELETE | /api/strapi-plugin-oidc/whitelist | Remove all entries |

API calls write directly to the database — there is no unsaved state.

Import format

Accepted by both the API import endpoint and the Admin UI import button. If the email already exists as a Strapi admin user, their current roles are used automatically.

[{ "email": "[email protected]" }, { "email": "[email protected]" }]

Duplicate emails within the payload and emails already in the whitelist are silently skipped.

Examples

# List
curl -H "Authorization: Bearer <token>" \
  https://strapi.example.com/api/strapi-plugin-oidc/whitelist

# Export
curl -H "Authorization: Bearer <token>" \
  https://strapi.example.com/api/strapi-plugin-oidc/whitelist/export \
  -o whitelist.json

# Add
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}' \
  https://strapi.example.com/api/strapi-plugin-oidc/whitelist

# Bulk import
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"users": [{"email": "[email protected]"}, {"email": "[email protected]"}]}' \
  https://strapi.example.com/api/strapi-plugin-oidc/whitelist/import

# Delete one (by email)
curl -X DELETE -H "Authorization: Bearer <token>" \
  "https://strapi.example.com/api/strapi-plugin-oidc/whitelist/user%40example.com"

# Delete all
curl -X DELETE -H "Authorization: Bearer <token>" \
  https://strapi.example.com/api/strapi-plugin-oidc/whitelist

Audit Log API

Audit log entries can be fetched programmatically using a Strapi API token. Endpoints are under /api/strapi-plugin-oidc and require Authorization: Bearer <token>.

Full-access tokens can call all routes. Custom tokens must be granted one of the following scopes:

| Scope | Routes | | ----------------------------------------- | ------------------------------------------- | | plugin::strapi-plugin-oidc.audit.read | GET /audit-logs, GET /audit-logs/export | | plugin::strapi-plugin-oidc.audit.delete | DELETE /audit-logs |

| Method | Path | Description | | -------- | ------------------------------------------- | ----------------------------------- | | GET | /api/strapi-plugin-oidc/audit-logs | Paginated list of log entries | | GET | /api/strapi-plugin-oidc/audit-logs/export | Matching records as NDJSON download | | DELETE | /api/strapi-plugin-oidc/audit-logs | Delete all audit log entries (204) |

Query parameters (GET /audit-logs, GET /audit-logs/export)

| Parameter | Default | Description | | ---------- | ------- | ---------------------------------------------- | | page | 1 | Page number (list endpoint only) | | pageSize | 25 | Results per page, max 100 (list only) | | filters | — | Field/operator filters, same on both endpoints |

Results are sorted newest-first. The response shape is:

{
  "results": [
    {
      "id": 42,
      "action": "login_success",
      "email": "[email protected]",
      "ip": "203.0.113.42",
      "details": null,
      "createdAt": "2026-04-08T12:00:00.000Z",
      "updatedAt": "2026-04-08T12:00:00.000Z"
    }
  ],
  "pagination": { "page": 1, "pageSize": 25, "total": 1, "pageCount": 1 }
}

The NDJSON export emits one row per line with { datetime, action, email, ip, details } where datetime is the entry's createdAt timestamp.

Filtering

Use filters[<field>][<operator>]=<value> to narrow results. Invalid filters return a 400.

| Field | Operators | Value | | ----------- | ---------------------------------------------------- | ------------------------------------------------------- | | action | $eq, $in | One of the recorded actions | | email | $eq, $contains, $endsWith, $null, $notNull | String (use true/false with $null / $notNull) | | ip | $eq, $contains, $endsWith, $null, $notNull | String (use true/false with $null / $notNull) | | createdAt | $gte, $lt, $lte, $between, $in | ISO-8601 UTC timestamp, e.g. 2026-04-08T00:00:00.000Z |

$between takes a [start, end] pair. $in on createdAt takes a list of day-start timestamps and matches anything within that UTC day.

# Failed logins on one day
curl -H "Authorization: Bearer <token>" -G \
  --data-urlencode 'filters[action][$eq]=login_failure' \
  --data-urlencode 'filters[createdAt][$gte]=2026-04-08T00:00:00.000Z' \
  --data-urlencode 'filters[createdAt][$lt]=2026-04-09T00:00:00.000Z' \
  https://strapi.example.com/api/strapi-plugin-oidc/audit-logs

Recorded actions

| Action | Trigger | | ----------------------- | ----------------------------------------------------------------- | | login_success | Successful OIDC authentication | | user_created | New Strapi admin user created during login | | login_failure | Unexpected error during the OIDC login flow | | missing_code | Callback received without an authorisation code | | state_mismatch | CSRF state cookie does not match callback parameter | | nonce_mismatch | ID token nonce does not match the session nonce | | token_exchange_failed | Provider returned an error during token exchange | | whitelist_rejected | Email not present in the active whitelist | | email_not_verified | Provider did not report email_verified=true | | id_token_invalid | ID token failed signature, issuer, audience, or expiry validation | | logout | User logged out via /logout | | session_expired | Logout attempted but provider session already stale |

Each event is also emitted on Strapi's internal eventHub as strapi-plugin-oidc::auth.<action>, which Enterprise audit log listeners pick up automatically.

Examples

# Paginated list
curl -H "Authorization: Bearer <token>" \
  "https://strapi.example.com/api/strapi-plugin-oidc/audit-logs?page=1&pageSize=50"

# NDJSON export
curl -H "Authorization: Bearer <token>" \
  https://strapi.example.com/api/strapi-plugin-oidc/audit-logs/export \
  -o oidc-audit-log.ndjson

Credits & Changes

This plugin is a hard fork of strapi-plugin-sso by yasudacloud. Huge thanks to them for creating the foundation of this plugin!

Changes from the original:

  • OIDC-only (removed other SSO methods)
  • Redesigned whitelist and role management UI using native Strapi components
  • OIDC enforcement and skip-login-page via admin toggles, overridable via OIDC_ENFORCE / OIDC_SKIP_LOGIN_PAGE config
  • RP-initiated logout with smart session detection
  • Migrated to Vitest with e2e coverage
  • Config variable names aligned with OIDC discovery document field names
  • Login via SSO button always injected; text configurable via OIDC_SSO_BUTTON_TEXT
  • Whitelist REST API with JSON import/export, bulk delete, delete by email
  • Hardened OIDC flow: server-generated state and nonce, PKCE, Bearer token auth for userinfo, generic error messages on failure
  • Audit log: records all auth events to a queryable table with UI, JSON/NDJSON export, and REST API

License

MIT