ep_openid_connect 3.0.29 → 3.0.31
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 +8 -0
- package/index.js +53 -7
- package/package.json +4 -3
- package/static/tests/backend/specs/custom_ca.js +243 -0
package/README.md
CHANGED
|
@@ -60,6 +60,14 @@ Etherpad's `requireAuthentication` setting must be `true`.
|
|
|
60
60
|
* `base_url` (required): The public base Etherpad URL. When registering Etherpad
|
|
61
61
|
with your identity provider, the redirect URL (a.k.a. callback URL) is this
|
|
62
62
|
base URL plus `/ep_openid_connect/callback`.
|
|
63
|
+
* `ca` (optional): Custom Certificate Authority bundle for verifying TLS
|
|
64
|
+
connections to the identity provider — useful when the provider's
|
|
65
|
+
certificate is signed by a private or internal CA rather than a public
|
|
66
|
+
one. The value can be either a filesystem path to a PEM file or the PEM
|
|
67
|
+
content itself (recognised by a leading `-----BEGIN`). If you control the
|
|
68
|
+
Node startup environment you may instead set the
|
|
69
|
+
[`NODE_EXTRA_CA_CERTS`](https://nodejs.org/api/cli.html#node_extra_ca_certsfile)
|
|
70
|
+
environment variable, which has the same effect for the whole process.
|
|
63
71
|
* `scope` (optional; defaults to `["openid"]`): List of OAuth2 scope strings.
|
|
64
72
|
* `prohibited_usernames` (optional; defaults to `["admin", "guest"]`): List of
|
|
65
73
|
strings that will trigger an authentication error if any match the `sub`
|
package/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const Ajv = require('ajv/dist/jtd');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const undici = require('undici');
|
|
4
6
|
const {URL} = require('url');
|
|
5
7
|
|
|
6
8
|
let logger = {};
|
|
@@ -29,6 +31,14 @@ const validSettings = new Ajv().compile({
|
|
|
29
31
|
client_secret: {type: 'string'},
|
|
30
32
|
},
|
|
31
33
|
optionalProperties: {
|
|
34
|
+
// Path to a PEM-encoded certificate authority bundle (or the PEM
|
|
35
|
+
// content itself, recognised by a leading `-----BEGIN`). Used to verify
|
|
36
|
+
// TLS connections to identity providers that present a certificate
|
|
37
|
+
// signed by a private/internal CA. Operators who control the Node
|
|
38
|
+
// process can equivalently set NODE_EXTRA_CA_CERTS — this option is for
|
|
39
|
+
// settings.json-driven deployments where touching the Node startup
|
|
40
|
+
// command isn't an option.
|
|
41
|
+
ca: {type: 'string'},
|
|
32
42
|
issuer: {type: 'string'},
|
|
33
43
|
issuer_metadata: {},
|
|
34
44
|
prohibited_usernames: {elements: {type: 'string'}},
|
|
@@ -82,6 +92,24 @@ const isHttp = (urlString) => {
|
|
|
82
92
|
}
|
|
83
93
|
};
|
|
84
94
|
|
|
95
|
+
// Load a CA bundle from either a path or an inline PEM string. Returns the
|
|
96
|
+
// PEM content as a string, or `null` for an empty/missing setting.
|
|
97
|
+
// Exported for unit testing.
|
|
98
|
+
const loadCaBundle = (caSetting) => {
|
|
99
|
+
if (typeof caSetting !== 'string' || !caSetting) return null;
|
|
100
|
+
if (caSetting.startsWith('-----BEGIN ')) return caSetting;
|
|
101
|
+
return fs.readFileSync(caSetting, 'utf8');
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Build a `fetch` implementation that trusts the given PEM-encoded CA
|
|
105
|
+
// bundle, suitable for assigning to `config[oidc.customFetch]`. Each call
|
|
106
|
+
// creates a single shared undici dispatcher so HTTP connections to the IdP
|
|
107
|
+
// can be reused across discovery, token-exchange, and userinfo calls.
|
|
108
|
+
const buildCustomFetch = (caBundle) => {
|
|
109
|
+
const dispatcher = new undici.Agent({connect: {ca: caBundle}});
|
|
110
|
+
return (url, options) => undici.fetch(url, {...options, dispatcher});
|
|
111
|
+
};
|
|
112
|
+
|
|
85
113
|
// Pick the token endpoint auth method to use, given the IdP's advertised
|
|
86
114
|
// `token_endpoint_auth_methods_supported` and any explicit override from
|
|
87
115
|
// settings. Pure; exported for unit testing.
|
|
@@ -116,7 +144,7 @@ const clientAuthFor = (method, secret) => {
|
|
|
116
144
|
}
|
|
117
145
|
};
|
|
118
146
|
|
|
119
|
-
const fetchServerMetadata = async (issuerUrl, clientId) => {
|
|
147
|
+
const fetchServerMetadata = async (issuerUrl, clientId, customFetch) => {
|
|
120
148
|
const url = new URL(issuerUrl);
|
|
121
149
|
// https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery says that the URI
|
|
122
150
|
// must not have query or fragment components.
|
|
@@ -128,16 +156,22 @@ const fetchServerMetadata = async (issuerUrl, clientId) => {
|
|
|
128
156
|
}
|
|
129
157
|
// openid-client@6 rejects http:// issuers by default; opt back in for
|
|
130
158
|
// localhost / private-network providers (matches v5 behaviour).
|
|
131
|
-
const options =
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
const options = {};
|
|
160
|
+
if (url.protocol === 'http:') options.execute = [oidc.allowInsecureRequests];
|
|
161
|
+
// `customFetch` must be set on the options bag (not on `execute`) so that
|
|
162
|
+
// discovery's OWN HTTPS request to /.well-known/openid-configuration uses
|
|
163
|
+
// it. `execute` callbacks only run AFTER discovery finishes.
|
|
164
|
+
if (customFetch != null) options[oidc.customFetch] = customFetch;
|
|
134
165
|
try {
|
|
135
166
|
// Discovery fetches the .well-known/openid-configuration document. The
|
|
136
167
|
// clientAuthentication argument is irrelevant for that HTTP request — it
|
|
137
168
|
// only matters when the Configuration is later used to exchange a code
|
|
138
169
|
// for a token — so we can pass `undefined` here and bind the real method
|
|
139
170
|
// below once we know what the IdP supports.
|
|
140
|
-
const tempConfig = await oidc.discovery(
|
|
171
|
+
const tempConfig = await oidc.discovery(
|
|
172
|
+
url, clientId, undefined, undefined,
|
|
173
|
+
Object.keys(options).length || Object.getOwnPropertySymbols(options).length
|
|
174
|
+
? options : undefined);
|
|
141
175
|
return tempConfig.serverMetadata();
|
|
142
176
|
} catch (err) {
|
|
143
177
|
// The URL used to get the issuer metadata doesn't exactly follow RFC 8615; see:
|
|
@@ -156,13 +190,22 @@ const fetchServerMetadata = async (issuerUrl, clientId) => {
|
|
|
156
190
|
};
|
|
157
191
|
|
|
158
192
|
const buildConfig = async (settings) => {
|
|
193
|
+
// If the operator gave us a custom CA bundle, build a fetch that trusts
|
|
194
|
+
// it; otherwise leave the default fetch in place. We deliberately build
|
|
195
|
+
// the customFetch BEFORE discovery so the discovery HTTP call itself
|
|
196
|
+
// uses the trusted CA — the configured IdP is typically the same host
|
|
197
|
+
// that serves the well-known document.
|
|
198
|
+
const caBundle = loadCaBundle(settings.ca);
|
|
199
|
+
const customFetch = caBundle ? buildCustomFetch(caBundle) : null;
|
|
200
|
+
|
|
159
201
|
// Resolve the server metadata (either via discovery or from the inline
|
|
160
202
|
// `issuer_metadata` blob) BEFORE choosing an auth method, so we can pick
|
|
161
203
|
// one the IdP actually advertises.
|
|
162
204
|
let serverMetadata;
|
|
163
205
|
let phaseLog;
|
|
164
206
|
if (settings.issuer) {
|
|
165
|
-
serverMetadata =
|
|
207
|
+
serverMetadata =
|
|
208
|
+
await fetchServerMetadata(settings.issuer, settings.client_id, customFetch);
|
|
166
209
|
phaseLog = 'OpenID Connect Discovery complete.';
|
|
167
210
|
} else {
|
|
168
211
|
serverMetadata = settings.issuer_metadata;
|
|
@@ -177,8 +220,10 @@ const buildConfig = async (settings) => {
|
|
|
177
220
|
if (isHttp(serverMetadata && serverMetadata.issuer)) {
|
|
178
221
|
oidc.allowInsecureRequests(config);
|
|
179
222
|
}
|
|
223
|
+
if (customFetch != null) config[oidc.customFetch] = customFetch;
|
|
180
224
|
const source = settings.token_endpoint_auth_method ? 'configured' : 'auto-picked';
|
|
181
|
-
logger.info(`${phaseLog} Token endpoint auth method: ${method} (${source})
|
|
225
|
+
logger.info(`${phaseLog} Token endpoint auth method: ${method} (${source})` +
|
|
226
|
+
(caBundle ? ' with custom CA bundle.' : '.'));
|
|
182
227
|
return config;
|
|
183
228
|
};
|
|
184
229
|
|
|
@@ -378,6 +423,7 @@ exports.preAuthorize = (hookName, {req}) => {
|
|
|
378
423
|
exports.exportedForTestingOnly = {
|
|
379
424
|
callbackUrlFromRequest,
|
|
380
425
|
defaultSettings,
|
|
426
|
+
loadCaBundle,
|
|
381
427
|
pickAuthMethod,
|
|
382
428
|
validSettings,
|
|
383
429
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ep_openid_connect",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.31",
|
|
4
4
|
"description": "Etherpad plugin to authenticate users against an OpenID Connect provider.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -24,12 +24,13 @@
|
|
|
24
24
|
"homepage": "https://github.com/ether/ep_openid_connect#readme",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"ajv": "^8.20.0",
|
|
27
|
-
"openid-client": "^6.8.4"
|
|
27
|
+
"openid-client": "^6.8.4",
|
|
28
|
+
"undici": "^8.3.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"eslint": "^8.57.1",
|
|
31
32
|
"eslint-config-etherpad": "^4.0.5",
|
|
32
|
-
"oidc-provider": "^9.8.
|
|
33
|
+
"oidc-provider": "^9.8.4",
|
|
33
34
|
"supertest": "^6.3.4",
|
|
34
35
|
"typescript": "^6.0.3"
|
|
35
36
|
},
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// End-to-end coverage for the `ca` setting: stand up an HTTPS-fronted OIDC
|
|
4
|
+
// provider with a self-signed certificate, point the plugin at it, and
|
|
5
|
+
// confirm a real login completes when the matching CA is configured (and
|
|
6
|
+
// fails when it isn't). This is the only sound way to prove that the
|
|
7
|
+
// custom-fetch wiring actually replaces Node's default fetch — a unit test
|
|
8
|
+
// that just checks `config[customFetch]` is set would miss the case where
|
|
9
|
+
// undici and openid-client disagree on the option shape.
|
|
10
|
+
|
|
11
|
+
const Provider = require('oidc-provider').default;
|
|
12
|
+
const MemoryAdapter = require('oidc-provider/lib/adapters/memory_adapter').default;
|
|
13
|
+
const assert = require('assert').strict;
|
|
14
|
+
const child_process = require('child_process');
|
|
15
|
+
const common = require('ep_etherpad-lite/tests/backend/common');
|
|
16
|
+
const epOpenidConnect = require('../../../../index');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const https = require('https');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
|
22
|
+
const util = require('util');
|
|
23
|
+
|
|
24
|
+
// Generate a self-signed cert valid for localhost via the OpenSSL CLI.
|
|
25
|
+
// Using a CLI avoids pulling in `selfsigned`+`@peculiar/x509`+`node-forge`
|
|
26
|
+
// as devDependencies just for one test, and `openssl` is available on every
|
|
27
|
+
// CI runner and dev machine.
|
|
28
|
+
const generateSelfSignedCert = () => {
|
|
29
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ep-openid-cert-'));
|
|
30
|
+
const keyPath = path.join(dir, 'key.pem');
|
|
31
|
+
const certPath = path.join(dir, 'cert.pem');
|
|
32
|
+
child_process.execFileSync('openssl', [
|
|
33
|
+
'req', '-x509',
|
|
34
|
+
'-newkey', 'rsa:2048',
|
|
35
|
+
'-nodes',
|
|
36
|
+
'-keyout', keyPath,
|
|
37
|
+
'-out', certPath,
|
|
38
|
+
'-days', '1',
|
|
39
|
+
'-subj', '/CN=localhost',
|
|
40
|
+
'-addext', 'subjectAltName=DNS:localhost,IP:127.0.0.1',
|
|
41
|
+
], {stdio: 'pipe'});
|
|
42
|
+
return {
|
|
43
|
+
dir,
|
|
44
|
+
key: fs.readFileSync(keyPath, 'utf8'),
|
|
45
|
+
cert: fs.readFileSync(certPath, 'utf8'),
|
|
46
|
+
keyPath,
|
|
47
|
+
certPath,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const httpsListen = util.promisify(https.Server.prototype.listen);
|
|
52
|
+
const httpsClose = util.promisify(https.Server.prototype.close);
|
|
53
|
+
|
|
54
|
+
// No-op subclass mirrors the helper in ../oidc-provider.js — silences the
|
|
55
|
+
// MemoryAdapter "not for production" warning.
|
|
56
|
+
class Adapter extends MemoryAdapter {}
|
|
57
|
+
|
|
58
|
+
// Minimal HTTPS-fronted OIDC provider for the CA-trust test. Mirrors the
|
|
59
|
+
// happy path of static/tests/backend/oidc-provider.js but listens on TLS.
|
|
60
|
+
class HttpsOidcProvider {
|
|
61
|
+
async start({clients, tls}) {
|
|
62
|
+
this._server = https.createServer(tls);
|
|
63
|
+
await httpsListen.call(this._server, 0, 'localhost');
|
|
64
|
+
this.issuer = `https://localhost:${this._server.address().port}/`;
|
|
65
|
+
this._provider = new Provider(this.issuer, {
|
|
66
|
+
adapter: Adapter,
|
|
67
|
+
claims: {etherpad: ['name']},
|
|
68
|
+
clients,
|
|
69
|
+
cookies: {keys: [common.randomString()]},
|
|
70
|
+
features: {devInteractions: {enabled: false}},
|
|
71
|
+
findAccount: async (ctx, sub) => ({
|
|
72
|
+
accountId: sub,
|
|
73
|
+
claims: async () => ({sub, name: 'Firstname Lastname'}),
|
|
74
|
+
}),
|
|
75
|
+
jwks: {keys: [{
|
|
76
|
+
kty: 'EC',
|
|
77
|
+
crv: 'P-256',
|
|
78
|
+
d: 'K9xfPv773dZR22TVUB80xouzdF7qCg5cWjPjkHyv7Ws',
|
|
79
|
+
x: 'FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4',
|
|
80
|
+
y: '_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4',
|
|
81
|
+
use: 'sig',
|
|
82
|
+
}]},
|
|
83
|
+
ttl: {AccessToken: 3600, Grant: 3600, IdToken: 3600, Interaction: 3600, Session: 3600},
|
|
84
|
+
});
|
|
85
|
+
this._server.on('request', this._provider.callback());
|
|
86
|
+
// Same shortcut as the HTTP helper: skip past the login + consent UI by
|
|
87
|
+
// accepting the username in a query parameter.
|
|
88
|
+
this._provider.app.use(async (ctx, next) => {
|
|
89
|
+
const {request: {method, path: p, query}} = ctx;
|
|
90
|
+
if (!p.startsWith('/interaction/') || method !== 'GET') return await next();
|
|
91
|
+
const details = await this._provider.interactionDetails(ctx.req, ctx.res);
|
|
92
|
+
if (Object.entries(query).length === 0) {
|
|
93
|
+
ctx.response.body = `waiting for ${details.prompt.name}`;
|
|
94
|
+
ctx.response.status = 200;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
switch (details.prompt.name) {
|
|
98
|
+
case 'login':
|
|
99
|
+
return await this._provider.interactionFinished(ctx.req, ctx.res,
|
|
100
|
+
{login: {accountId: query.login, remember: false}});
|
|
101
|
+
case 'consent': {
|
|
102
|
+
const grant = details.grantId
|
|
103
|
+
? await this._provider.Grant.find(details.grantId)
|
|
104
|
+
: new this._provider.Grant({
|
|
105
|
+
accountId: details.session.accountId,
|
|
106
|
+
clientId: details.params.client_id,
|
|
107
|
+
});
|
|
108
|
+
const {prompt: {details: d}} = details;
|
|
109
|
+
if (d.missingOIDCScope) grant.addOIDCScope(d.missingOIDCScope.join(' '));
|
|
110
|
+
if (d.missingOIDCClaims) grant.addOIDCClaims(d.missingOIDCClaims);
|
|
111
|
+
if (d.missingResourceScopes) {
|
|
112
|
+
for (const [r, s] of Object.entries(d.missingResourceScopes)) {
|
|
113
|
+
grant.addResourceScope(r, s.join(' '));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const grantId = await grant.save();
|
|
117
|
+
return await this._provider.interactionFinished(ctx.req, ctx.res,
|
|
118
|
+
{consent: details.grantId ? {} : {grantId}});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return await next();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async stop() {
|
|
126
|
+
if (this._server == null) return;
|
|
127
|
+
await httpsClose.call(this._server);
|
|
128
|
+
this._server = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
describe(__filename, function () {
|
|
133
|
+
let provider;
|
|
134
|
+
let pems; // {key, cert, certPath} from generateSelfSignedCert()
|
|
135
|
+
const backup = {};
|
|
136
|
+
const client = {client_id: 'the_client_id', client_secret: 'the_client_secret'};
|
|
137
|
+
|
|
138
|
+
before(async function () {
|
|
139
|
+
await common.init();
|
|
140
|
+
backup.settings = {
|
|
141
|
+
requireAuthentication: settings.requireAuthentication,
|
|
142
|
+
requireAuthorization: settings.requireAuthorization,
|
|
143
|
+
users: settings.users,
|
|
144
|
+
};
|
|
145
|
+
// Self-signed cert valid for localhost. The same PEM is used as both
|
|
146
|
+
// the server cert AND the CA bundle the plugin trusts.
|
|
147
|
+
pems = generateSelfSignedCert();
|
|
148
|
+
|
|
149
|
+
provider = new HttpsOidcProvider();
|
|
150
|
+
await provider.start({
|
|
151
|
+
tls: {key: pems.key, cert: pems.cert},
|
|
152
|
+
clients: [{
|
|
153
|
+
...client,
|
|
154
|
+
redirect_uris: [new URL('/ep_openid_connect/callback', common.baseUrl).href],
|
|
155
|
+
}],
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
beforeEach(function () {
|
|
160
|
+
settings.requireAuthentication = true;
|
|
161
|
+
settings.requireAuthorization = false;
|
|
162
|
+
settings.users = {};
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
afterEach(function () {
|
|
166
|
+
settings.requireAuthentication = backup.settings.requireAuthentication;
|
|
167
|
+
settings.requireAuthorization = backup.settings.requireAuthorization;
|
|
168
|
+
settings.users = backup.settings.users;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
after(async function () {
|
|
172
|
+
if (provider != null) await provider.stop();
|
|
173
|
+
provider = null;
|
|
174
|
+
if (pems && pems.dir) try { fs.rmSync(pems.dir, {recursive: true, force: true}); } catch (_) { /* ignore */ }
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('loadCaBundle', function () {
|
|
178
|
+
const {loadCaBundle} = epOpenidConnect.exportedForTestingOnly;
|
|
179
|
+
|
|
180
|
+
it('returns null for an empty / missing value', function () {
|
|
181
|
+
assert.equal(loadCaBundle(undefined), null);
|
|
182
|
+
assert.equal(loadCaBundle(null), null);
|
|
183
|
+
assert.equal(loadCaBundle(''), null);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('returns inline PEM content unchanged', function () {
|
|
187
|
+
assert.equal(loadCaBundle(pems.cert), pems.cert);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('reads PEM content from a path', function () {
|
|
191
|
+
assert.equal(loadCaBundle(pems.certPath), pems.cert);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('throws on a non-existent path', function () {
|
|
195
|
+
assert.throws(() => loadCaBundle('/this/path/does/not/exist.pem'),
|
|
196
|
+
/ENOENT|no such file/);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('discovery against a self-signed HTTPS provider', function () {
|
|
201
|
+
const baseSettings = () => ({
|
|
202
|
+
...client,
|
|
203
|
+
base_url: common.baseUrl,
|
|
204
|
+
issuer: provider.issuer,
|
|
205
|
+
scope: ['openid', 'etherpad'],
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('fails without the ca setting', async function () {
|
|
209
|
+
// Discovery has to hit https://localhost:PORT/.well-known/... and
|
|
210
|
+
// there's no CA on the system trust store that signed our cert. The
|
|
211
|
+
// outer error from openid-client is the generic `TypeError: fetch
|
|
212
|
+
// failed`; the underlying TLS reason lives in err.cause.
|
|
213
|
+
await assert.rejects(
|
|
214
|
+
epOpenidConnect.loadSettings('loadSettings', {settings: {ep_openid_connect: {
|
|
215
|
+
...baseSettings(),
|
|
216
|
+
}}}),
|
|
217
|
+
(err) => {
|
|
218
|
+
const messages = [];
|
|
219
|
+
for (let e = err; e != null; e = e.cause) {
|
|
220
|
+
if (e.message) messages.push(String(e.message));
|
|
221
|
+
if (e.code) messages.push(String(e.code));
|
|
222
|
+
}
|
|
223
|
+
const joined = messages.join(' | ');
|
|
224
|
+
return /self-signed|self signed|unable to verify|UNABLE_TO_VERIFY_LEAF_SIGNATURE|CERT|TLS/i.test(joined);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('succeeds when ca points to a file', async function () {
|
|
229
|
+
await epOpenidConnect.loadSettings('loadSettings', {settings: {ep_openid_connect: {
|
|
230
|
+
...baseSettings(),
|
|
231
|
+
ca: pems.certPath,
|
|
232
|
+
}}});
|
|
233
|
+
// No throw means discovery completed against the self-signed IdP.
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('succeeds when ca contains inline PEM content', async function () {
|
|
237
|
+
await epOpenidConnect.loadSettings('loadSettings', {settings: {ep_openid_connect: {
|
|
238
|
+
...baseSettings(),
|
|
239
|
+
ca: pems.cert,
|
|
240
|
+
}}});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
});
|