ep_openid_connect 3.0.23 → 3.0.25
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/AGENTS.md +58 -0
- package/README.md +5 -0
- package/index.js +18 -2
- package/package.json +1 -1
- package/static/tests/backend/specs/tests.js +25 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Agent Guide — ep_openid_connect
|
|
2
|
+
|
|
3
|
+
Etherpad plugin to authenticate users against an OpenID Connect provider.
|
|
4
|
+
|
|
5
|
+
## Tech stack
|
|
6
|
+
|
|
7
|
+
* Etherpad plugin framework (hooks declared in `ep.json`)
|
|
8
|
+
|
|
9
|
+
## Project structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
ep_openid_connect/
|
|
13
|
+
├── AGENTS.md
|
|
14
|
+
├── CHANGELOG.md
|
|
15
|
+
├── CONTRIBUTING.md
|
|
16
|
+
├── ep.json
|
|
17
|
+
├── index.js
|
|
18
|
+
├── package.json
|
|
19
|
+
├── static/
|
|
20
|
+
│ ├── tests/
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Helpers used
|
|
24
|
+
|
|
25
|
+
_None — `ep_plugin_helpers` is not a dependency. Adoption is part of the helpers-adoption sweep (Phase 4)._
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Helpers NOT used
|
|
29
|
+
|
|
30
|
+
_To be audited in the helpers-adoption sweep (Phase 4)._
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Running tests locally
|
|
34
|
+
|
|
35
|
+
`ep_openid_connect` runs inside Etherpad's test harness. From an etherpad checkout that has installed this plugin via `pnpm run plugins i --path ../ep_openid_connect`:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Backend (Mocha) — harness boots its own server
|
|
39
|
+
pnpm --filter ep_etherpad-lite run test
|
|
40
|
+
|
|
41
|
+
# Playwright — needs `pnpm run dev` in a second terminal
|
|
42
|
+
pnpm --filter ep_etherpad-lite run test-ui
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Standing rules for agent edits
|
|
46
|
+
|
|
47
|
+
* PRs target `main`. Linear commits, no merge commits.
|
|
48
|
+
* Every bug fix includes a regression test in the same commit.
|
|
49
|
+
* All user-facing strings in `locales/`. No hardcoded English in templates.
|
|
50
|
+
* No hardcoded `aria-label` on icon-only controls — etherpad's html10n auto-populates `aria-label` from the localized string when (a) the element has a `data-l10n-id` and (b) no author-supplied `aria-label` is present. Adding a hardcoded English `aria-label` blocks that and leaves it untranslated. (See `etherpad-lite/src/static/js/vendors/html10n.ts:665-678`.)
|
|
51
|
+
* No nested interactive elements (no `<button>` inside `<a>`).
|
|
52
|
+
* LLM/Agent contributions are explicitly welcomed by maintainers.
|
|
53
|
+
|
|
54
|
+
## Quick reference: hooks declared in `ep.json`
|
|
55
|
+
|
|
56
|
+
* Server: `authenticate`, `authnFailure`, `expressCreateServer`, `init_ep_openid_connect`, `loadSettings`, `preAuthorize`
|
|
57
|
+
|
|
58
|
+
When adding a hook, register it in both `ep.json` *and* the matching `exports.<hook> = ...` in the JS file.
|
package/README.md
CHANGED
|
@@ -49,6 +49,11 @@ Etherpad's `requireAuthentication` setting must be `true`.
|
|
|
49
49
|
* `client_id` (required): The OAuth2 client ID issued by the identity provider.
|
|
50
50
|
* `client_secret` (required): The OAuth2 client secret issued by the identity
|
|
51
51
|
provider.
|
|
52
|
+
* `token_endpoint_auth_method` (optional; defaults to `client_secret_basic`):
|
|
53
|
+
Client authentication method to use for token endpoint requests. Supported
|
|
54
|
+
values are `client_secret_basic` and `client_secret_post`. Use
|
|
55
|
+
`client_secret_post` if your identity provider expects the client secret in
|
|
56
|
+
the token request body instead of the HTTP `Authorization` header.
|
|
52
57
|
* `base_url` (required): The public base Etherpad URL. When registering Etherpad
|
|
53
58
|
with your identity provider, the redirect URL (a.k.a. callback URL) is this
|
|
54
59
|
base URL plus `/ep_openid_connect/callback`.
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ for (const level of ['debug', 'info', 'warn', 'error']) {
|
|
|
10
10
|
const defaultSettings = {
|
|
11
11
|
prohibited_usernames: ['admin', 'guest'],
|
|
12
12
|
scope: ['openid'],
|
|
13
|
+
token_endpoint_auth_method: 'client_secret_basic',
|
|
13
14
|
user_properties: {},
|
|
14
15
|
};
|
|
15
16
|
let settings;
|
|
@@ -33,6 +34,7 @@ const validSettings = new Ajv().compile({
|
|
|
33
34
|
issuer_metadata: {},
|
|
34
35
|
prohibited_usernames: {elements: {type: 'string'}},
|
|
35
36
|
scope: {elements: {type: 'string'}},
|
|
37
|
+
token_endpoint_auth_method: {enum: ['client_secret_basic', 'client_secret_post']},
|
|
36
38
|
user_properties: {values: {
|
|
37
39
|
optionalProperties: {
|
|
38
40
|
claim: {type: 'string'},
|
|
@@ -100,11 +102,25 @@ const discoverConfig = async (issuerUrl, clientId, clientAuth) => {
|
|
|
100
102
|
}
|
|
101
103
|
};
|
|
102
104
|
|
|
105
|
+
const getClientAuth = (settings) => {
|
|
106
|
+
switch (settings.token_endpoint_auth_method) {
|
|
107
|
+
case 'client_secret_basic':
|
|
108
|
+
// eslint-disable-next-line new-cap
|
|
109
|
+
return oidc.ClientSecretBasic(settings.client_secret);
|
|
110
|
+
case 'client_secret_post':
|
|
111
|
+
// eslint-disable-next-line new-cap
|
|
112
|
+
return oidc.ClientSecretPost(settings.client_secret);
|
|
113
|
+
default:
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Unsupported token endpoint auth method: ${settings.token_endpoint_auth_method}`);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
103
119
|
const buildConfig = async (settings) => {
|
|
104
120
|
// openid-client@5 defaulted the token endpoint auth method to
|
|
105
121
|
// `client_secret_basic`; v6 defaults to `client_secret_post`. Pin the
|
|
106
|
-
// method explicitly so existing IdP registrations keep working.
|
|
107
|
-
const clientAuth =
|
|
122
|
+
// default method explicitly so existing IdP registrations keep working.
|
|
123
|
+
const clientAuth = getClientAuth(settings);
|
|
108
124
|
if (settings.issuer) {
|
|
109
125
|
const config =
|
|
110
126
|
await discoverConfig(settings.issuer, settings.client_id, clientAuth);
|
package/package.json
CHANGED
|
@@ -125,6 +125,31 @@ describe(__filename, function () {
|
|
|
125
125
|
assert.equal(msg.data.readonly, false);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
+
describe('token_endpoint_auth_method', function () {
|
|
129
|
+
for (const tokenEndpointAuthMethod of ['client_secret_basic', 'client_secret_post']) {
|
|
130
|
+
it(`supports ${tokenEndpointAuthMethod}`, async function () {
|
|
131
|
+
await epOpenidConnect.loadSettings('loadSettings', {settings: {ep_openid_connect: {
|
|
132
|
+
...pluginSettings,
|
|
133
|
+
token_endpoint_auth_method: tokenEndpointAuthMethod,
|
|
134
|
+
}}});
|
|
135
|
+
const padId = common.randomString();
|
|
136
|
+
const url = new URL(`/p/${padId}`, common.baseUrl).toString();
|
|
137
|
+
const res = await login(agent, issuer, url, 'normalUser');
|
|
138
|
+
assert.equal(res.request.url, url);
|
|
139
|
+
assert.equal(res.status, 200);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
it('rejects unsupported methods', async function () {
|
|
144
|
+
await epOpenidConnect.loadSettings('loadSettings', {settings: {ep_openid_connect: {
|
|
145
|
+
...pluginSettings,
|
|
146
|
+
token_endpoint_auth_method: 'unsupported',
|
|
147
|
+
}}});
|
|
148
|
+
await agent.get(new URL('/ep_openid_connect/login', common.baseUrl))
|
|
149
|
+
.expect(401);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
128
153
|
it('noCreate user is unable to create a pad', async function () {
|
|
129
154
|
const padId = common.randomString();
|
|
130
155
|
const url = new URL(`/p/${padId}`, common.baseUrl).toString();
|