@webda/auth 0.0.0-stage → 4.0.0-beta.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/LICENSE +166 -0
- package/README.md +26 -2
- package/lib/authentication.service.d.ts +286 -0
- package/lib/authentication.service.d.ts.map +1 -0
- package/lib/authentication.service.js +798 -0
- package/lib/authentication.service.js.map +1 -0
- package/lib/compat/upgrade.d.ts +71 -0
- package/lib/compat/upgrade.d.ts.map +1 -0
- package/lib/compat/upgrade.js +185 -0
- package/lib/compat/upgrade.js.map +1 -0
- package/lib/email/emailpassword.service.d.ts +241 -0
- package/lib/email/emailpassword.service.d.ts.map +1 -0
- package/lib/email/emailpassword.service.js +718 -0
- package/lib/email/emailpassword.service.js.map +1 -0
- package/lib/email/tokens.d.ts +36 -0
- package/lib/email/tokens.d.ts.map +1 -0
- package/lib/email/tokens.js +46 -0
- package/lib/email/tokens.js.map +1 -0
- package/lib/errors.d.ts +53 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +73 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +1 -0
- package/lib/oauth/oauth.service.d.ts +293 -0
- package/lib/oauth/oauth.service.d.ts.map +1 -0
- package/lib/oauth/oauth.service.js +557 -0
- package/lib/oauth/oauth.service.js.map +1 -0
- package/lib/provider.d.ts +53 -0
- package/lib/provider.d.ts.map +1 -0
- package/lib/provider.js +55 -0
- package/lib/provider.js.map +1 -0
- package/lib/test/authtest.d.ts +39 -0
- package/lib/test/authtest.d.ts.map +1 -0
- package/lib/test/authtest.js +62 -0
- package/lib/test/authtest.js.map +1 -0
- package/lib/test/emailtest.d.ts +25 -0
- package/lib/test/emailtest.d.ts.map +1 -0
- package/lib/test/emailtest.js +39 -0
- package/lib/test/emailtest.js.map +1 -0
- package/lib/test/fakeoauth.service.d.ts +45 -0
- package/lib/test/fakeoauth.service.d.ts.map +1 -0
- package/lib/test/fakeoauth.service.js +78 -0
- package/lib/test/fakeoauth.service.js.map +1 -0
- package/lib/test/stubprovider.d.ts +35 -0
- package/lib/test/stubprovider.d.ts.map +1 -0
- package/lib/test/stubprovider.js +46 -0
- package/lib/test/stubprovider.js.map +1 -0
- package/lib/test/v3.d.ts +29 -0
- package/lib/test/v3.d.ts.map +1 -0
- package/lib/test/v3.js +47 -0
- package/lib/test/v3.js.map +1 -0
- package/lib/throttle.d.ts +40 -0
- package/lib/throttle.d.ts.map +1 -0
- package/lib/throttle.js +47 -0
- package/lib/throttle.js.map +1 -0
- package/package.json +69 -3
- package/test/config.json +20 -0
- package/webda.module.json +632 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
36
|
+
import { CookieOptions, registerOperation, Route, runAsSystem, runWithContext, SecureCookie, Service, ServiceParameters, useContext, useCrypto, useDynamicService, WebdaError } from "@webda/core";
|
|
37
|
+
import { useAuthentication } from "../provider.js";
|
|
38
|
+
import { IdentLinkedElsewhere, TokenInvalid, UnsupportedMediaType } from "../errors.js";
|
|
39
|
+
/** OAuthProvider parameters */
|
|
40
|
+
export class OAuthProviderParameters extends ServiceParameters {
|
|
41
|
+
/**
|
|
42
|
+
* @param params - raw parameters
|
|
43
|
+
* @returns this
|
|
44
|
+
*/
|
|
45
|
+
load(params = {}) {
|
|
46
|
+
super.load(params);
|
|
47
|
+
this.authorized_uris ?? (this.authorized_uris = []);
|
|
48
|
+
this.redirects = { success: "/", ...(this.redirects ?? {}) };
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Validity of a pending login, in seconds */
|
|
53
|
+
const PENDING_TTL = 600;
|
|
54
|
+
/**
|
|
55
|
+
* Maximum length of a login `redirect`: the pending login cookie must stay a single cookie (SecureCookie splits
|
|
56
|
+
* values above ~4 KB, and the encrypted cookie is about 1.8 times the size of its content)
|
|
57
|
+
*/
|
|
58
|
+
const REDIRECT_MAX = 1024;
|
|
59
|
+
/**
|
|
60
|
+
* @param err - any error
|
|
61
|
+
* @returns the code of a WebdaError.HttpError, undefined for anything else
|
|
62
|
+
*/
|
|
63
|
+
function httpErrorCode(err) {
|
|
64
|
+
return typeof err?.getResponseCode === "function" &&
|
|
65
|
+
typeof err?.code === "string" &&
|
|
66
|
+
/^[A-Z][A-Z0-9_]*$/.test(err.code)
|
|
67
|
+
? err.code
|
|
68
|
+
: undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Loggable reason of an error: library messages may contain tokens or their payload, so only a short code or the
|
|
72
|
+
* error class name is kept
|
|
73
|
+
* @param err - any error
|
|
74
|
+
* @returns a reason without any part of the message
|
|
75
|
+
*/
|
|
76
|
+
export function safeErrorReason(err) {
|
|
77
|
+
const safe = (value) => typeof value === "string" && /^[A-Za-z0-9_.-]{1,40}$/.test(value);
|
|
78
|
+
const name = safe(err?.name) ? err.name : "Error";
|
|
79
|
+
const code = err?.code;
|
|
80
|
+
if (safe(code) || (typeof code === "number" && Number.isFinite(code))) {
|
|
81
|
+
return `${name}(${code})`;
|
|
82
|
+
}
|
|
83
|
+
return name;
|
|
84
|
+
}
|
|
85
|
+
/** Credentials keys kept from a client `tokens` body */
|
|
86
|
+
const TOKEN_KEYS = ["id_token", "access_token", "refresh_token", "expiry_date", "token_type", "scope"];
|
|
87
|
+
/** Maximum length of a kept credentials value */
|
|
88
|
+
const TOKEN_VALUE_MAX = 4096;
|
|
89
|
+
/**
|
|
90
|
+
* Keep only the known credentials keys, as bounded strings or finite numbers
|
|
91
|
+
* @param tokens - client provided credentials
|
|
92
|
+
* @returns the sanitised credentials
|
|
93
|
+
*/
|
|
94
|
+
export function sanitizeTokens(tokens) {
|
|
95
|
+
const out = {};
|
|
96
|
+
for (const key of TOKEN_KEYS) {
|
|
97
|
+
const value = tokens?.[key];
|
|
98
|
+
if ((typeof value === "string" && value.length <= TOKEN_VALUE_MAX) ||
|
|
99
|
+
(typeof value === "number" && Number.isFinite(value))) {
|
|
100
|
+
out[key] = value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* @param url - absolute or relative url
|
|
107
|
+
* @param params - query parameters to add
|
|
108
|
+
* @returns the url with the parameters appended
|
|
109
|
+
*/
|
|
110
|
+
function withQuery(url, params) {
|
|
111
|
+
const query = new URLSearchParams(params).toString();
|
|
112
|
+
const hash = url.indexOf("#");
|
|
113
|
+
const [base, fragment] = hash < 0 ? [url, ""] : [url.substring(0, hash), url.substring(hash)];
|
|
114
|
+
return `${base}${base.includes("?") ? "&" : "?"}${query}${fragment}`;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* @param url - candidate
|
|
118
|
+
* @returns the parsed url when it is an absolute http(s) url without credentials
|
|
119
|
+
*/
|
|
120
|
+
function parseHttpUrl(url) {
|
|
121
|
+
if (typeof url !== "string")
|
|
122
|
+
return undefined;
|
|
123
|
+
try {
|
|
124
|
+
const parsed = new URL(url);
|
|
125
|
+
if (!["http:", "https:"].includes(parsed.protocol) || parsed.username || parsed.password)
|
|
126
|
+
return undefined;
|
|
127
|
+
return parsed;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Constant-time comparison of two secrets
|
|
135
|
+
* @param a - received value
|
|
136
|
+
* @param b - expected value
|
|
137
|
+
* @returns true when equal
|
|
138
|
+
*/
|
|
139
|
+
function sameSecret(a, b) {
|
|
140
|
+
const left = Buffer.from(a);
|
|
141
|
+
const right = Buffer.from(b);
|
|
142
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* @param bytes - entropy
|
|
146
|
+
* @returns a random base64url string
|
|
147
|
+
*/
|
|
148
|
+
function randomSecret(bytes) {
|
|
149
|
+
return randomBytes(bytes).toString("base64url");
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Base of the OAuth 2.0 / OpenID Connect login providers
|
|
153
|
+
*
|
|
154
|
+
* Exposes, under `url` (default `/auth/<providerName>`):
|
|
155
|
+
* - `GET <url>{?redirect}`: starts a login with a random `state`, a PKCE (S256) verifier and an OpenID `nonce`, kept
|
|
156
|
+
* in a dedicated encrypted cookie (10 min, HttpOnly, SameSite=Lax, path `url`), then redirects to the provider;
|
|
157
|
+
* `redirect` must match `authorized_uris`
|
|
158
|
+
* - `GET <url>/callback`: consumes that cookie, checks the state, exchanges the code (`handleCallback`) and hands
|
|
159
|
+
* the identity to `Authentication.complete()`; never throws, failures go to `redirects.failure?reason=<CODE>`
|
|
160
|
+
* - operation `Auth.<Provider>.Token` (`POST auth/<providerName>/token`, JSON body `{ token }` or `{ tokens }`) for
|
|
161
|
+
* non-browser clients; it never links an identity to the session user
|
|
162
|
+
*
|
|
163
|
+
* Subclasses implement `providerName`, `getAuthorizationUrl`, `handleCallback` and `handleToken`; the provider of the
|
|
164
|
+
* returned identities is always forced to `providerName`.
|
|
165
|
+
*/
|
|
166
|
+
let OAuthProvider = (() => {
|
|
167
|
+
var _a;
|
|
168
|
+
let _classSuper = Service;
|
|
169
|
+
let _instanceExtraInitializers = [];
|
|
170
|
+
let _login_decorators;
|
|
171
|
+
let _callback_decorators;
|
|
172
|
+
return _a = class OAuthProvider extends _classSuper {
|
|
173
|
+
/**
|
|
174
|
+
* Called after a successful `Authentication.complete()` (status ok or mfa_required)
|
|
175
|
+
* @param _identity - the identity
|
|
176
|
+
* @param _result - the result
|
|
177
|
+
* @param _source - browser callback or token operation
|
|
178
|
+
* @param _request - the token operation request (token operation only)
|
|
179
|
+
*/
|
|
180
|
+
async onAuthenticated(_identity, _result, _source, _request) {
|
|
181
|
+
// Hook for providers
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* @returns false when the provider works without client secret (public clients)
|
|
185
|
+
*/
|
|
186
|
+
requiresClientSecret() {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* @returns the name of the pending login cookie
|
|
191
|
+
*/
|
|
192
|
+
getCookieName() {
|
|
193
|
+
return `webda_oauth_${this.providerName}`;
|
|
194
|
+
}
|
|
195
|
+
/** @override */
|
|
196
|
+
resolve() {
|
|
197
|
+
var _b;
|
|
198
|
+
super.resolve();
|
|
199
|
+
(_b = this.parameters).url ?? (_b.url = `/auth/${this.providerName}`);
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
/** @override */
|
|
203
|
+
async init() {
|
|
204
|
+
await super.init();
|
|
205
|
+
const name = `${this.constructor.name} '${this.getName()}'`;
|
|
206
|
+
const params = this.parameters;
|
|
207
|
+
if (!params.client_id) {
|
|
208
|
+
throw new Error(`${name} requires the 'client_id' parameter`);
|
|
209
|
+
}
|
|
210
|
+
if (this.requiresClientSecret() && !params.client_secret) {
|
|
211
|
+
throw new Error(`${name} requires the 'client_secret' parameter`);
|
|
212
|
+
}
|
|
213
|
+
if (!params.redirects?.failure) {
|
|
214
|
+
throw new Error(`${name} requires the 'redirects.failure' parameter`);
|
|
215
|
+
}
|
|
216
|
+
if (!useDynamicService("Authentication")) {
|
|
217
|
+
throw new Error(`${name} requires the Authentication service`);
|
|
218
|
+
}
|
|
219
|
+
for (const uri of params.authorized_uris ?? []) {
|
|
220
|
+
if (!parseHttpUrl(uri)) {
|
|
221
|
+
throw new Error(`${name}: authorized_uris entries must be absolute http(s) urls, got '${uri}'`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Register `Auth.<Provider>.Token`: its id and path depend on the provider name
|
|
228
|
+
* @override
|
|
229
|
+
*/
|
|
230
|
+
initOperations() {
|
|
231
|
+
super.initOperations();
|
|
232
|
+
const name = this.providerName;
|
|
233
|
+
const id = this.getOperationId(`Auth.${name.substring(0, 1).toUpperCase()}${name.substring(1)}.Token`);
|
|
234
|
+
if (!id)
|
|
235
|
+
return;
|
|
236
|
+
registerOperation(id, {
|
|
237
|
+
service: this.getName(),
|
|
238
|
+
method: "token",
|
|
239
|
+
// Schemas are compiled per class: a shared named schema serves every provider
|
|
240
|
+
input: "Webda/OAuthTokenRequest",
|
|
241
|
+
summary: `Log in with a ${name} token`,
|
|
242
|
+
tags: ["Authentication"],
|
|
243
|
+
rest: { method: "post", path: `auth/${name}/token` }
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* @returns public info
|
|
248
|
+
*/
|
|
249
|
+
getPublicInfo() {
|
|
250
|
+
return { name: this.providerName, type: "oauth", startUrl: this.parameters.url ?? `/auth/${this.providerName}` };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Log in with a token obtained by the client from the provider
|
|
254
|
+
*
|
|
255
|
+
* Requires a JSON request (a cross-site form or `text/plain` POST is refused). A request carrying a logged-in
|
|
256
|
+
* session never links the identity to that user: an identity owned by another user is refused
|
|
257
|
+
* (IDENT_LINKED_ELSEWHERE), a new or unowned one gets a fresh session.
|
|
258
|
+
* @param token - token (an ID token for OpenID Connect providers)
|
|
259
|
+
* @param tokens - credentials obtained from the provider (v3 body)
|
|
260
|
+
* @returns the auth result
|
|
261
|
+
*/
|
|
262
|
+
async token(token, tokens) {
|
|
263
|
+
const ctx = useContext();
|
|
264
|
+
this.requireJson(ctx);
|
|
265
|
+
if (token !== undefined && (typeof token !== "string" || !token)) {
|
|
266
|
+
throw new WebdaError.BadRequest("token must be a non-empty string");
|
|
267
|
+
}
|
|
268
|
+
if (tokens !== undefined && (tokens === null || typeof tokens !== "object" || Array.isArray(tokens))) {
|
|
269
|
+
throw new WebdaError.BadRequest("tokens must be an object");
|
|
270
|
+
}
|
|
271
|
+
if (!token && !tokens) {
|
|
272
|
+
throw new WebdaError.BadRequest("token or tokens is required");
|
|
273
|
+
}
|
|
274
|
+
// Only known credentials keys are verified, stored or published
|
|
275
|
+
tokens = tokens ? sanitizeTokens(tokens) : undefined;
|
|
276
|
+
let identity;
|
|
277
|
+
try {
|
|
278
|
+
identity = this.ownIdentity(this.checkIdentity(await this.handleToken({ token, tokens })));
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
if (httpErrorCode(err))
|
|
282
|
+
throw err;
|
|
283
|
+
// Fail closed: an unexpected verification failure is an invalid token
|
|
284
|
+
this.log("WARN", `${this.providerName} token verification failed:`, safeErrorReason(err));
|
|
285
|
+
throw new TokenInvalid();
|
|
286
|
+
}
|
|
287
|
+
await this.leaveForeignSession(ctx, identity);
|
|
288
|
+
const result = await useAuthentication().complete(identity);
|
|
289
|
+
await this.onAuthenticated(identity, result, "token", { token, tokens });
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Refuse a request that is not JSON: cross-site requests can only send form or `text/plain` bodies without a
|
|
294
|
+
* preflight
|
|
295
|
+
* @param ctx - operation context
|
|
296
|
+
* @throws UnsupportedMediaType when the HTTP request is not `application/json`
|
|
297
|
+
*/
|
|
298
|
+
requireJson(ctx) {
|
|
299
|
+
const http = ctx?.getHttpContext?.();
|
|
300
|
+
if (!http)
|
|
301
|
+
return;
|
|
302
|
+
const type = `${http.getUniqueHeader?.("content-type", "") ?? ""}`;
|
|
303
|
+
if (!/^application\/json\s*(;|$)/i.test(type.trim())) {
|
|
304
|
+
throw new UnsupportedMediaType();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* The token operation never links: with a logged-in session, an identity owned by another user is refused and a
|
|
309
|
+
* new or unowned one starts a fresh session
|
|
310
|
+
* @param ctx - operation context
|
|
311
|
+
* @param identity - verified identity
|
|
312
|
+
* @throws IdentLinkedElsewhere when the identity belongs to another user than the session one
|
|
313
|
+
*/
|
|
314
|
+
async leaveForeignSession(ctx, identity) {
|
|
315
|
+
if (!ctx?.getSession?.()?.isLogged())
|
|
316
|
+
return;
|
|
317
|
+
const current = ctx.getCurrentUserId();
|
|
318
|
+
const ident = await runAsSystem(() => useAuthentication().findIdent(identity.provider, identity.providerUid));
|
|
319
|
+
const owner = ident?.getUser()?.toString();
|
|
320
|
+
if (owner === current)
|
|
321
|
+
return;
|
|
322
|
+
if (owner) {
|
|
323
|
+
throw new IdentLinkedElsewhere();
|
|
324
|
+
}
|
|
325
|
+
ctx.newSession();
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* @param identity - identity returned by the subclass
|
|
329
|
+
* @returns the identity
|
|
330
|
+
* @throws TokenInvalid when it has no subject
|
|
331
|
+
*/
|
|
332
|
+
checkIdentity(identity) {
|
|
333
|
+
if (!identity?.providerUid || typeof identity.providerUid !== "string") {
|
|
334
|
+
throw new TokenInvalid("Provider returned no subject");
|
|
335
|
+
}
|
|
336
|
+
return identity;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* @param identity - identity returned by the subclass
|
|
340
|
+
* @returns the identity, always attributed to this provider
|
|
341
|
+
*/
|
|
342
|
+
ownIdentity(identity) {
|
|
343
|
+
return {
|
|
344
|
+
...identity,
|
|
345
|
+
provider: this.providerName,
|
|
346
|
+
amr: identity.amr?.length ? identity.amr : ["oauth"]
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Check a post-login target against `authorized_uris`: same origin, and the listed path or below it; an encoded
|
|
351
|
+
* slash or backslash in the path is refused
|
|
352
|
+
* @param redirect - candidate
|
|
353
|
+
* @returns the normalised url, undefined when not allowed
|
|
354
|
+
*/
|
|
355
|
+
allowedRedirect(redirect) {
|
|
356
|
+
if (typeof redirect !== "string" || redirect.length > REDIRECT_MAX || /%2f|%5c/i.test(redirect.split(/[?#]/)[0])) {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
const target = parseHttpUrl(redirect);
|
|
360
|
+
if (!target)
|
|
361
|
+
return undefined;
|
|
362
|
+
for (const uri of this.parameters.authorized_uris ?? []) {
|
|
363
|
+
const allowed = parseHttpUrl(uri);
|
|
364
|
+
if (!allowed || allowed.origin !== target.origin)
|
|
365
|
+
continue;
|
|
366
|
+
const path = allowed.pathname;
|
|
367
|
+
if (target.pathname === path || target.pathname.startsWith(path.endsWith("/") ? path : `${path}/`)) {
|
|
368
|
+
return target.href;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* @param ctx - web context
|
|
375
|
+
* @returns the callback url sent to the provider
|
|
376
|
+
*/
|
|
377
|
+
getRedirectUri(ctx) {
|
|
378
|
+
return this.parameters.redirect_uri ?? ctx.getHttpContext().getAbsoluteUrl(`${this.parameters.url ?? ""}/callback`);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Set (or clear, without value) the pending login cookie, scoped to the callback path: the path of the effective
|
|
382
|
+
* redirect_uri, which includes any deployment prefix (API Gateway stage, path-stripping proxy) the routes do not see
|
|
383
|
+
* @param ctx - web context
|
|
384
|
+
* @param redirectUri - callback url
|
|
385
|
+
* @param value - encrypted pending login
|
|
386
|
+
*/
|
|
387
|
+
sendPendingCookie(ctx, redirectUri, value) {
|
|
388
|
+
const callback = parseHttpUrl(redirectUri);
|
|
389
|
+
const options = new CookieOptions({
|
|
390
|
+
name: this.getCookieName(),
|
|
391
|
+
path: callback?.pathname || "/",
|
|
392
|
+
maxAge: value ? PENDING_TTL : 0,
|
|
393
|
+
sameSite: "lax",
|
|
394
|
+
httpOnly: true
|
|
395
|
+
}, ctx.getHttpContext());
|
|
396
|
+
// Secure when the request or the callback is https
|
|
397
|
+
options.secure = options.secure || callback?.protocol === "https:";
|
|
398
|
+
if (value) {
|
|
399
|
+
SecureCookie.sendCookie(ctx, this.getCookieName(), value, options);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
ctx.cookie(this.getCookieName(), "", options);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Read and clear the pending login cookie
|
|
407
|
+
* @param ctx - web context
|
|
408
|
+
* @returns the pending login, undefined when absent, invalid, expired or of another provider
|
|
409
|
+
*/
|
|
410
|
+
async consumePending(ctx) {
|
|
411
|
+
const raw = ctx.getHttpContext()?.getCookies?.()?.[this.getCookieName()];
|
|
412
|
+
// Always cleared, on the path it was set on (the callback): single use
|
|
413
|
+
this.sendPendingCookie(ctx, this.getRedirectUri(ctx));
|
|
414
|
+
if (typeof raw !== "string" || !raw)
|
|
415
|
+
return undefined;
|
|
416
|
+
let pending;
|
|
417
|
+
try {
|
|
418
|
+
pending = await useCrypto().decrypt(raw);
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
if (!pending ||
|
|
424
|
+
pending.provider !== this.providerName ||
|
|
425
|
+
typeof pending.state !== "string" ||
|
|
426
|
+
typeof pending.verifier !== "string" ||
|
|
427
|
+
typeof pending.nonce !== "string" ||
|
|
428
|
+
!(Date.now() <= pending.expires)) {
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
return pending;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Redirect without caching
|
|
435
|
+
* @param ctx - web context
|
|
436
|
+
* @param url - target
|
|
437
|
+
*/
|
|
438
|
+
redirect(ctx, url) {
|
|
439
|
+
ctx.setHeader("Cache-Control", "no-store");
|
|
440
|
+
ctx.redirect(url);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* @param ctx - web context
|
|
444
|
+
* @param reason - failure code
|
|
445
|
+
*/
|
|
446
|
+
fail(ctx, reason) {
|
|
447
|
+
this.redirect(ctx, withQuery(this.parameters.redirects.failure, { reason }));
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Start a login: redirect the browser to the provider
|
|
451
|
+
* @param ctx - web context
|
|
452
|
+
*/
|
|
453
|
+
async login(ctx) {
|
|
454
|
+
try {
|
|
455
|
+
const requested = ctx.parameter("redirect");
|
|
456
|
+
let redirect;
|
|
457
|
+
if (requested !== undefined && requested !== "") {
|
|
458
|
+
redirect = this.allowedRedirect(requested);
|
|
459
|
+
if (!redirect) {
|
|
460
|
+
this.fail(ctx, "REDIRECT_NOT_ALLOWED");
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const pending = {
|
|
465
|
+
provider: this.providerName,
|
|
466
|
+
state: randomSecret(32),
|
|
467
|
+
verifier: randomSecret(32),
|
|
468
|
+
nonce: randomSecret(24),
|
|
469
|
+
redirect,
|
|
470
|
+
redirectUri: this.getRedirectUri(ctx),
|
|
471
|
+
expires: Date.now() + PENDING_TTL * 1000
|
|
472
|
+
};
|
|
473
|
+
const url = await this.getAuthorizationUrl({
|
|
474
|
+
state: pending.state,
|
|
475
|
+
redirectUri: pending.redirectUri,
|
|
476
|
+
scope: this.parameters.scope ?? [],
|
|
477
|
+
codeChallenge: createHash("sha256").update(pending.verifier).digest("base64url"),
|
|
478
|
+
codeChallengeMethod: "S256",
|
|
479
|
+
nonce: pending.nonce
|
|
480
|
+
});
|
|
481
|
+
this.sendPendingCookie(ctx, pending.redirectUri, await useCrypto().encrypt(pending));
|
|
482
|
+
this.redirect(ctx, url);
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
this.log("ERROR", `Cannot start ${this.providerName} login:`, safeErrorReason(err));
|
|
486
|
+
this.fail(ctx, "OAUTH_ERROR");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Provider callback: verify the state, exchange the code, complete the login and redirect; never throws
|
|
491
|
+
* @param ctx - web context
|
|
492
|
+
*/
|
|
493
|
+
async callback(ctx) {
|
|
494
|
+
try {
|
|
495
|
+
const pending = await this.consumePending(ctx);
|
|
496
|
+
const state = ctx.parameter("state");
|
|
497
|
+
if (!pending || typeof state !== "string" || !sameSecret(state, pending.state)) {
|
|
498
|
+
this.fail(ctx, "STATE_MISMATCH");
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const code = ctx.parameter("code");
|
|
502
|
+
if (ctx.parameter("error") !== undefined || typeof code !== "string" || !code) {
|
|
503
|
+
this.fail(ctx, "PROVIDER_ERROR");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const result = await runWithContext(ctx, async () => {
|
|
507
|
+
const identity = this.ownIdentity(this.checkIdentity(await this.handleCallback({
|
|
508
|
+
code,
|
|
509
|
+
redirectUri: pending.redirectUri,
|
|
510
|
+
codeVerifier: pending.verifier,
|
|
511
|
+
nonce: pending.nonce
|
|
512
|
+
})));
|
|
513
|
+
const res = await useAuthentication().complete(identity);
|
|
514
|
+
await this.onAuthenticated(identity, res, "callback");
|
|
515
|
+
return res;
|
|
516
|
+
});
|
|
517
|
+
const target = pending.redirect ?? this.parameters.redirects.success ?? "/";
|
|
518
|
+
this.redirect(ctx, result.status === "mfa_required" ? withQuery(target, { mfa: "required" }) : target);
|
|
519
|
+
}
|
|
520
|
+
catch (err) {
|
|
521
|
+
const code = httpErrorCode(err);
|
|
522
|
+
if (!code) {
|
|
523
|
+
this.log("ERROR", `${this.providerName} callback failed:`, safeErrorReason(err));
|
|
524
|
+
}
|
|
525
|
+
this.fail(ctx, code ?? "OAUTH_ERROR");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
constructor() {
|
|
529
|
+
super(...arguments);
|
|
530
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
(() => {
|
|
534
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
535
|
+
_login_decorators = [Route(".{?redirect?}", ["GET"], {
|
|
536
|
+
get: {
|
|
537
|
+
summary: "Start an OAuth login",
|
|
538
|
+
description: "Redirects to the provider; `redirect` must match the provider authorized_uris",
|
|
539
|
+
responses: { "302": { description: "Redirect to the provider, or to redirects.failure" } }
|
|
540
|
+
}
|
|
541
|
+
})];
|
|
542
|
+
_callback_decorators = [Route("./callback{?code?,state?,error?}", ["GET"], {
|
|
543
|
+
get: {
|
|
544
|
+
summary: "OAuth callback",
|
|
545
|
+
description: "Completes the login started by the login route",
|
|
546
|
+
responses: { "302": { description: "Redirect to the login target, or to redirects.failure?reason=CODE" } }
|
|
547
|
+
}
|
|
548
|
+
})];
|
|
549
|
+
__esDecorate(_a, null, _login_decorators, { kind: "method", name: "login", static: false, private: false, access: { has: obj => "login" in obj, get: obj => obj.login }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
550
|
+
__esDecorate(_a, null, _callback_decorators, { kind: "method", name: "callback", static: false, private: false, access: { has: obj => "callback" in obj, get: obj => obj.callback }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
551
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
552
|
+
})(),
|
|
553
|
+
_a.Parameters = OAuthProviderParameters,
|
|
554
|
+
_a;
|
|
555
|
+
})();
|
|
556
|
+
export { OAuthProvider };
|
|
557
|
+
//# sourceMappingURL=oauth.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.service.js","sourceRoot":"","sources":["../../src/oauth/oauth.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ProviderInfo, type ResolvedIdentity, type Service } from "@webda/core";
|
|
2
|
+
import type { Authentication } from "./authentication.service.js";
|
|
3
|
+
/** A login method plugged into {@link Authentication} */
|
|
4
|
+
export interface AuthProvider extends Service {
|
|
5
|
+
/** Unique provider name, used in ident keys ("email", "google") */
|
|
6
|
+
readonly providerName: string;
|
|
7
|
+
/**
|
|
8
|
+
* @returns what clients see in Auth.Providers
|
|
9
|
+
*/
|
|
10
|
+
getPublicInfo(): ProviderInfo;
|
|
11
|
+
/**
|
|
12
|
+
* Email rules of this provider; when absent, Authentication reads the provider service parameters
|
|
13
|
+
* @returns the email policy
|
|
14
|
+
*/
|
|
15
|
+
getEmailPolicy?(): ProviderEmailPolicy;
|
|
16
|
+
}
|
|
17
|
+
/** Per-provider rules on asserted emails; read from the provider service parameters */
|
|
18
|
+
export interface ProviderEmailPolicy {
|
|
19
|
+
/**
|
|
20
|
+
* Only identities whose email domain is listed may log in (case-insensitive, exact domain match, no wildcard).
|
|
21
|
+
* The email must also be verified and trusted (see trustEmailVerification). An empty list refuses everyone.
|
|
22
|
+
* Undefined means all domains.
|
|
23
|
+
*/
|
|
24
|
+
allowedEmailDomains?: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Whether the provider's emailVerified claim is believed: true (default) = all domains, false = never,
|
|
27
|
+
* string[] = only these domains.
|
|
28
|
+
*/
|
|
29
|
+
trustEmailVerification?: boolean | string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @param email - an email address
|
|
33
|
+
* @returns the lowercased part after the last "@", undefined if none
|
|
34
|
+
*/
|
|
35
|
+
export declare function emailDomain(email: string): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Apply a provider email policy to a resolved identity without mutating it
|
|
38
|
+
* @param identity - identity asserted by the provider
|
|
39
|
+
* @param policy - the provider policy, if any
|
|
40
|
+
* @returns the identity to use (a copy with emailVerified forced to false when the claim is not trusted)
|
|
41
|
+
* @throws EmailDomainNotAllowed when the email is missing or its domain is not allowed
|
|
42
|
+
*/
|
|
43
|
+
export declare function applyEmailPolicy(identity: ResolvedIdentity, policy: ProviderEmailPolicy | undefined): ResolvedIdentity;
|
|
44
|
+
/**
|
|
45
|
+
* @param service - any service
|
|
46
|
+
* @returns true when it implements AuthProvider
|
|
47
|
+
*/
|
|
48
|
+
export declare function isAuthProvider(service: any): service is AuthProvider;
|
|
49
|
+
/**
|
|
50
|
+
* @returns the Authentication service
|
|
51
|
+
*/
|
|
52
|
+
export declare function useAuthentication(): Authentication;
|
|
53
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,KAAK,OAAO,EAAqB,MAAM,aAAa,CAAC;AACxG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGlE,yDAAyD;AACzD,MAAM,WAAW,YAAa,SAAQ,OAAO;IAC3C,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;OAEG;IACH,aAAa,IAAI,YAAY,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,IAAI,mBAAmB,CAAC;CACxC;AAED,uFAAuF;AACvF,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ7D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,mBAAmB,GAAG,SAAS,GACtC,gBAAgB,CAmBlB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,IAAI,YAAY,CAEpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAElD"}
|