strapi-plugin-oidc 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,8 +5,11 @@
5
5
  <a href="https://www.npmjs.com/package/strapi-plugin-oidc">
6
6
  <img src="https://img.shields.io/npm/v/strapi-plugin-oidc.svg" alt="npm version">
7
7
  </a>
8
- <a href="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/test.yml">
9
- <img src="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/test.yml/badge.svg?branch=main" alt="Tests">
8
+ <a href="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/ci.yml">
9
+ <img src="https://github.com/edmogeor/strapi-plugin-oidc/actions/workflows/ci.yml/badge.svg" alt="CI"/>
10
+ </a>
11
+ <a href="./LICENSE">
12
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"/>
10
13
  </a>
11
14
  </p>
12
15
  </div>
@@ -28,7 +31,7 @@ module.exports = ({ env }) => ({
28
31
  'strapi-plugin-oidc': {
29
32
  enabled: true,
30
33
  config: {
31
- // Required
34
+ // Required — find these in your provider's OIDC discovery document
32
35
  OIDC_CLIENT_ID: env('OIDC_CLIENT_ID'),
33
36
  OIDC_CLIENT_SECRET: env('OIDC_CLIENT_SECRET'),
34
37
  OIDC_REDIRECT_URI: env('OIDC_REDIRECT_URI'), // https://your-strapi.com/strapi-plugin-oidc/oidc/callback
@@ -41,18 +44,27 @@ module.exports = ({ env }) => ({
41
44
  OIDC_GRANT_TYPE: 'authorization_code',
42
45
  OIDC_FAMILY_NAME_FIELD: 'family_name',
43
46
  OIDC_GIVEN_NAME_FIELD: 'given_name',
44
- OIDC_END_SESSION_ENDPOINT: '', // Provider end-session URL; omit to redirect to Strapi login
47
+ OIDC_END_SESSION_ENDPOINT: '', // Provider end-session URL for RP-initiated logout
45
48
  OIDC_SSO_BUTTON_TEXT: 'Login via SSO',
46
49
  OIDC_ENFORCE: null, // null = use Admin UI toggle; true/false = override in config
47
50
  REMEMBER_ME: false, // Persist session across browser restarts
51
+ AUDIT_LOG_RETENTION_DAYS: 90, // Set to 0 to disable audit logging; otherwise entries older than this many days are purged daily at midnight
52
+ OIDC_GROUP_FIELD: 'groups', // OIDC claim field containing group membership
53
+ OIDC_GROUP_ROLE_MAP: '{}', // JSON map of group names to Strapi role names
48
54
  },
49
55
  },
50
56
  });
51
57
  ```
52
58
 
59
+ All required values come from your provider's OIDC discovery document, typically available at `https://your-provider/.well-known/openid-configuration`.
60
+
53
61
  ## Login
54
62
 
55
- Navigate to `/strapi-plugin-oidc/oidc` to start the OIDC flow, or click the **Login via SSO** button that is always injected into the Strapi login page.
63
+ Navigate to `/strapi-plugin-oidc/oidc` to start the OIDC flow, or click the **Login via SSO** button injected into the Strapi login page.
64
+
65
+ ## Logout
66
+
67
+ When `OIDC_END_SESSION_ENDPOINT` is set, clicking logout in Strapi redirects the browser 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.
56
68
 
57
69
  ## Admin Settings
58
70
 
@@ -60,18 +72,57 @@ Manage the plugin under **Settings → OIDC Plugin**.
60
72
 
61
73
  **Default Roles** — Select which Strapi admin role(s) are assigned to new users on first login.
62
74
 
63
- **Whitelist** — Restrict access to specific email addresses. When the whitelist is enabled, only listed emails can log in. When empty, any successfully authenticated OIDC user gets an account. The whitelist supports:
75
+ **Whitelist** — Restrict access to specific email addresses. When enabled, only listed emails can log in. When empty, any successfully authenticated OIDC user gets an account. The whitelist supports:
64
76
 
65
77
  - Adding individual emails with optional role overrides
66
78
  - JSON import / export (see [format](#import-format) below)
67
79
  - Bulk delete with confirmation
68
80
  - Unsaved changes are held in the UI until **Save Changes** is clicked
69
81
 
82
+ **Audit Logs** — Every authentication event is recorded in the plugin's audit log table and visible in the **Audit Logs** section at the bottom of the settings page. A **Download** button exports all records as JSON, compatible with SIEM tools and log processors. Setting `AUDIT_LOG_RETENTION_DAYS` to `0` disables audit logging entirely. Otherwise records older than the configured value (default: 90 days) are automatically purged by a daily cron job. The audit log is also accessible [via API](#audit-log-api).
83
+
70
84
  **Enforce OIDC Login** — Removes the standard email/password fields from the login page and blocks direct login API calls server-side. Automatically disabled when the whitelist is empty to prevent lockout.
71
85
 
72
86
  - The toggle is grayed out and locked when `OIDC_ENFORCE` is set in config.
73
87
  - **Lockout recovery**: set `OIDC_ENFORCE: false` in your plugin config and restart Strapi. This writes through to the database so removing the variable afterwards keeps the setting.
74
88
 
89
+ ## Group-to-Role Mapping
90
+
91
+ 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.
92
+
93
+ | Setting | Default | Description |
94
+ | --------------------- | ---------- | --------------------------------------------------------- |
95
+ | `OIDC_GROUP_FIELD` | `'groups'` | OIDC claim field that contains the group membership array |
96
+ | `OIDC_GROUP_ROLE_MAP` | `'{}'` | JSON map of group names → Strapi role names |
97
+
98
+ ### Example configuration
99
+
100
+ ```javascript
101
+ module.exports = ({ env }) => ({
102
+ 'strapi-plugin-oidc': {
103
+ enabled: true,
104
+ config: {
105
+ // ... other OIDC config ...
106
+ OIDC_GROUP_FIELD: 'groups',
107
+ OIDC_GROUP_ROLE_MAP: JSON.stringify({
108
+ 'strapi-admins': ['Super Admin'],
109
+ 'strapi-editors': ['Editor'],
110
+ 'strapi-authors': ['Editor', 'Author'],
111
+ }),
112
+ },
113
+ },
114
+ });
115
+ ```
116
+
117
+ Role names are the **display names** shown in **Settings → Roles** (e.g. `"Editor"`, `"Super Admin"`, `"Author"`). IDs are not supported — use names for clarity.
118
+
119
+ ### Role assignment precedence
120
+
121
+ 1. **User's OIDC groups match `OIDC_GROUP_ROLE_MAP`** → use the mapped Strapi roles
122
+ 2. **No group match or no mapping configured** → use the default OIDC roles
123
+
124
+ > **Note:** Existing users' roles are updated on every login to reflect current group membership.
125
+
75
126
  ## Whitelist API
76
127
 
77
128
  The whitelist can be managed programmatically using a Strapi **API token** (Settings → API Tokens → Full Access). All endpoints are under `/api/strapi-plugin-oidc` and require `Authorization: Bearer <token>`.
@@ -79,9 +130,10 @@ The whitelist can be managed programmatically using a Strapi **API token** (Sett
79
130
  | Method | Path | Description |
80
131
  | -------- | ------------------------------------------ | ---------------------- |
81
132
  | `GET` | `/api/strapi-plugin-oidc/whitelist` | List all entries |
133
+ | `GET` | `/api/strapi-plugin-oidc/whitelist/export` | Export as JSON |
82
134
  | `POST` | `/api/strapi-plugin-oidc/whitelist` | Add one or more emails |
83
135
  | `POST` | `/api/strapi-plugin-oidc/whitelist/import` | Bulk import |
84
- | `DELETE` | `/api/strapi-plugin-oidc/whitelist/:id` | Remove by ID |
136
+ | `DELETE` | `/api/strapi-plugin-oidc/whitelist/:email` | Remove by email |
85
137
  | `DELETE` | `/api/strapi-plugin-oidc/whitelist` | Remove all entries |
86
138
 
87
139
  API calls write directly to the database — there is no unsaved state.
@@ -107,6 +159,11 @@ Duplicate emails within the payload and emails already in the whitelist are sile
107
159
  curl -H "Authorization: Bearer <token>" \
108
160
  http://localhost:1337/api/strapi-plugin-oidc/whitelist
109
161
 
162
+ # Export
163
+ curl -H "Authorization: Bearer <token>" \
164
+ http://localhost:1337/api/strapi-plugin-oidc/whitelist/export \
165
+ -o whitelist.json
166
+
110
167
  # Add
111
168
  curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
112
169
  -d '{"email": "user@example.com", "roles": ["Editor"]}' \
@@ -117,33 +174,93 @@ curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/js
117
174
  -d '{"users": [{"email": "a@example.com", "roles": ["Editor"]}, {"email": "b@example.com"}]}' \
118
175
  http://localhost:1337/api/strapi-plugin-oidc/whitelist/import
119
176
 
120
- # Delete one
177
+ # Delete one (by email)
121
178
  curl -X DELETE -H "Authorization: Bearer <token>" \
122
- http://localhost:1337/api/strapi-plugin-oidc/whitelist/42
179
+ "http://localhost:1337/api/strapi-plugin-oidc/whitelist/user%40example.com"
123
180
 
124
181
  # Delete all
125
182
  curl -X DELETE -H "Authorization: Bearer <token>" \
126
183
  http://localhost:1337/api/strapi-plugin-oidc/whitelist
127
184
  ```
128
185
 
186
+ ## Audit Log API
187
+
188
+ Audit log entries can be fetched programmatically using a Strapi **API token** (Settings → API Tokens → Full Access). Endpoints are under `/api/strapi-plugin-oidc` and require `Authorization: Bearer <token>`.
189
+
190
+ | Method | Path | Description |
191
+ | ------ | ------------------------------------------- | ----------------------------- |
192
+ | `GET` | `/api/strapi-plugin-oidc/audit-logs` | Paginated list of log entries |
193
+ | `GET` | `/api/strapi-plugin-oidc/audit-logs/export` | All records as JSON download |
194
+
195
+ ### Query parameters (`GET /audit-logs`)
196
+
197
+ | Parameter | Default | Description |
198
+ | ---------- | ------- | ---------------- |
199
+ | `page` | `1` | Page number |
200
+ | `pageSize` | `25` | Results per page |
201
+
202
+ Results are sorted newest-first. The response shape is:
203
+
204
+ ```json
205
+ {
206
+ "results": [
207
+ {
208
+ "datetime": "2026-04-08T12:00:00.000Z",
209
+ "action": "login_success",
210
+ "email": "alice@example.com",
211
+ "ip": "203.0.113.42"
212
+ }
213
+ ],
214
+ "pagination": { "page": 1, "pageSize": 25, "total": 1, "pageCount": 1 }
215
+ }
216
+ ```
217
+
218
+ ### Recorded actions
219
+
220
+ | Action | Trigger |
221
+ | ----------------------- | --------------------------------------------------- |
222
+ | `login_success` | Successful OIDC authentication |
223
+ | `user_created` | New Strapi admin user created during login |
224
+ | `login_failure` | Generic authentication error (missing code, etc.) |
225
+ | `state_mismatch` | CSRF state cookie does not match callback parameter |
226
+ | `nonce_mismatch` | ID token nonce does not match the session nonce |
227
+ | `token_exchange_failed` | Provider returned an error during token exchange |
228
+ | `whitelist_rejected` | Email not present in the active whitelist |
229
+ | `logout` | User logged out via `/logout` |
230
+ | `session_expired` | Logout attempted but provider session already stale |
231
+
232
+ Each event is also emitted on Strapi's internal eventHub as `strapi-plugin-oidc::auth.<action>`, which Enterprise audit log listeners pick up automatically.
233
+
234
+ ### Examples
235
+
236
+ ```bash
237
+ # Paginated list
238
+ curl -H "Authorization: Bearer <token>" \
239
+ "http://localhost:1337/api/strapi-plugin-oidc/audit-logs?page=1&pageSize=50"
240
+
241
+ # JSON export
242
+ curl -H "Authorization: Bearer <token>" \
243
+ http://localhost:1337/api/strapi-plugin-oidc/audit-logs/export \
244
+ -o oidc-audit-log.json
245
+ ```
246
+
129
247
  ## Credits & Changes
130
248
 
131
249
  This plugin is a hard fork of [`strapi-plugin-sso`](https://github.com/yasudacloud/strapi-plugin-sso) by **yasudacloud**. Huge thanks to them for creating the foundation of this plugin!
132
250
 
133
- ### Changes made to the original codebase:
134
-
135
- - Removed alternative SSO methods to simplify the plugin.
136
- - Redesigned the Whitelist and Role management UI (switched to native Strapi cards, added pagination, etc.).
137
- - Added an OIDC logout redirect URL.
138
- - Added an option to "Enforce OIDC login" with an admin toggle (automatically disabled if the whitelist is empty).
139
- - Migrated the testing framework to Vitest and added comprehensive test coverage for controllers and services.
140
- - Cleaned up dead code and unused dependencies to improve maintainability.
141
- - Upgraded to use newer versions of Node.js.
142
- - Added styled success and error pages.
143
- - Always injects a "Login via SSO" button on the Strapi login page. Button text is configurable via `OIDC_SSO_BUTTON_TEXT`. When enforcement is on, standard login fields are hidden so only the SSO button is visible.
144
- - Whitelist improvements:
145
- - JSON import and export (uses human-readable role names).
146
- - Bulk delete all entries with a confirmation dialog.
147
- - Unsaved changes confirmation when navigating away from the settings page.
148
- - Programmatic API for managing the whitelist via Strapi API tokens (list, register, import, delete, delete all).
149
- - Added misc. quality of life improvements and bug fixes.
251
+ ### Changes from the original:
252
+
253
+ - Removed alternative SSO methods to focus solely on OIDC.
254
+ - Redesigned the Whitelist and Role management UI using native Strapi components.
255
+ - Added OIDC enforcement with an admin toggle and config override (`OIDC_ENFORCE`).
256
+ - Added RP-initiated logout with smart session detection skips the provider redirect if the session is already expired.
257
+ - Migrated to Vitest with comprehensive e2e test coverage.
258
+ - Config variable names aligned with OIDC discovery document field names (`OIDC_SCOPE`, `OIDC_USERINFO_ENDPOINT`, `OIDC_END_SESSION_ENDPOINT`).
259
+ - Always injects a **Login via SSO** button on the Strapi login page. Button text is configurable via `OIDC_SSO_BUTTON_TEXT`.
260
+ - Whitelist: programmatic REST API with JSON import/export, bulk delete, delete by email, and unsaved changes guard.
261
+ - Hardened OIDC flow: server-generated state and nonce, PKCE, Bearer token auth for userinfo, and generic error messages on failure.
262
+ - Audit log: records all auth events to a queryable table with Admin UI, JSON export, and REST API. API responses use a single `datetime` field and omit framework metadata (id, documentId, locale, publishedAt, etc.). A separate `user_created` event is emitted when a Strapi admin is provisioned during login.
263
+
264
+ ## License
265
+
266
+ [MIT](./LICENSE)