strapi-plugin-oidc 1.10.3 → 1.10.5
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 +12 -21
- package/dist/server/index.js +15 -6
- package/dist/server/index.mjs +15 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,10 +35,10 @@ module.exports = ({ env }) => ({
|
|
|
35
35
|
enabled: true,
|
|
36
36
|
config: {
|
|
37
37
|
// Required
|
|
38
|
+
OIDC_PUBLIC_URL: env('PUBLIC_URL', 'https://strapi.example.com'), // origin only — we append /strapi-plugin-oidc/oidc/callback
|
|
38
39
|
OIDC_ISSUER: env('OIDC_ISSUER'), // https://your-provider or https://your-provider/realms/your-realm
|
|
39
40
|
OIDC_CLIENT_ID: env('OIDC_CLIENT_ID'),
|
|
40
41
|
OIDC_CLIENT_SECRET: env('OIDC_CLIENT_SECRET'),
|
|
41
|
-
OIDC_REDIRECT_URI: env('OIDC_REDIRECT_URI'), // https://your-strapi.com/strapi-plugin-oidc/oidc/callback
|
|
42
42
|
|
|
43
43
|
// Optional — defaults shown
|
|
44
44
|
OIDC_SCOPE: 'openid profile email', // space-separated scopes
|
|
@@ -59,6 +59,8 @@ module.exports = ({ env }) => ({
|
|
|
59
59
|
});
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
`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.
|
|
63
|
+
|
|
62
64
|
`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.
|
|
63
65
|
|
|
64
66
|
### Security features
|
|
@@ -98,17 +100,6 @@ Only headers that CDN/proxy vendors guarantee to strip from inbound client reque
|
|
|
98
100
|
|
|
99
101
|
Navigate to `/strapi-plugin-oidc/oidc` to start the OIDC flow, or click the **Login via SSO** button injected into the Strapi login page.
|
|
100
102
|
|
|
101
|
-
## Skip Login Page
|
|
102
|
-
|
|
103
|
-
Set `OIDC_SKIP_LOGIN_PAGE: true` to prevent users from ever seeing the Strapi admin login page. Unauthenticated requests are redirected directly to the OIDC provider. This is typically used alongside `OIDC_ENFORCE: true`.
|
|
104
|
-
|
|
105
|
-
Two independent mechanisms cover both entry points:
|
|
106
|
-
|
|
107
|
-
- **Server-side** — A Koa middleware intercepts unauthenticated GET requests to `/admin` and redirects to `/strapi-plugin-oidc/oidc` before the SPA is served. Excluded from this redirect: API routes (`/admin/login`, `/admin/logout`, `/admin/init`, etc.), static assets (`.js`, `.css`, `.png`, etc.), and POST requests.
|
|
108
|
-
- **Client-side** — A DOM `MutationObserver` watches for React Router navigations to `/auth/login` (triggered by session expiry or 401 responses) and redirects before the login form renders.
|
|
109
|
-
|
|
110
|
-
After logout without a provider `end_session_endpoint`, the fallback URL becomes `/strapi-plugin-oidc/oidc` instead of `/admin/auth/login`, ensuring the user lands on the provider's own page.
|
|
111
|
-
|
|
112
103
|
## Logout
|
|
113
104
|
|
|
114
105
|
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.
|
|
@@ -212,30 +203,30 @@ Duplicate emails within the payload and emails already in the whitelist are sile
|
|
|
212
203
|
```bash
|
|
213
204
|
# List
|
|
214
205
|
curl -H "Authorization: Bearer <token>" \
|
|
215
|
-
|
|
206
|
+
https://strapi.example.com/api/strapi-plugin-oidc/whitelist
|
|
216
207
|
|
|
217
208
|
# Export
|
|
218
209
|
curl -H "Authorization: Bearer <token>" \
|
|
219
|
-
|
|
210
|
+
https://strapi.example.com/api/strapi-plugin-oidc/whitelist/export \
|
|
220
211
|
-o whitelist.json
|
|
221
212
|
|
|
222
213
|
# Add
|
|
223
214
|
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
|
|
224
215
|
-d '{"email": "user@example.com"}' \
|
|
225
|
-
|
|
216
|
+
https://strapi.example.com/api/strapi-plugin-oidc/whitelist
|
|
226
217
|
|
|
227
218
|
# Bulk import
|
|
228
219
|
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
|
|
229
220
|
-d '{"users": [{"email": "a@example.com"}, {"email": "b@example.com"}]}' \
|
|
230
|
-
|
|
221
|
+
https://strapi.example.com/api/strapi-plugin-oidc/whitelist/import
|
|
231
222
|
|
|
232
223
|
# Delete one (by email)
|
|
233
224
|
curl -X DELETE -H "Authorization: Bearer <token>" \
|
|
234
|
-
"
|
|
225
|
+
"https://strapi.example.com/api/strapi-plugin-oidc/whitelist/user%40example.com"
|
|
235
226
|
|
|
236
227
|
# Delete all
|
|
237
228
|
curl -X DELETE -H "Authorization: Bearer <token>" \
|
|
238
|
-
|
|
229
|
+
https://strapi.example.com/api/strapi-plugin-oidc/whitelist
|
|
239
230
|
```
|
|
240
231
|
|
|
241
232
|
## Audit Log API
|
|
@@ -303,7 +294,7 @@ curl -H "Authorization: Bearer <token>" -G \
|
|
|
303
294
|
--data-urlencode 'filters[action][$eq]=login_failure' \
|
|
304
295
|
--data-urlencode 'filters[createdAt][$gte]=2026-04-08T00:00:00.000Z' \
|
|
305
296
|
--data-urlencode 'filters[createdAt][$lt]=2026-04-09T00:00:00.000Z' \
|
|
306
|
-
|
|
297
|
+
https://strapi.example.com/api/strapi-plugin-oidc/audit-logs
|
|
307
298
|
```
|
|
308
299
|
|
|
309
300
|
### Recorded actions
|
|
@@ -330,11 +321,11 @@ Each event is also emitted on Strapi's internal eventHub as `strapi-plugin-oidc:
|
|
|
330
321
|
```bash
|
|
331
322
|
# Paginated list
|
|
332
323
|
curl -H "Authorization: Bearer <token>" \
|
|
333
|
-
"
|
|
324
|
+
"https://strapi.example.com/api/strapi-plugin-oidc/audit-logs?page=1&pageSize=50"
|
|
334
325
|
|
|
335
326
|
# NDJSON export
|
|
336
327
|
curl -H "Authorization: Bearer <token>" \
|
|
337
|
-
|
|
328
|
+
https://strapi.example.com/api/strapi-plugin-oidc/audit-logs/export \
|
|
338
329
|
-o oidc-audit-log.ndjson
|
|
339
330
|
```
|
|
340
331
|
|
package/dist/server/index.js
CHANGED
|
@@ -103,7 +103,7 @@ const coerceBoolNullable = zod.z.preprocess(
|
|
|
103
103
|
);
|
|
104
104
|
const pluginConfigSchema = zod.z.object({
|
|
105
105
|
REMEMBER_ME: coerceBool(false),
|
|
106
|
-
|
|
106
|
+
OIDC_PUBLIC_URL: zod.z.string().default(""),
|
|
107
107
|
OIDC_CLIENT_ID: zod.z.string().default(""),
|
|
108
108
|
OIDC_CLIENT_SECRET: zod.z.string().default(""),
|
|
109
109
|
OIDC_SCOPE: zod.z.string().default("openid profile email"),
|
|
@@ -175,6 +175,7 @@ const DAY_MS = 864e5;
|
|
|
175
175
|
const DISCOVERY_TIMEOUT_MS = 5e3;
|
|
176
176
|
const OIDC_DISCOVERY_PATH = "/.well-known/openid-configuration";
|
|
177
177
|
const OIDC_SIGN_IN_PATH = "/strapi-plugin-oidc/oidc";
|
|
178
|
+
const OIDC_CALLBACK_PATH = "/strapi-plugin-oidc/oidc/callback";
|
|
178
179
|
const AUTH_ROUTES = [
|
|
179
180
|
"login",
|
|
180
181
|
"register",
|
|
@@ -422,7 +423,6 @@ function destroy() {
|
|
|
422
423
|
const config = {
|
|
423
424
|
default: {
|
|
424
425
|
REMEMBER_ME: false,
|
|
425
|
-
OIDC_REDIRECT_URI: "http://localhost:1337/strapi-plugin-oidc/oidc/callback",
|
|
426
426
|
OIDC_CLIENT_ID: "",
|
|
427
427
|
OIDC_CLIENT_SECRET: "",
|
|
428
428
|
OIDC_SCOPE: "openid profile email",
|
|
@@ -562,7 +562,6 @@ const REQUIRED_CONFIG_KEYS = [
|
|
|
562
562
|
"OIDC_ISSUER",
|
|
563
563
|
"OIDC_CLIENT_ID",
|
|
564
564
|
"OIDC_CLIENT_SECRET",
|
|
565
|
-
"OIDC_REDIRECT_URI",
|
|
566
565
|
"OIDC_SCOPE",
|
|
567
566
|
"OIDC_FAMILY_NAME_FIELD",
|
|
568
567
|
"OIDC_GIVEN_NAME_FIELD",
|
|
@@ -571,6 +570,15 @@ const REQUIRED_CONFIG_KEYS = [
|
|
|
571
570
|
"OIDC_USERINFO_ENDPOINT",
|
|
572
571
|
"OIDC_AUTHORIZATION_ENDPOINT"
|
|
573
572
|
];
|
|
573
|
+
function resolveRedirectUri(config2) {
|
|
574
|
+
const publicUrl = config2.OIDC_PUBLIC_URL || process.env.PUBLIC_URL || (process.env.NODE_ENV !== "production" ? "http://localhost:1337" : "");
|
|
575
|
+
if (!publicUrl) {
|
|
576
|
+
throw new Error(
|
|
577
|
+
"OIDC_PUBLIC_URL or PUBLIC_URL must be set in production. Provide your Strapi origin (e.g. https://myapp.com)."
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
return `${publicUrl.replace(/\/+$/, "")}${OIDC_CALLBACK_PATH}`;
|
|
581
|
+
}
|
|
574
582
|
const jwksCache = /* @__PURE__ */ new Map();
|
|
575
583
|
let jwksDisabledWarned = false;
|
|
576
584
|
function getJwks(uri) {
|
|
@@ -3621,7 +3629,7 @@ async function oidcSignIn(ctx) {
|
|
|
3621
3629
|
ctx.redirect(`${adminUrl}/auth/login?oidc_redirect=1`);
|
|
3622
3630
|
return;
|
|
3623
3631
|
}
|
|
3624
|
-
const { OIDC_CLIENT_ID,
|
|
3632
|
+
const { OIDC_CLIENT_ID, OIDC_SCOPE, OIDC_AUTHORIZATION_ENDPOINT } = config2;
|
|
3625
3633
|
const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge__default.default();
|
|
3626
3634
|
const state = node_crypto.randomBytes(32).toString("base64url");
|
|
3627
3635
|
const nonce = node_crypto.randomBytes(32).toString("base64url");
|
|
@@ -3634,10 +3642,11 @@ async function oidcSignIn(ctx) {
|
|
|
3634
3642
|
ctx.cookies.set(COOKIE_NAMES.codeVerifier, codeVerifier, cookieOptions);
|
|
3635
3643
|
ctx.cookies.set(COOKIE_NAMES.state, state, cookieOptions);
|
|
3636
3644
|
ctx.cookies.set(COOKIE_NAMES.nonce, nonce, cookieOptions);
|
|
3645
|
+
const redirectUri = resolveRedirectUri(config2);
|
|
3637
3646
|
const params = new URLSearchParams({
|
|
3638
3647
|
response_type: "code",
|
|
3639
3648
|
client_id: OIDC_CLIENT_ID,
|
|
3640
|
-
redirect_uri:
|
|
3649
|
+
redirect_uri: redirectUri,
|
|
3641
3650
|
scope: OIDC_SCOPE,
|
|
3642
3651
|
code_challenge: codeChallenge,
|
|
3643
3652
|
code_challenge_method: "S256",
|
|
@@ -3982,7 +3991,7 @@ async function oidcSignInCallback(ctx) {
|
|
|
3982
3991
|
code: String(ctx.query.code),
|
|
3983
3992
|
client_id: config2.OIDC_CLIENT_ID,
|
|
3984
3993
|
client_secret: config2.OIDC_CLIENT_SECRET,
|
|
3985
|
-
redirect_uri: config2
|
|
3994
|
+
redirect_uri: resolveRedirectUri(config2),
|
|
3986
3995
|
grant_type: "authorization_code",
|
|
3987
3996
|
code_verifier: codeVerifier ?? ""
|
|
3988
3997
|
});
|
package/dist/server/index.mjs
CHANGED
|
@@ -97,7 +97,7 @@ const coerceBoolNullable = z.preprocess(
|
|
|
97
97
|
);
|
|
98
98
|
const pluginConfigSchema = z.object({
|
|
99
99
|
REMEMBER_ME: coerceBool(false),
|
|
100
|
-
|
|
100
|
+
OIDC_PUBLIC_URL: z.string().default(""),
|
|
101
101
|
OIDC_CLIENT_ID: z.string().default(""),
|
|
102
102
|
OIDC_CLIENT_SECRET: z.string().default(""),
|
|
103
103
|
OIDC_SCOPE: z.string().default("openid profile email"),
|
|
@@ -169,6 +169,7 @@ const DAY_MS = 864e5;
|
|
|
169
169
|
const DISCOVERY_TIMEOUT_MS = 5e3;
|
|
170
170
|
const OIDC_DISCOVERY_PATH = "/.well-known/openid-configuration";
|
|
171
171
|
const OIDC_SIGN_IN_PATH = "/strapi-plugin-oidc/oidc";
|
|
172
|
+
const OIDC_CALLBACK_PATH = "/strapi-plugin-oidc/oidc/callback";
|
|
172
173
|
const AUTH_ROUTES = [
|
|
173
174
|
"login",
|
|
174
175
|
"register",
|
|
@@ -416,7 +417,6 @@ function destroy() {
|
|
|
416
417
|
const config = {
|
|
417
418
|
default: {
|
|
418
419
|
REMEMBER_ME: false,
|
|
419
|
-
OIDC_REDIRECT_URI: "http://localhost:1337/strapi-plugin-oidc/oidc/callback",
|
|
420
420
|
OIDC_CLIENT_ID: "",
|
|
421
421
|
OIDC_CLIENT_SECRET: "",
|
|
422
422
|
OIDC_SCOPE: "openid profile email",
|
|
@@ -556,7 +556,6 @@ const REQUIRED_CONFIG_KEYS = [
|
|
|
556
556
|
"OIDC_ISSUER",
|
|
557
557
|
"OIDC_CLIENT_ID",
|
|
558
558
|
"OIDC_CLIENT_SECRET",
|
|
559
|
-
"OIDC_REDIRECT_URI",
|
|
560
559
|
"OIDC_SCOPE",
|
|
561
560
|
"OIDC_FAMILY_NAME_FIELD",
|
|
562
561
|
"OIDC_GIVEN_NAME_FIELD",
|
|
@@ -565,6 +564,15 @@ const REQUIRED_CONFIG_KEYS = [
|
|
|
565
564
|
"OIDC_USERINFO_ENDPOINT",
|
|
566
565
|
"OIDC_AUTHORIZATION_ENDPOINT"
|
|
567
566
|
];
|
|
567
|
+
function resolveRedirectUri(config2) {
|
|
568
|
+
const publicUrl = config2.OIDC_PUBLIC_URL || process.env.PUBLIC_URL || (process.env.NODE_ENV !== "production" ? "http://localhost:1337" : "");
|
|
569
|
+
if (!publicUrl) {
|
|
570
|
+
throw new Error(
|
|
571
|
+
"OIDC_PUBLIC_URL or PUBLIC_URL must be set in production. Provide your Strapi origin (e.g. https://myapp.com)."
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
return `${publicUrl.replace(/\/+$/, "")}${OIDC_CALLBACK_PATH}`;
|
|
575
|
+
}
|
|
568
576
|
const jwksCache = /* @__PURE__ */ new Map();
|
|
569
577
|
let jwksDisabledWarned = false;
|
|
570
578
|
function getJwks(uri) {
|
|
@@ -3615,7 +3623,7 @@ async function oidcSignIn(ctx) {
|
|
|
3615
3623
|
ctx.redirect(`${adminUrl}/auth/login?oidc_redirect=1`);
|
|
3616
3624
|
return;
|
|
3617
3625
|
}
|
|
3618
|
-
const { OIDC_CLIENT_ID,
|
|
3626
|
+
const { OIDC_CLIENT_ID, OIDC_SCOPE, OIDC_AUTHORIZATION_ENDPOINT } = config2;
|
|
3619
3627
|
const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge();
|
|
3620
3628
|
const state = randomBytes(32).toString("base64url");
|
|
3621
3629
|
const nonce = randomBytes(32).toString("base64url");
|
|
@@ -3628,10 +3636,11 @@ async function oidcSignIn(ctx) {
|
|
|
3628
3636
|
ctx.cookies.set(COOKIE_NAMES.codeVerifier, codeVerifier, cookieOptions);
|
|
3629
3637
|
ctx.cookies.set(COOKIE_NAMES.state, state, cookieOptions);
|
|
3630
3638
|
ctx.cookies.set(COOKIE_NAMES.nonce, nonce, cookieOptions);
|
|
3639
|
+
const redirectUri = resolveRedirectUri(config2);
|
|
3631
3640
|
const params = new URLSearchParams({
|
|
3632
3641
|
response_type: "code",
|
|
3633
3642
|
client_id: OIDC_CLIENT_ID,
|
|
3634
|
-
redirect_uri:
|
|
3643
|
+
redirect_uri: redirectUri,
|
|
3635
3644
|
scope: OIDC_SCOPE,
|
|
3636
3645
|
code_challenge: codeChallenge,
|
|
3637
3646
|
code_challenge_method: "S256",
|
|
@@ -3976,7 +3985,7 @@ async function oidcSignInCallback(ctx) {
|
|
|
3976
3985
|
code: String(ctx.query.code),
|
|
3977
3986
|
client_id: config2.OIDC_CLIENT_ID,
|
|
3978
3987
|
client_secret: config2.OIDC_CLIENT_SECRET,
|
|
3979
|
-
redirect_uri: config2
|
|
3988
|
+
redirect_uri: resolveRedirectUri(config2),
|
|
3980
3989
|
grant_type: "authorization_code",
|
|
3981
3990
|
code_verifier: codeVerifier ?? ""
|
|
3982
3991
|
});
|
package/package.json
CHANGED