strapi-plugin-oidc 1.5.3 → 1.6.1

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,23 +130,20 @@ 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.
88
140
 
89
141
  ### Import format
90
142
 
91
- Accepted by both the API import endpoint and the Admin UI import button. `roles` is optional and accepts role **names** (recommended) or numeric IDs. If the email already exists as a Strapi admin user, their current roles are used automatically.
143
+ 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.
92
144
 
93
145
  ```json
94
- [
95
- { "email": "alice@example.com", "roles": ["Editor"] },
96
- { "email": "bob@example.com", "roles": ["Editor", "Author"] },
97
- { "email": "carol@example.com" }
98
- ]
146
+ [{ "email": "alice@example.com" }, { "email": "bob@example.com" }]
99
147
  ```
100
148
 
101
149
  Duplicate emails within the payload and emails already in the whitelist are silently skipped.
@@ -107,43 +155,108 @@ Duplicate emails within the payload and emails already in the whitelist are sile
107
155
  curl -H "Authorization: Bearer <token>" \
108
156
  http://localhost:1337/api/strapi-plugin-oidc/whitelist
109
157
 
158
+ # Export
159
+ curl -H "Authorization: Bearer <token>" \
160
+ http://localhost:1337/api/strapi-plugin-oidc/whitelist/export \
161
+ -o whitelist.json
162
+
110
163
  # Add
111
164
  curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
112
- -d '{"email": "user@example.com", "roles": ["Editor"]}' \
165
+ -d '{"email": "user@example.com"}' \
113
166
  http://localhost:1337/api/strapi-plugin-oidc/whitelist
114
167
 
115
168
  # Bulk import
116
169
  curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
117
- -d '{"users": [{"email": "a@example.com", "roles": ["Editor"]}, {"email": "b@example.com"}]}' \
170
+ -d '{"users": [{"email": "a@example.com"}, {"email": "b@example.com"}]}' \
118
171
  http://localhost:1337/api/strapi-plugin-oidc/whitelist/import
119
172
 
120
- # Delete one
173
+ # Delete one (by email)
121
174
  curl -X DELETE -H "Authorization: Bearer <token>" \
122
- http://localhost:1337/api/strapi-plugin-oidc/whitelist/42
175
+ "http://localhost:1337/api/strapi-plugin-oidc/whitelist/user%40example.com"
123
176
 
124
177
  # Delete all
125
178
  curl -X DELETE -H "Authorization: Bearer <token>" \
126
179
  http://localhost:1337/api/strapi-plugin-oidc/whitelist
127
180
  ```
128
181
 
182
+ ## Audit Log API
183
+
184
+ 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>`.
185
+
186
+ | Method | Path | Description |
187
+ | ------ | ------------------------------------------- | ----------------------------- |
188
+ | `GET` | `/api/strapi-plugin-oidc/audit-logs` | Paginated list of log entries |
189
+ | `GET` | `/api/strapi-plugin-oidc/audit-logs/export` | All records as JSON download |
190
+
191
+ ### Query parameters (`GET /audit-logs`)
192
+
193
+ | Parameter | Default | Description |
194
+ | ---------- | ------- | ---------------- |
195
+ | `page` | `1` | Page number |
196
+ | `pageSize` | `25` | Results per page |
197
+
198
+ Results are sorted newest-first. The response shape is:
199
+
200
+ ```json
201
+ {
202
+ "results": [
203
+ {
204
+ "datetime": "2026-04-08T12:00:00.000Z",
205
+ "action": "login_success",
206
+ "email": "alice@example.com",
207
+ "ip": "203.0.113.42"
208
+ }
209
+ ],
210
+ "pagination": { "page": 1, "pageSize": 25, "total": 1, "pageCount": 1 }
211
+ }
212
+ ```
213
+
214
+ ### Recorded actions
215
+
216
+ | Action | Trigger |
217
+ | ----------------------- | --------------------------------------------------- |
218
+ | `login_success` | Successful OIDC authentication |
219
+ | `user_created` | New Strapi admin user created during login |
220
+ | `login_failure` | Generic authentication error (missing code, etc.) |
221
+ | `state_mismatch` | CSRF state cookie does not match callback parameter |
222
+ | `nonce_mismatch` | ID token nonce does not match the session nonce |
223
+ | `token_exchange_failed` | Provider returned an error during token exchange |
224
+ | `whitelist_rejected` | Email not present in the active whitelist |
225
+ | `logout` | User logged out via `/logout` |
226
+ | `session_expired` | Logout attempted but provider session already stale |
227
+
228
+ Each event is also emitted on Strapi's internal eventHub as `strapi-plugin-oidc::auth.<action>`, which Enterprise audit log listeners pick up automatically.
229
+
230
+ ### Examples
231
+
232
+ ```bash
233
+ # Paginated list
234
+ curl -H "Authorization: Bearer <token>" \
235
+ "http://localhost:1337/api/strapi-plugin-oidc/audit-logs?page=1&pageSize=50"
236
+
237
+ # JSON export
238
+ curl -H "Authorization: Bearer <token>" \
239
+ http://localhost:1337/api/strapi-plugin-oidc/audit-logs/export \
240
+ -o oidc-audit-log.json
241
+ ```
242
+
129
243
  ## Credits & Changes
130
244
 
131
245
  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
246
 
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.
247
+ ### Changes from the original:
248
+
249
+ - Removed alternative SSO methods to focus solely on OIDC.
250
+ - Redesigned the Whitelist and Role management UI using native Strapi components.
251
+ - Added OIDC enforcement with an admin toggle and config override (`OIDC_ENFORCE`).
252
+ - Added RP-initiated logout with smart session detection skips the provider redirect if the session is already expired.
253
+ - Migrated to Vitest with comprehensive e2e test coverage.
254
+ - Config variable names aligned with OIDC discovery document field names (`OIDC_SCOPE`, `OIDC_USERINFO_ENDPOINT`, `OIDC_END_SESSION_ENDPOINT`).
255
+ - Always injects a **Login via SSO** button on the Strapi login page. Button text is configurable via `OIDC_SSO_BUTTON_TEXT`.
256
+ - Whitelist: programmatic REST API with JSON import/export, bulk delete, delete by email, and unsaved changes guard.
257
+ - Hardened OIDC flow: server-generated state and nonce, PKCE, Bearer token auth for userinfo, and generic error messages on failure.
258
+ - 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.
259
+
260
+ ## License
261
+
262
+ [MIT](./LICENSE)