@tachybase/plugin-auth-oidc 0.23.8

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.
Files changed (93) hide show
  1. package/.turbo/turbo-build.log +12 -0
  2. package/README.md +11 -0
  3. package/README.zh-CN.md +38 -0
  4. package/client.d.ts +2 -0
  5. package/client.js +1 -0
  6. package/dist/client/OIDCButton.d.ts +9 -0
  7. package/dist/client/Options.d.ts +2 -0
  8. package/dist/client/index.d.ts +5 -0
  9. package/dist/client/index.js +3 -0
  10. package/dist/client/locale/index.d.ts +3 -0
  11. package/dist/constants.d.ts +3 -0
  12. package/dist/constants.js +34 -0
  13. package/dist/externalVersion.js +14 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +39 -0
  16. package/dist/locale/en-US.json +40 -0
  17. package/dist/locale/es-ES.json +25 -0
  18. package/dist/locale/fr-FR.json +21 -0
  19. package/dist/locale/ko_KR.json +28 -0
  20. package/dist/locale/pt-BR.json +21 -0
  21. package/dist/locale/zh-CN.json +28 -0
  22. package/dist/node_modules/nanoid/.devcontainer.json +23 -0
  23. package/dist/node_modules/nanoid/LICENSE +20 -0
  24. package/dist/node_modules/nanoid/async/index.browser.cjs +69 -0
  25. package/dist/node_modules/nanoid/async/index.browser.js +69 -0
  26. package/dist/node_modules/nanoid/async/index.cjs +71 -0
  27. package/dist/node_modules/nanoid/async/index.d.ts +56 -0
  28. package/dist/node_modules/nanoid/async/index.js +71 -0
  29. package/dist/node_modules/nanoid/async/index.native.js +57 -0
  30. package/dist/node_modules/nanoid/async/package.json +12 -0
  31. package/dist/node_modules/nanoid/bin/nanoid.cjs +55 -0
  32. package/dist/node_modules/nanoid/index.browser.cjs +72 -0
  33. package/dist/node_modules/nanoid/index.browser.js +72 -0
  34. package/dist/node_modules/nanoid/index.cjs +1 -0
  35. package/dist/node_modules/nanoid/index.d.cts +91 -0
  36. package/dist/node_modules/nanoid/index.d.ts +91 -0
  37. package/dist/node_modules/nanoid/index.js +85 -0
  38. package/dist/node_modules/nanoid/nanoid.js +1 -0
  39. package/dist/node_modules/nanoid/non-secure/index.cjs +34 -0
  40. package/dist/node_modules/nanoid/non-secure/index.d.ts +33 -0
  41. package/dist/node_modules/nanoid/non-secure/index.js +34 -0
  42. package/dist/node_modules/nanoid/non-secure/package.json +6 -0
  43. package/dist/node_modules/nanoid/package.json +1 -0
  44. package/dist/node_modules/nanoid/url-alphabet/index.cjs +7 -0
  45. package/dist/node_modules/nanoid/url-alphabet/index.js +7 -0
  46. package/dist/node_modules/nanoid/url-alphabet/package.json +6 -0
  47. package/dist/node_modules/openid-client/lib/client.js +1884 -0
  48. package/dist/node_modules/openid-client/lib/device_flow_handle.js +125 -0
  49. package/dist/node_modules/openid-client/lib/errors.js +55 -0
  50. package/dist/node_modules/openid-client/lib/helpers/assert.js +24 -0
  51. package/dist/node_modules/openid-client/lib/helpers/base64url.js +13 -0
  52. package/dist/node_modules/openid-client/lib/helpers/client.js +208 -0
  53. package/dist/node_modules/openid-client/lib/helpers/consts.js +7 -0
  54. package/dist/node_modules/openid-client/lib/helpers/decode_jwt.js +27 -0
  55. package/dist/node_modules/openid-client/lib/helpers/deep_clone.js +1 -0
  56. package/dist/node_modules/openid-client/lib/helpers/defaults.js +27 -0
  57. package/dist/node_modules/openid-client/lib/helpers/generators.js +14 -0
  58. package/dist/node_modules/openid-client/lib/helpers/is_key_object.js +4 -0
  59. package/dist/node_modules/openid-client/lib/helpers/is_plain_object.js +1 -0
  60. package/dist/node_modules/openid-client/lib/helpers/issuer.js +111 -0
  61. package/dist/node_modules/openid-client/lib/helpers/keystore.js +298 -0
  62. package/dist/node_modules/openid-client/lib/helpers/merge.js +24 -0
  63. package/dist/node_modules/openid-client/lib/helpers/pick.js +9 -0
  64. package/dist/node_modules/openid-client/lib/helpers/process_response.js +71 -0
  65. package/dist/node_modules/openid-client/lib/helpers/request.js +200 -0
  66. package/dist/node_modules/openid-client/lib/helpers/unix_timestamp.js +1 -0
  67. package/dist/node_modules/openid-client/lib/helpers/weak_cache.js +1 -0
  68. package/dist/node_modules/openid-client/lib/helpers/webfinger_normalize.js +71 -0
  69. package/dist/node_modules/openid-client/lib/helpers/www_authenticate_parser.js +14 -0
  70. package/dist/node_modules/openid-client/lib/index.js +1 -0
  71. package/dist/node_modules/openid-client/lib/issuer.js +192 -0
  72. package/dist/node_modules/openid-client/lib/issuer_registry.js +3 -0
  73. package/dist/node_modules/openid-client/lib/passport_strategy.js +205 -0
  74. package/dist/node_modules/openid-client/lib/token_set.js +35 -0
  75. package/dist/node_modules/openid-client/package.json +1 -0
  76. package/dist/node_modules/openid-client/types/index.d.ts +623 -0
  77. package/dist/server/actions/getAuthUrl.d.ts +2 -0
  78. package/dist/server/actions/getAuthUrl.js +47 -0
  79. package/dist/server/actions/redirect.d.ts +2 -0
  80. package/dist/server/actions/redirect.js +55 -0
  81. package/dist/server/index.d.ts +1 -0
  82. package/dist/server/index.js +33 -0
  83. package/dist/server/migrations/20231007124508-update-autosignup.d.ts +6 -0
  84. package/dist/server/migrations/20231007124508-update-autosignup.js +52 -0
  85. package/dist/server/oidc-auth.d.ts +15 -0
  86. package/dist/server/oidc-auth.js +154 -0
  87. package/dist/server/plugin.d.ts +11 -0
  88. package/dist/server/plugin.js +83 -0
  89. package/dist/swagger/index.d.ts +143 -0
  90. package/dist/swagger/index.js +178 -0
  91. package/package.json +37 -0
  92. package/server.d.ts +2 -0
  93. package/server.js +1 -0
@@ -0,0 +1,192 @@
1
+ const { inspect } = require('util');
2
+ const url = require('url');
3
+
4
+ const { RPError } = require('./errors');
5
+ const getClient = require('./client');
6
+ const registry = require('./issuer_registry');
7
+ const processResponse = require('./helpers/process_response');
8
+ const webfingerNormalize = require('./helpers/webfinger_normalize');
9
+ const request = require('./helpers/request');
10
+ const clone = require('./helpers/deep_clone');
11
+ const { keystore } = require('./helpers/issuer');
12
+
13
+ const AAD_MULTITENANT_DISCOVERY = [
14
+ 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
15
+ 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
16
+ 'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration',
17
+ 'https://login.microsoftonline.com/consumers/v2.0/.well-known/openid-configuration',
18
+ ];
19
+ const AAD_MULTITENANT = Symbol();
20
+ const ISSUER_DEFAULTS = {
21
+ claim_types_supported: ['normal'],
22
+ claims_parameter_supported: false,
23
+ grant_types_supported: ['authorization_code', 'implicit'],
24
+ request_parameter_supported: false,
25
+ request_uri_parameter_supported: true,
26
+ require_request_uri_registration: false,
27
+ response_modes_supported: ['query', 'fragment'],
28
+ token_endpoint_auth_methods_supported: ['client_secret_basic'],
29
+ };
30
+
31
+ class Issuer {
32
+ #metadata;
33
+ constructor(meta = {}) {
34
+ const aadIssValidation = meta[AAD_MULTITENANT];
35
+ delete meta[AAD_MULTITENANT];
36
+ ['introspection', 'revocation'].forEach((endpoint) => {
37
+ // if intro/revocation endpoint auth specific meta is missing use the token ones if they
38
+ // are defined
39
+ if (
40
+ meta[`${endpoint}_endpoint`] &&
41
+ meta[`${endpoint}_endpoint_auth_methods_supported`] === undefined &&
42
+ meta[`${endpoint}_endpoint_auth_signing_alg_values_supported`] === undefined
43
+ ) {
44
+ if (meta.token_endpoint_auth_methods_supported) {
45
+ meta[`${endpoint}_endpoint_auth_methods_supported`] =
46
+ meta.token_endpoint_auth_methods_supported;
47
+ }
48
+ if (meta.token_endpoint_auth_signing_alg_values_supported) {
49
+ meta[`${endpoint}_endpoint_auth_signing_alg_values_supported`] =
50
+ meta.token_endpoint_auth_signing_alg_values_supported;
51
+ }
52
+ }
53
+ });
54
+
55
+ this.#metadata = new Map();
56
+
57
+ Object.entries(meta).forEach(([key, value]) => {
58
+ this.#metadata.set(key, value);
59
+ if (!this[key]) {
60
+ Object.defineProperty(this, key, {
61
+ get() {
62
+ return this.#metadata.get(key);
63
+ },
64
+ enumerable: true,
65
+ });
66
+ }
67
+ });
68
+
69
+ registry.set(this.issuer, this);
70
+
71
+ const Client = getClient(this, aadIssValidation);
72
+
73
+ Object.defineProperties(this, {
74
+ Client: { value: Client, enumerable: true },
75
+ FAPI1Client: { value: class FAPI1Client extends Client {}, enumerable: true },
76
+ FAPI2Client: { value: class FAPI2Client extends Client {}, enumerable: true },
77
+ });
78
+ }
79
+
80
+ get metadata() {
81
+ return clone(Object.fromEntries(this.#metadata.entries()));
82
+ }
83
+
84
+ static async webfinger(input) {
85
+ const resource = webfingerNormalize(input);
86
+ const { host } = url.parse(resource);
87
+ const webfingerUrl = `https://${host}/.well-known/webfinger`;
88
+
89
+ const response = await request.call(this, {
90
+ method: 'GET',
91
+ url: webfingerUrl,
92
+ responseType: 'json',
93
+ searchParams: { resource, rel: 'http://openid.net/specs/connect/1.0/issuer' },
94
+ headers: {
95
+ Accept: 'application/json',
96
+ },
97
+ });
98
+ const body = processResponse(response);
99
+
100
+ const location =
101
+ Array.isArray(body.links) &&
102
+ body.links.find(
103
+ (link) =>
104
+ typeof link === 'object' &&
105
+ link.rel === 'http://openid.net/specs/connect/1.0/issuer' &&
106
+ link.href,
107
+ );
108
+
109
+ if (!location) {
110
+ throw new RPError({
111
+ message: 'no issuer found in webfinger response',
112
+ body,
113
+ });
114
+ }
115
+
116
+ if (typeof location.href !== 'string' || !location.href.startsWith('https://')) {
117
+ throw new RPError({
118
+ printf: ['invalid issuer location %s', location.href],
119
+ body,
120
+ });
121
+ }
122
+
123
+ const expectedIssuer = location.href;
124
+ if (registry.has(expectedIssuer)) {
125
+ return registry.get(expectedIssuer);
126
+ }
127
+
128
+ const issuer = await this.discover(expectedIssuer);
129
+
130
+ if (issuer.issuer !== expectedIssuer) {
131
+ registry.del(issuer.issuer);
132
+ throw new RPError(
133
+ 'discovered issuer mismatch, expected %s, got: %s',
134
+ expectedIssuer,
135
+ issuer.issuer,
136
+ );
137
+ }
138
+ return issuer;
139
+ }
140
+
141
+ static async discover(uri) {
142
+ const wellKnownUri = resolveWellKnownUri(uri);
143
+
144
+ const response = await request.call(this, {
145
+ method: 'GET',
146
+ responseType: 'json',
147
+ url: wellKnownUri,
148
+ headers: {
149
+ Accept: 'application/json',
150
+ },
151
+ });
152
+ const body = processResponse(response);
153
+ return new Issuer({
154
+ ...ISSUER_DEFAULTS,
155
+ ...body,
156
+ [AAD_MULTITENANT]: !!AAD_MULTITENANT_DISCOVERY.find((discoveryURL) =>
157
+ wellKnownUri.startsWith(discoveryURL),
158
+ ),
159
+ });
160
+ }
161
+
162
+ async reloadJwksUri() {
163
+ await keystore.call(this, true);
164
+ }
165
+
166
+ /* istanbul ignore next */
167
+ [inspect.custom]() {
168
+ return `${this.constructor.name} ${inspect(this.metadata, {
169
+ depth: Infinity,
170
+ colors: process.stdout.isTTY,
171
+ compact: false,
172
+ sorted: true,
173
+ })}`;
174
+ }
175
+ }
176
+
177
+ function resolveWellKnownUri(uri) {
178
+ const parsed = url.parse(uri);
179
+ if (parsed.pathname.includes('/.well-known/')) {
180
+ return uri;
181
+ } else {
182
+ let pathname;
183
+ if (parsed.pathname.endsWith('/')) {
184
+ pathname = `${parsed.pathname}.well-known/openid-configuration`;
185
+ } else {
186
+ pathname = `${parsed.pathname}/.well-known/openid-configuration`;
187
+ }
188
+ return url.format({ ...parsed, pathname });
189
+ }
190
+ }
191
+
192
+ module.exports = Issuer;
@@ -0,0 +1,3 @@
1
+ const LRU = require('lru-cache');
2
+
3
+ module.exports = new LRU({ max: 100 });
@@ -0,0 +1,205 @@
1
+ const url = require('url');
2
+ const { format } = require('util');
3
+
4
+ const cloneDeep = require('./helpers/deep_clone');
5
+ const { RPError, OPError } = require('./errors');
6
+ const { BaseClient } = require('./client');
7
+ const { random, codeChallenge } = require('./helpers/generators');
8
+ const pick = require('./helpers/pick');
9
+ const { resolveResponseType, resolveRedirectUri } = require('./helpers/client');
10
+
11
+ function verified(err, user, info = {}) {
12
+ if (err) {
13
+ this.error(err);
14
+ } else if (!user) {
15
+ this.fail(info);
16
+ } else {
17
+ this.success(user, info);
18
+ }
19
+ }
20
+
21
+ function OpenIDConnectStrategy(
22
+ { client, params = {}, passReqToCallback = false, sessionKey, usePKCE = true, extras = {} } = {},
23
+ verify,
24
+ ) {
25
+ if (!(client instanceof BaseClient)) {
26
+ throw new TypeError('client must be an instance of openid-client Client');
27
+ }
28
+
29
+ if (typeof verify !== 'function') {
30
+ throw new TypeError('verify callback must be a function');
31
+ }
32
+
33
+ if (!client.issuer || !client.issuer.issuer) {
34
+ throw new TypeError('client must have an issuer with an identifier');
35
+ }
36
+
37
+ this._client = client;
38
+ this._issuer = client.issuer;
39
+ this._verify = verify;
40
+ this._passReqToCallback = passReqToCallback;
41
+ this._usePKCE = usePKCE;
42
+ this._key = sessionKey || `oidc:${url.parse(this._issuer.issuer).hostname}`;
43
+ this._params = cloneDeep(params);
44
+
45
+ // state and nonce are handled in authenticate()
46
+ delete this._params.state;
47
+ delete this._params.nonce;
48
+
49
+ this._extras = cloneDeep(extras);
50
+
51
+ if (!this._params.response_type) this._params.response_type = resolveResponseType.call(client);
52
+ if (!this._params.redirect_uri) this._params.redirect_uri = resolveRedirectUri.call(client);
53
+ if (!this._params.scope) this._params.scope = 'openid';
54
+
55
+ if (this._usePKCE === true) {
56
+ const supportedMethods = Array.isArray(this._issuer.code_challenge_methods_supported)
57
+ ? this._issuer.code_challenge_methods_supported
58
+ : false;
59
+
60
+ if (supportedMethods && supportedMethods.includes('S256')) {
61
+ this._usePKCE = 'S256';
62
+ } else if (supportedMethods && supportedMethods.includes('plain')) {
63
+ this._usePKCE = 'plain';
64
+ } else if (supportedMethods) {
65
+ throw new TypeError(
66
+ 'neither code_challenge_method supported by the client is supported by the issuer',
67
+ );
68
+ } else {
69
+ this._usePKCE = 'S256';
70
+ }
71
+ } else if (typeof this._usePKCE === 'string' && !['plain', 'S256'].includes(this._usePKCE)) {
72
+ throw new TypeError(`${this._usePKCE} is not valid/implemented PKCE code_challenge_method`);
73
+ }
74
+
75
+ this.name = url.parse(client.issuer.issuer).hostname;
76
+ }
77
+
78
+ OpenIDConnectStrategy.prototype.authenticate = function authenticate(req, options) {
79
+ (async () => {
80
+ const client = this._client;
81
+ if (!req.session) {
82
+ throw new TypeError('authentication requires session support');
83
+ }
84
+ const reqParams = client.callbackParams(req);
85
+ const sessionKey = this._key;
86
+
87
+ const { 0: parameter, length } = Object.keys(reqParams);
88
+
89
+ /**
90
+ * Start authentication request if this has no authorization response parameters or
91
+ * this might a login initiated from a third party as per
92
+ * https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin.
93
+ */
94
+ if (length === 0 || (length === 1 && parameter === 'iss')) {
95
+ // provide options object with extra authentication parameters
96
+ const params = {
97
+ state: random(),
98
+ ...this._params,
99
+ ...options,
100
+ };
101
+
102
+ if (!params.nonce && params.response_type.includes('id_token')) {
103
+ params.nonce = random();
104
+ }
105
+
106
+ req.session[sessionKey] = pick(params, 'nonce', 'state', 'max_age', 'response_type');
107
+
108
+ if (this._usePKCE && params.response_type.includes('code')) {
109
+ const verifier = random();
110
+ req.session[sessionKey].code_verifier = verifier;
111
+
112
+ switch (this._usePKCE) {
113
+ case 'S256':
114
+ params.code_challenge = codeChallenge(verifier);
115
+ params.code_challenge_method = 'S256';
116
+ break;
117
+ case 'plain':
118
+ params.code_challenge = verifier;
119
+ break;
120
+ }
121
+ }
122
+
123
+ this.redirect(client.authorizationUrl(params));
124
+ return;
125
+ }
126
+ /* end authentication request */
127
+
128
+ /* start authentication response */
129
+
130
+ const session = req.session[sessionKey];
131
+ if (Object.keys(session || {}).length === 0) {
132
+ throw new Error(
133
+ format(
134
+ 'did not find expected authorization request details in session, req.session["%s"] is %j',
135
+ sessionKey,
136
+ session,
137
+ ),
138
+ );
139
+ }
140
+
141
+ const {
142
+ state,
143
+ nonce,
144
+ max_age: maxAge,
145
+ code_verifier: codeVerifier,
146
+ response_type: responseType,
147
+ } = session;
148
+
149
+ try {
150
+ delete req.session[sessionKey];
151
+ } catch (err) {}
152
+
153
+ const opts = {
154
+ redirect_uri: this._params.redirect_uri,
155
+ ...options,
156
+ };
157
+
158
+ const checks = {
159
+ state,
160
+ nonce,
161
+ max_age: maxAge,
162
+ code_verifier: codeVerifier,
163
+ response_type: responseType,
164
+ };
165
+
166
+ const tokenset = await client.callback(opts.redirect_uri, reqParams, checks, this._extras);
167
+
168
+ const passReq = this._passReqToCallback;
169
+ const loadUserinfo = this._verify.length > (passReq ? 3 : 2) && client.issuer.userinfo_endpoint;
170
+
171
+ const args = [tokenset, verified.bind(this)];
172
+
173
+ if (loadUserinfo) {
174
+ if (!tokenset.access_token) {
175
+ throw new RPError({
176
+ message:
177
+ 'expected access_token to be returned when asking for userinfo in verify callback',
178
+ tokenset,
179
+ });
180
+ }
181
+ const userinfo = await client.userinfo(tokenset);
182
+ args.splice(1, 0, userinfo);
183
+ }
184
+
185
+ if (passReq) {
186
+ args.unshift(req);
187
+ }
188
+
189
+ this._verify(...args);
190
+ /* end authentication response */
191
+ })().catch((error) => {
192
+ if (
193
+ (error instanceof OPError &&
194
+ error.error !== 'server_error' &&
195
+ !error.error.startsWith('invalid')) ||
196
+ error instanceof RPError
197
+ ) {
198
+ this.fail(error);
199
+ } else {
200
+ this.error(error);
201
+ }
202
+ });
203
+ };
204
+
205
+ module.exports = OpenIDConnectStrategy;
@@ -0,0 +1,35 @@
1
+ const base64url = require('./helpers/base64url');
2
+ const now = require('./helpers/unix_timestamp');
3
+
4
+ class TokenSet {
5
+ constructor(values) {
6
+ Object.assign(this, values);
7
+ const { constructor, ...properties } = Object.getOwnPropertyDescriptors(
8
+ this.constructor.prototype,
9
+ );
10
+
11
+ Object.defineProperties(this, properties);
12
+ }
13
+
14
+ set expires_in(value) {
15
+ this.expires_at = now() + Number(value);
16
+ }
17
+
18
+ get expires_in() {
19
+ return Math.max.apply(null, [this.expires_at - now(), 0]);
20
+ }
21
+
22
+ expired() {
23
+ return this.expires_in === 0;
24
+ }
25
+
26
+ claims() {
27
+ if (!this.id_token) {
28
+ throw new TypeError('id_token not present in TokenSet');
29
+ }
30
+
31
+ return JSON.parse(base64url.decode(this.id_token.split('.')[1]));
32
+ }
33
+ }
34
+
35
+ module.exports = TokenSet;
@@ -0,0 +1 @@
1
+ {"name":"openid-client","version":"5.7.1","description":"OpenID Connect Relying Party (RP, Client) implementation for Node.js runtime, supports passportjs","keywords":["auth","authentication","basic","certified","client","connect","dynamic","electron","hybrid","identity","implicit","oauth","oauth2","oidc","openid","passport","relying party","strategy"],"homepage":"https://github.com/panva/openid-client","repository":"panva/openid-client","funding":{"url":"https://github.com/sponsors/panva"},"license":"MIT","author":"Filip Skokan <panva.ip@gmail.com>","exports":{"types":"./types/index.d.ts","import":"./lib/index.mjs","require":"./lib/index.js"},"main":"./lib/index.js","types":"./types/index.d.ts","files":["lib","types/index.d.ts"],"scripts":{"format":"npx prettier --loglevel silent --write ./lib ./test ./certification ./types","test":"mocha test/**/*.test.js"},"dependencies":{"jose":"^4.15.9","lru-cache":"^6.0.0","object-hash":"^2.2.0","oidc-token-hash":"^5.0.3"},"devDependencies":{"@types/node":"^16.18.106","@types/passport":"^1.0.16","base64url":"^3.0.1","chai":"^4.5.0","mocha":"^10.7.3","nock":"^13.5.5","prettier":"^2.8.8","readable-mock-req":"^0.2.2","sinon":"^9.2.4","timekeeper":"^2.3.1"},"standard-version":{"scripts":{"postchangelog":"sed -i '' -e 's/### \\[/## [/g' CHANGELOG.md"},"types":[{"type":"feat","section":"Features"},{"type":"fix","section":"Fixes"},{"type":"chore","hidden":true},{"type":"docs","hidden":true},{"type":"style","hidden":true},{"type":"refactor","section":"Refactor","hidden":false},{"type":"perf","section":"Performance","hidden":false},{"type":"test","hidden":true}]},"_lastModified":"2024-12-22T16:05:42.361Z"}