@backstage/plugin-auth-node 0.5.2 → 0.5.3-next.1
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/CHANGELOG.md +26 -0
- package/dist/extensions/AuthOwnershipResolutionExtensionPoint.cjs.js +10 -0
- package/dist/extensions/AuthOwnershipResolutionExtensionPoint.cjs.js.map +1 -0
- package/dist/extensions/AuthProvidersExtensionPoint.cjs.js +10 -0
- package/dist/extensions/AuthProvidersExtensionPoint.cjs.js.map +1 -0
- package/dist/flow/sendWebMessageResponse.cjs.js +41 -0
- package/dist/flow/sendWebMessageResponse.cjs.js.map +1 -0
- package/dist/identity/DefaultIdentityClient.cjs.js +103 -0
- package/dist/identity/DefaultIdentityClient.cjs.js.map +1 -0
- package/dist/identity/IdentityClient.cjs.js +27 -0
- package/dist/identity/IdentityClient.cjs.js.map +1 -0
- package/dist/identity/getBearerTokenFromAuthorizationHeader.cjs.js +12 -0
- package/dist/identity/getBearerTokenFromAuthorizationHeader.cjs.js.map +1 -0
- package/dist/identity/prepareBackstageIdentityResponse.cjs.js +36 -0
- package/dist/identity/prepareBackstageIdentityResponse.cjs.js.map +1 -0
- package/dist/index.cjs.js +48 -1063
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/oauth/CookieScopeManager.cjs.js +90 -0
- package/dist/oauth/CookieScopeManager.cjs.js.map +1 -0
- package/dist/oauth/OAuthCookieManager.cjs.js +87 -0
- package/dist/oauth/OAuthCookieManager.cjs.js.map +1 -0
- package/dist/oauth/OAuthEnvironmentHandler.cjs.js +64 -0
- package/dist/oauth/OAuthEnvironmentHandler.cjs.js.map +1 -0
- package/dist/oauth/PassportOAuthAuthenticatorHelper.cjs.js +69 -0
- package/dist/oauth/PassportOAuthAuthenticatorHelper.cjs.js.map +1 -0
- package/dist/oauth/createOAuthProviderFactory.cjs.js +37 -0
- package/dist/oauth/createOAuthProviderFactory.cjs.js.map +1 -0
- package/dist/oauth/createOAuthRouteHandlers.cjs.js +208 -0
- package/dist/oauth/createOAuthRouteHandlers.cjs.js.map +1 -0
- package/dist/oauth/state.cjs.js +31 -0
- package/dist/oauth/state.cjs.js.map +1 -0
- package/dist/oauth/types.cjs.js +8 -0
- package/dist/oauth/types.cjs.js.map +1 -0
- package/dist/passport/PassportHelpers.cjs.js +144 -0
- package/dist/passport/PassportHelpers.cjs.js.map +1 -0
- package/dist/proxy/createProxyAuthProviderFactory.cjs.js +32 -0
- package/dist/proxy/createProxyAuthProviderFactory.cjs.js.map +1 -0
- package/dist/proxy/createProxyRouteHandlers.cjs.js +39 -0
- package/dist/proxy/createProxyRouteHandlers.cjs.js.map +1 -0
- package/dist/proxy/types.cjs.js +8 -0
- package/dist/proxy/types.cjs.js.map +1 -0
- package/dist/sign-in/commonSignInResolvers.cjs.js +70 -0
- package/dist/sign-in/commonSignInResolvers.cjs.js.map +1 -0
- package/dist/sign-in/createSignInResolverFactory.cjs.js +37 -0
- package/dist/sign-in/createSignInResolverFactory.cjs.js.map +1 -0
- package/dist/sign-in/readDeclarativeSignInResolver.cjs.js +38 -0
- package/dist/sign-in/readDeclarativeSignInResolver.cjs.js.map +1 -0
- package/dist/types.cjs.js +17 -0
- package/dist/types.cjs.js.map +1 -0
- package/package.json +11 -10
package/dist/index.cjs.js
CHANGED
|
@@ -1,1067 +1,52 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
res.setHeader("Content-Security-Policy", `script-src 'sha256-${hash}'`);
|
|
51
|
-
res.end(`<html><body><script>${script}<\/script></body></html>`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseJwtPayload(token) {
|
|
55
|
-
const [_header, payload, _signature] = token.split(".");
|
|
56
|
-
return JSON.parse(Buffer.from(payload, "base64").toString());
|
|
57
|
-
}
|
|
58
|
-
function prepareBackstageIdentityResponse(result) {
|
|
59
|
-
if (!result.token) {
|
|
60
|
-
throw new errors.InputError(`Identity response must return a token`);
|
|
61
|
-
}
|
|
62
|
-
const { sub, ent = [], exp: expStr } = parseJwtPayload(result.token);
|
|
63
|
-
if (!sub) {
|
|
64
|
-
throw new errors.InputError(
|
|
65
|
-
`Identity response must return a token with subject claim`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
const expAt = Number(expStr);
|
|
69
|
-
const exp = expAt ? Math.round(expAt - Date.now() / 1e3) : void 0;
|
|
70
|
-
if (exp && exp < 0) {
|
|
71
|
-
throw new errors.InputError(`Identity response must not return an expired token`);
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
...result,
|
|
75
|
-
expiresInSeconds: exp,
|
|
76
|
-
identity: {
|
|
77
|
-
type: "user",
|
|
78
|
-
userEntityRef: sub,
|
|
79
|
-
ownershipEntityRefs: ent
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function getBearerTokenFromAuthorizationHeader(authorizationHeader) {
|
|
85
|
-
if (typeof authorizationHeader !== "string") {
|
|
86
|
-
return void 0;
|
|
87
|
-
}
|
|
88
|
-
const matches = authorizationHeader.match(/^Bearer[ ]+(\S+)$/i);
|
|
89
|
-
return matches?.[1];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const CLOCK_MARGIN_S = 10;
|
|
93
|
-
class DefaultIdentityClient {
|
|
94
|
-
discovery;
|
|
95
|
-
issuer;
|
|
96
|
-
algorithms;
|
|
97
|
-
keyStore;
|
|
98
|
-
keyStoreUpdated = 0;
|
|
99
|
-
/**
|
|
100
|
-
* Create a new {@link DefaultIdentityClient} instance.
|
|
101
|
-
*/
|
|
102
|
-
static create(options) {
|
|
103
|
-
return new DefaultIdentityClient(options);
|
|
104
|
-
}
|
|
105
|
-
constructor(options) {
|
|
106
|
-
this.discovery = options.discovery;
|
|
107
|
-
this.issuer = options.issuer;
|
|
108
|
-
this.algorithms = options.hasOwnProperty("algorithms") ? options.algorithms : ["ES256"];
|
|
109
|
-
}
|
|
110
|
-
async getIdentity(options) {
|
|
111
|
-
const {
|
|
112
|
-
request: { headers }
|
|
113
|
-
} = options;
|
|
114
|
-
if (!headers.authorization) {
|
|
115
|
-
return void 0;
|
|
116
|
-
}
|
|
117
|
-
try {
|
|
118
|
-
return await this.authenticate(
|
|
119
|
-
getBearerTokenFromAuthorizationHeader(headers.authorization)
|
|
120
|
-
);
|
|
121
|
-
} catch (e) {
|
|
122
|
-
throw new errors.AuthenticationError(e.message);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Verifies the given backstage identity token
|
|
127
|
-
* Returns a BackstageIdentity (user) matching the token.
|
|
128
|
-
* The method throws an error if verification fails.
|
|
129
|
-
*
|
|
130
|
-
* @deprecated You should start to use getIdentity instead of authenticate to retrieve the user
|
|
131
|
-
* identity.
|
|
132
|
-
*/
|
|
133
|
-
async authenticate(token) {
|
|
134
|
-
if (!token) {
|
|
135
|
-
throw new errors.AuthenticationError("No token specified");
|
|
136
|
-
}
|
|
137
|
-
await this.refreshKeyStore(token);
|
|
138
|
-
if (!this.keyStore) {
|
|
139
|
-
throw new errors.AuthenticationError("No keystore exists");
|
|
140
|
-
}
|
|
141
|
-
const decoded = await jose.jwtVerify(token, this.keyStore, {
|
|
142
|
-
algorithms: this.algorithms,
|
|
143
|
-
audience: "backstage",
|
|
144
|
-
issuer: this.issuer
|
|
145
|
-
});
|
|
146
|
-
if (!decoded.payload.sub) {
|
|
147
|
-
throw new errors.AuthenticationError("No user sub found in token");
|
|
148
|
-
}
|
|
149
|
-
const user = {
|
|
150
|
-
token,
|
|
151
|
-
identity: {
|
|
152
|
-
type: "user",
|
|
153
|
-
userEntityRef: decoded.payload.sub,
|
|
154
|
-
ownershipEntityRefs: decoded.payload.ent ? decoded.payload.ent : []
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
return user;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* If the last keystore refresh is stale, update the keystore URL to the latest
|
|
161
|
-
*/
|
|
162
|
-
async refreshKeyStore(rawJwtToken) {
|
|
163
|
-
const payload = await jose.decodeJwt(rawJwtToken);
|
|
164
|
-
const header = await jose.decodeProtectedHeader(rawJwtToken);
|
|
165
|
-
let keyStoreHasKey;
|
|
166
|
-
try {
|
|
167
|
-
if (this.keyStore) {
|
|
168
|
-
const [_, rawPayload, rawSignature] = rawJwtToken.split(".");
|
|
169
|
-
keyStoreHasKey = await this.keyStore(header, {
|
|
170
|
-
payload: rawPayload,
|
|
171
|
-
signature: rawSignature
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
} catch (error) {
|
|
175
|
-
keyStoreHasKey = false;
|
|
176
|
-
}
|
|
177
|
-
const issuedAfterLastRefresh = payload?.iat && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
|
|
178
|
-
if (!this.keyStore || !keyStoreHasKey && issuedAfterLastRefresh) {
|
|
179
|
-
const url = await this.discovery.getBaseUrl("auth");
|
|
180
|
-
const endpoint = new URL(`${url}/.well-known/jwks.json`);
|
|
181
|
-
this.keyStore = jose.createRemoteJWKSet(endpoint);
|
|
182
|
-
this.keyStoreUpdated = Date.now() / 1e3;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
class IdentityClient {
|
|
188
|
-
defaultIdentityClient;
|
|
189
|
-
static create(options) {
|
|
190
|
-
return new IdentityClient(DefaultIdentityClient.create(options));
|
|
191
|
-
}
|
|
192
|
-
constructor(defaultIdentityClient) {
|
|
193
|
-
this.defaultIdentityClient = defaultIdentityClient;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Verifies the given backstage identity token
|
|
197
|
-
* Returns a BackstageIdentity (user) matching the token.
|
|
198
|
-
* The method throws an error if verification fails.
|
|
199
|
-
*
|
|
200
|
-
* @deprecated You should start to use IdentityApi#getIdentity instead of authenticate
|
|
201
|
-
* to retrieve the user identity.
|
|
202
|
-
*/
|
|
203
|
-
async authenticate(token) {
|
|
204
|
-
return await this.defaultIdentityClient.authenticate(token);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function encodeOAuthState(state) {
|
|
209
|
-
const stateString = new URLSearchParams(
|
|
210
|
-
pickBy__default.default(state, (value) => value !== void 0)
|
|
211
|
-
).toString();
|
|
212
|
-
return Buffer.from(stateString, "utf-8").toString("hex");
|
|
213
|
-
}
|
|
214
|
-
function decodeOAuthState(encodedState) {
|
|
215
|
-
const state = Object.fromEntries(
|
|
216
|
-
new URLSearchParams(Buffer.from(encodedState, "hex").toString("utf-8"))
|
|
217
|
-
);
|
|
218
|
-
if (!state.env || state.env?.length === 0) {
|
|
219
|
-
throw new errors.NotAllowedError("OAuth state is invalid, missing env");
|
|
220
|
-
}
|
|
221
|
-
if (!state.nonce || state.nonce?.length === 0) {
|
|
222
|
-
throw new errors.NotAllowedError("OAuth state is invalid, missing nonce");
|
|
223
|
-
}
|
|
224
|
-
return state;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const THOUSAND_DAYS_MS = 1e3 * 24 * 60 * 60 * 1e3;
|
|
228
|
-
const TEN_MINUTES_MS = 600 * 1e3;
|
|
229
|
-
const defaultCookieConfigurer = ({
|
|
230
|
-
callbackUrl,
|
|
231
|
-
providerId,
|
|
232
|
-
appOrigin
|
|
233
|
-
}) => {
|
|
234
|
-
const { hostname: domain, pathname, protocol } = new URL(callbackUrl);
|
|
235
|
-
const secure = protocol === "https:";
|
|
236
|
-
let sameSite = "lax";
|
|
237
|
-
if (new URL(appOrigin).hostname !== domain && secure) {
|
|
238
|
-
sameSite = "none";
|
|
239
|
-
}
|
|
240
|
-
const path = pathname.endsWith(`${providerId}/handler/frame`) ? pathname.slice(0, -"/handler/frame".length) : `${pathname}/${providerId}`;
|
|
241
|
-
return { domain, path, secure, sameSite };
|
|
242
|
-
};
|
|
243
|
-
class OAuthCookieManager {
|
|
244
|
-
constructor(options) {
|
|
245
|
-
this.options = options;
|
|
246
|
-
this.cookieConfigurer = options.cookieConfigurer ?? defaultCookieConfigurer;
|
|
247
|
-
this.nonceCookie = `${options.providerId}-nonce`;
|
|
248
|
-
this.refreshTokenCookie = `${options.providerId}-refresh-token`;
|
|
249
|
-
this.grantedScopeCookie = `${options.providerId}-granted-scope`;
|
|
250
|
-
}
|
|
251
|
-
cookieConfigurer;
|
|
252
|
-
nonceCookie;
|
|
253
|
-
refreshTokenCookie;
|
|
254
|
-
grantedScopeCookie;
|
|
255
|
-
getConfig(origin, pathSuffix = "") {
|
|
256
|
-
const cookieConfig = this.cookieConfigurer({
|
|
257
|
-
providerId: this.options.providerId,
|
|
258
|
-
baseUrl: this.options.baseUrl,
|
|
259
|
-
callbackUrl: this.options.callbackUrl,
|
|
260
|
-
appOrigin: origin ?? this.options.defaultAppOrigin
|
|
261
|
-
});
|
|
262
|
-
return {
|
|
263
|
-
httpOnly: true,
|
|
264
|
-
sameSite: "lax",
|
|
265
|
-
...cookieConfig,
|
|
266
|
-
path: cookieConfig.path + pathSuffix
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
setNonce(res, nonce, origin) {
|
|
270
|
-
res.cookie(this.nonceCookie, nonce, {
|
|
271
|
-
maxAge: TEN_MINUTES_MS,
|
|
272
|
-
...this.getConfig(origin, "/handler")
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
setRefreshToken(res, refreshToken, origin) {
|
|
276
|
-
res.cookie(this.refreshTokenCookie, refreshToken, {
|
|
277
|
-
maxAge: THOUSAND_DAYS_MS,
|
|
278
|
-
...this.getConfig(origin)
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
removeRefreshToken(res, origin) {
|
|
282
|
-
res.cookie(this.refreshTokenCookie, "", {
|
|
283
|
-
maxAge: 0,
|
|
284
|
-
...this.getConfig(origin)
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
removeGrantedScopes(res, origin) {
|
|
288
|
-
res.cookie(this.grantedScopeCookie, "", {
|
|
289
|
-
maxAge: 0,
|
|
290
|
-
...this.getConfig(origin)
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
setGrantedScopes(res, scope, origin) {
|
|
294
|
-
res.cookie(this.grantedScopeCookie, scope, {
|
|
295
|
-
maxAge: THOUSAND_DAYS_MS,
|
|
296
|
-
...this.getConfig(origin)
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
getNonce(req) {
|
|
300
|
-
return req.cookies[this.nonceCookie];
|
|
301
|
-
}
|
|
302
|
-
getRefreshToken(req) {
|
|
303
|
-
return req.cookies[this.refreshTokenCookie];
|
|
304
|
-
}
|
|
305
|
-
getGrantedScopes(req) {
|
|
306
|
-
return req.cookies[this.grantedScopeCookie];
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function reqRes(req) {
|
|
311
|
-
const res = req.res;
|
|
312
|
-
if (!res) {
|
|
313
|
-
throw new Error("No response object found in request");
|
|
314
|
-
}
|
|
315
|
-
return res;
|
|
316
|
-
}
|
|
317
|
-
const defaultTransform = ({ requested, granted, required, additional }) => {
|
|
318
|
-
return [...requested, ...granted, ...required, ...additional];
|
|
319
|
-
};
|
|
320
|
-
function splitScope(scope) {
|
|
321
|
-
if (!scope) {
|
|
322
|
-
return [];
|
|
323
|
-
}
|
|
324
|
-
return scope.split(/[\s|,]/).filter(Boolean);
|
|
325
|
-
}
|
|
326
|
-
class CookieScopeManager {
|
|
327
|
-
constructor(scopeTransform, cookieManager) {
|
|
328
|
-
this.scopeTransform = scopeTransform;
|
|
329
|
-
this.cookieManager = cookieManager;
|
|
330
|
-
}
|
|
331
|
-
static create(options) {
|
|
332
|
-
const { authenticator, config } = options;
|
|
333
|
-
const shouldPersistScopes = authenticator.scopes?.persist ?? authenticator.shouldPersistScopes ?? false;
|
|
334
|
-
const configScopes = typeof config?.getOptional("additionalScopes") === "string" ? splitScope(config.getString("additionalScopes")) : config?.getOptionalStringArray("additionalScopes") ?? [];
|
|
335
|
-
const transform = authenticator?.scopes?.transform ?? defaultTransform;
|
|
336
|
-
const additional = [...configScopes, ...options.additionalScopes ?? []];
|
|
337
|
-
const required = authenticator?.scopes?.required ?? [];
|
|
338
|
-
return new CookieScopeManager(
|
|
339
|
-
(requested, granted) => Array.from(
|
|
340
|
-
new Set(transform({ required, additional, requested, granted }))
|
|
341
|
-
).join(" "),
|
|
342
|
-
shouldPersistScopes ? options.cookieManager : void 0
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
async start(req) {
|
|
346
|
-
const requestScope = splitScope(req.query.scope?.toString());
|
|
347
|
-
const grantedScope = splitScope(this.cookieManager?.getGrantedScopes(req));
|
|
348
|
-
const scope = this.scopeTransform(requestScope, grantedScope);
|
|
349
|
-
if (this.cookieManager) {
|
|
350
|
-
return {
|
|
351
|
-
scope,
|
|
352
|
-
scopeState: { scope }
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
return { scope };
|
|
356
|
-
}
|
|
357
|
-
async handleCallback(req, ctx) {
|
|
358
|
-
if (!this.cookieManager) {
|
|
359
|
-
return Array.from(splitScope(ctx.result.session.scope)).join(" ");
|
|
360
|
-
}
|
|
361
|
-
const scope = ctx.state.scope;
|
|
362
|
-
if (scope === void 0) {
|
|
363
|
-
throw new errors.AuthenticationError("No scope found in OAuth state");
|
|
364
|
-
}
|
|
365
|
-
this.cookieManager.setGrantedScopes(reqRes(req), scope, ctx.origin);
|
|
366
|
-
return scope;
|
|
367
|
-
}
|
|
368
|
-
async clear(req) {
|
|
369
|
-
if (this.cookieManager) {
|
|
370
|
-
this.cookieManager.removeGrantedScopes(reqRes(req), req.get("origin"));
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
async refresh(req) {
|
|
374
|
-
const requestScope = splitScope(req.query.scope?.toString());
|
|
375
|
-
const grantedScope = splitScope(this.cookieManager?.getGrantedScopes(req));
|
|
376
|
-
const scope = this.scopeTransform(requestScope, grantedScope);
|
|
377
|
-
return {
|
|
378
|
-
scope,
|
|
379
|
-
commit: async (result) => {
|
|
380
|
-
if (this.cookieManager) {
|
|
381
|
-
this.cookieManager.setGrantedScopes(
|
|
382
|
-
reqRes(req),
|
|
383
|
-
scope,
|
|
384
|
-
req.get("origin")
|
|
385
|
-
);
|
|
386
|
-
return scope;
|
|
387
|
-
}
|
|
388
|
-
return Array.from(splitScope(result.session.scope)).join(" ");
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function createOAuthRouteHandlers(options) {
|
|
395
|
-
const {
|
|
396
|
-
authenticator,
|
|
397
|
-
config,
|
|
398
|
-
baseUrl,
|
|
399
|
-
appUrl,
|
|
400
|
-
providerId,
|
|
401
|
-
isOriginAllowed,
|
|
402
|
-
cookieConfigurer,
|
|
403
|
-
resolverContext,
|
|
404
|
-
signInResolver
|
|
405
|
-
} = options;
|
|
406
|
-
const defaultAppOrigin = new url.URL(appUrl).origin;
|
|
407
|
-
const callbackUrl = config.getOptionalString("callbackUrl") ?? `${baseUrl}/${providerId}/handler/frame`;
|
|
408
|
-
const stateTransform = options.stateTransform ?? ((state) => ({ state }));
|
|
409
|
-
const profileTransform = options.profileTransform ?? authenticator.defaultProfileTransform;
|
|
410
|
-
const authenticatorCtx = authenticator.initialize({ config, callbackUrl });
|
|
411
|
-
const cookieManager = new OAuthCookieManager({
|
|
412
|
-
baseUrl,
|
|
413
|
-
callbackUrl,
|
|
414
|
-
defaultAppOrigin,
|
|
415
|
-
providerId,
|
|
416
|
-
cookieConfigurer
|
|
417
|
-
});
|
|
418
|
-
const scopeManager = CookieScopeManager.create({
|
|
419
|
-
config,
|
|
420
|
-
authenticator,
|
|
421
|
-
cookieManager,
|
|
422
|
-
additionalScopes: options.additionalScopes
|
|
423
|
-
});
|
|
424
|
-
return {
|
|
425
|
-
async start(req, res) {
|
|
426
|
-
const env = req.query.env?.toString();
|
|
427
|
-
const origin = req.query.origin?.toString();
|
|
428
|
-
const redirectUrl = req.query.redirectUrl?.toString();
|
|
429
|
-
const flow = req.query.flow?.toString();
|
|
430
|
-
if (!env) {
|
|
431
|
-
throw new errors.InputError("No env provided in request query parameters");
|
|
432
|
-
}
|
|
433
|
-
const nonce = crypto__default.default.randomBytes(16).toString("base64");
|
|
434
|
-
cookieManager.setNonce(res, nonce, origin);
|
|
435
|
-
const { scope, scopeState } = await scopeManager.start(req);
|
|
436
|
-
const state = { nonce, env, origin, redirectUrl, flow, ...scopeState };
|
|
437
|
-
const { state: transformedState } = await stateTransform(state, { req });
|
|
438
|
-
const { url, status } = await options.authenticator.start(
|
|
439
|
-
{
|
|
440
|
-
req,
|
|
441
|
-
scope,
|
|
442
|
-
state: encodeOAuthState(transformedState)
|
|
443
|
-
},
|
|
444
|
-
authenticatorCtx
|
|
445
|
-
);
|
|
446
|
-
res.statusCode = status || 302;
|
|
447
|
-
res.setHeader("Location", url);
|
|
448
|
-
res.setHeader("Content-Length", "0");
|
|
449
|
-
res.end();
|
|
450
|
-
},
|
|
451
|
-
async frameHandler(req, res) {
|
|
452
|
-
let origin = defaultAppOrigin;
|
|
453
|
-
try {
|
|
454
|
-
const state = decodeOAuthState(req.query.state?.toString() ?? "");
|
|
455
|
-
if (state.origin) {
|
|
456
|
-
try {
|
|
457
|
-
origin = new url.URL(state.origin).origin;
|
|
458
|
-
} catch {
|
|
459
|
-
throw new errors.NotAllowedError("App origin is invalid, failed to parse");
|
|
460
|
-
}
|
|
461
|
-
if (!isOriginAllowed(origin)) {
|
|
462
|
-
throw new errors.NotAllowedError(`Origin '${origin}' is not allowed`);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
const cookieNonce = cookieManager.getNonce(req);
|
|
466
|
-
const stateNonce = state.nonce;
|
|
467
|
-
if (!cookieNonce) {
|
|
468
|
-
throw new errors.NotAllowedError("Auth response is missing cookie nonce");
|
|
469
|
-
}
|
|
470
|
-
if (cookieNonce !== stateNonce) {
|
|
471
|
-
throw new errors.NotAllowedError("Invalid nonce");
|
|
472
|
-
}
|
|
473
|
-
const result = await authenticator.authenticate(
|
|
474
|
-
{ req },
|
|
475
|
-
authenticatorCtx
|
|
476
|
-
);
|
|
477
|
-
const { profile } = await profileTransform(result, resolverContext);
|
|
478
|
-
const signInResult = signInResolver && await signInResolver({ profile, result }, resolverContext);
|
|
479
|
-
const grantedScopes = await scopeManager.handleCallback(req, {
|
|
480
|
-
result,
|
|
481
|
-
state,
|
|
482
|
-
origin
|
|
483
|
-
});
|
|
484
|
-
const response = {
|
|
485
|
-
profile,
|
|
486
|
-
providerInfo: {
|
|
487
|
-
idToken: result.session.idToken,
|
|
488
|
-
accessToken: result.session.accessToken,
|
|
489
|
-
scope: grantedScopes,
|
|
490
|
-
expiresInSeconds: result.session.expiresInSeconds
|
|
491
|
-
},
|
|
492
|
-
...signInResult && {
|
|
493
|
-
backstageIdentity: prepareBackstageIdentityResponse(signInResult)
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
if (result.session.refreshToken) {
|
|
497
|
-
cookieManager.setRefreshToken(
|
|
498
|
-
res,
|
|
499
|
-
result.session.refreshToken,
|
|
500
|
-
origin
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
if (state.flow === "redirect") {
|
|
504
|
-
if (!state.redirectUrl) {
|
|
505
|
-
throw new errors.InputError(
|
|
506
|
-
"No redirectUrl provided in request query parameters"
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
res.redirect(state.redirectUrl);
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
sendWebMessageResponse(res, origin, {
|
|
513
|
-
type: "authorization_response",
|
|
514
|
-
response
|
|
515
|
-
});
|
|
516
|
-
} catch (error) {
|
|
517
|
-
const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
518
|
-
sendWebMessageResponse(res, origin, {
|
|
519
|
-
type: "authorization_response",
|
|
520
|
-
error: { name, message }
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
},
|
|
524
|
-
async logout(req, res) {
|
|
525
|
-
if (req.header("X-Requested-With") !== "XMLHttpRequest") {
|
|
526
|
-
throw new errors.AuthenticationError("Invalid X-Requested-With header");
|
|
527
|
-
}
|
|
528
|
-
if (authenticator.logout) {
|
|
529
|
-
const refreshToken = cookieManager.getRefreshToken(req);
|
|
530
|
-
await authenticator.logout({ req, refreshToken }, authenticatorCtx);
|
|
531
|
-
}
|
|
532
|
-
cookieManager.removeRefreshToken(res, req.get("origin"));
|
|
533
|
-
await scopeManager.clear(req);
|
|
534
|
-
res.status(200).end();
|
|
535
|
-
},
|
|
536
|
-
async refresh(req, res) {
|
|
537
|
-
if (req.header("X-Requested-With") !== "XMLHttpRequest") {
|
|
538
|
-
throw new errors.AuthenticationError("Invalid X-Requested-With header");
|
|
539
|
-
}
|
|
540
|
-
try {
|
|
541
|
-
const refreshToken = cookieManager.getRefreshToken(req);
|
|
542
|
-
if (!refreshToken) {
|
|
543
|
-
throw new errors.InputError("Missing session cookie");
|
|
544
|
-
}
|
|
545
|
-
const scopeRefresh = await scopeManager.refresh(req);
|
|
546
|
-
const result = await authenticator.refresh(
|
|
547
|
-
{ req, scope: scopeRefresh.scope, refreshToken },
|
|
548
|
-
authenticatorCtx
|
|
549
|
-
);
|
|
550
|
-
const grantedScope = await scopeRefresh.commit(result);
|
|
551
|
-
const { profile } = await profileTransform(result, resolverContext);
|
|
552
|
-
const newRefreshToken = result.session.refreshToken;
|
|
553
|
-
if (newRefreshToken && newRefreshToken !== refreshToken) {
|
|
554
|
-
cookieManager.setRefreshToken(
|
|
555
|
-
res,
|
|
556
|
-
newRefreshToken,
|
|
557
|
-
req.get("origin")
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
const response = {
|
|
561
|
-
profile,
|
|
562
|
-
providerInfo: {
|
|
563
|
-
idToken: result.session.idToken,
|
|
564
|
-
accessToken: result.session.accessToken,
|
|
565
|
-
scope: grantedScope,
|
|
566
|
-
expiresInSeconds: result.session.expiresInSeconds
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
if (signInResolver) {
|
|
570
|
-
const identity = await signInResolver(
|
|
571
|
-
{ profile, result },
|
|
572
|
-
resolverContext
|
|
573
|
-
);
|
|
574
|
-
response.backstageIdentity = prepareBackstageIdentityResponse(identity);
|
|
575
|
-
}
|
|
576
|
-
res.status(200).json(response);
|
|
577
|
-
} catch (error) {
|
|
578
|
-
throw new errors.AuthenticationError("Refresh failed", error);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
class PassportHelpers {
|
|
585
|
-
constructor() {
|
|
586
|
-
}
|
|
587
|
-
static transformProfile = (profile, idToken) => {
|
|
588
|
-
let email = void 0;
|
|
589
|
-
if (profile.emails && profile.emails.length > 0) {
|
|
590
|
-
const [firstEmail] = profile.emails;
|
|
591
|
-
email = firstEmail.value;
|
|
592
|
-
} else if (profile.email) {
|
|
593
|
-
email = profile.email;
|
|
594
|
-
}
|
|
595
|
-
let picture = void 0;
|
|
596
|
-
if (profile.avatarUrl) {
|
|
597
|
-
picture = profile.avatarUrl;
|
|
598
|
-
} else if (profile.photos && profile.photos.length > 0) {
|
|
599
|
-
const [firstPhoto] = profile.photos;
|
|
600
|
-
picture = firstPhoto.value;
|
|
601
|
-
} else if (profile.photo) {
|
|
602
|
-
picture = profile.photo;
|
|
603
|
-
}
|
|
604
|
-
let displayName = profile.displayName ?? profile.username ?? profile.id;
|
|
605
|
-
if ((!email || !picture || !displayName) && idToken) {
|
|
606
|
-
try {
|
|
607
|
-
const decoded = jose.decodeJwt(idToken);
|
|
608
|
-
if (!email && decoded.email) {
|
|
609
|
-
email = decoded.email;
|
|
610
|
-
}
|
|
611
|
-
if (!picture && decoded.picture) {
|
|
612
|
-
picture = decoded.picture;
|
|
613
|
-
}
|
|
614
|
-
if (!displayName && decoded.name) {
|
|
615
|
-
displayName = decoded.name;
|
|
616
|
-
}
|
|
617
|
-
} catch (e) {
|
|
618
|
-
throw new Error(`Failed to parse id token and get profile info, ${e}`);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return {
|
|
622
|
-
email,
|
|
623
|
-
picture,
|
|
624
|
-
displayName
|
|
625
|
-
};
|
|
626
|
-
};
|
|
627
|
-
static async executeRedirectStrategy(req, providerStrategy, options) {
|
|
628
|
-
return new Promise((resolve) => {
|
|
629
|
-
const strategy = Object.create(providerStrategy);
|
|
630
|
-
strategy.redirect = (url, status) => {
|
|
631
|
-
resolve({ url, status: status ?? void 0 });
|
|
632
|
-
};
|
|
633
|
-
strategy.authenticate(req, { ...options });
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
static async executeFrameHandlerStrategy(req, providerStrategy, options) {
|
|
637
|
-
return new Promise((resolve, reject) => {
|
|
638
|
-
const strategy = Object.create(providerStrategy);
|
|
639
|
-
strategy.success = (result, privateInfo) => {
|
|
640
|
-
resolve({ result, privateInfo });
|
|
641
|
-
};
|
|
642
|
-
strategy.fail = (info) => {
|
|
643
|
-
reject(new Error(`Authentication rejected, ${info.message ?? ""}`));
|
|
644
|
-
};
|
|
645
|
-
strategy.error = (error) => {
|
|
646
|
-
let message = `Authentication failed, ${error.message}`;
|
|
647
|
-
if (error.oauthError?.data) {
|
|
648
|
-
try {
|
|
649
|
-
const errorData = JSON.parse(error.oauthError.data);
|
|
650
|
-
if (errorData.message) {
|
|
651
|
-
message += ` - ${errorData.message}`;
|
|
652
|
-
}
|
|
653
|
-
} catch (parseError) {
|
|
654
|
-
message += ` - ${error.oauthError}`;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
reject(new Error(message));
|
|
658
|
-
};
|
|
659
|
-
strategy.redirect = () => {
|
|
660
|
-
reject(new Error("Unexpected redirect"));
|
|
661
|
-
};
|
|
662
|
-
strategy.authenticate(req, { ...options ?? {} });
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
static async executeRefreshTokenStrategy(providerStrategy, refreshToken, scope) {
|
|
666
|
-
return new Promise((resolve, reject) => {
|
|
667
|
-
const anyStrategy = providerStrategy;
|
|
668
|
-
const OAuth2 = anyStrategy._oauth2.constructor;
|
|
669
|
-
const oauth2 = new OAuth2(
|
|
670
|
-
anyStrategy._oauth2._clientId,
|
|
671
|
-
anyStrategy._oauth2._clientSecret,
|
|
672
|
-
anyStrategy._oauth2._baseSite,
|
|
673
|
-
anyStrategy._oauth2._authorizeUrl,
|
|
674
|
-
anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl,
|
|
675
|
-
anyStrategy._oauth2._customHeaders
|
|
676
|
-
);
|
|
677
|
-
oauth2.getOAuthAccessToken(
|
|
678
|
-
refreshToken,
|
|
679
|
-
{
|
|
680
|
-
scope,
|
|
681
|
-
grant_type: "refresh_token"
|
|
682
|
-
},
|
|
683
|
-
(err, accessToken, newRefreshToken, params) => {
|
|
684
|
-
if (err) {
|
|
685
|
-
reject(
|
|
686
|
-
new Error(`Failed to refresh access token ${err.toString()}`)
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
if (!accessToken) {
|
|
690
|
-
reject(
|
|
691
|
-
new Error(
|
|
692
|
-
`Failed to refresh access token, no access token received`
|
|
693
|
-
)
|
|
694
|
-
);
|
|
695
|
-
}
|
|
696
|
-
resolve({
|
|
697
|
-
accessToken,
|
|
698
|
-
refreshToken: newRefreshToken,
|
|
699
|
-
params
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
);
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
static async executeFetchUserProfileStrategy(providerStrategy, accessToken) {
|
|
706
|
-
return new Promise((resolve, reject) => {
|
|
707
|
-
const anyStrategy = providerStrategy;
|
|
708
|
-
anyStrategy.userProfile(
|
|
709
|
-
accessToken,
|
|
710
|
-
(error, rawProfile) => {
|
|
711
|
-
if (error) {
|
|
712
|
-
reject(error);
|
|
713
|
-
} else {
|
|
714
|
-
resolve(rawProfile);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
);
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
class PassportOAuthAuthenticatorHelper {
|
|
723
|
-
static defaultProfileTransform = async (input) => ({
|
|
724
|
-
profile: PassportHelpers.transformProfile(
|
|
725
|
-
input.fullProfile ?? {},
|
|
726
|
-
input.session.idToken
|
|
727
|
-
)
|
|
728
|
-
});
|
|
729
|
-
static from(strategy) {
|
|
730
|
-
return new PassportOAuthAuthenticatorHelper(strategy);
|
|
731
|
-
}
|
|
732
|
-
#strategy;
|
|
733
|
-
constructor(strategy) {
|
|
734
|
-
this.#strategy = strategy;
|
|
735
|
-
}
|
|
736
|
-
async start(input, options) {
|
|
737
|
-
return PassportHelpers.executeRedirectStrategy(input.req, this.#strategy, {
|
|
738
|
-
scope: input.scope,
|
|
739
|
-
state: input.state,
|
|
740
|
-
...options
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
async authenticate(input, options) {
|
|
744
|
-
const { result, privateInfo } = await PassportHelpers.executeFrameHandlerStrategy(input.req, this.#strategy, options);
|
|
745
|
-
return {
|
|
746
|
-
fullProfile: result.fullProfile,
|
|
747
|
-
session: {
|
|
748
|
-
accessToken: result.accessToken,
|
|
749
|
-
tokenType: result.params.token_type ?? "bearer",
|
|
750
|
-
scope: result.params.scope,
|
|
751
|
-
expiresInSeconds: result.params.expires_in,
|
|
752
|
-
idToken: result.params.id_token,
|
|
753
|
-
refreshToken: privateInfo.refreshToken
|
|
754
|
-
}
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
async refresh(input) {
|
|
758
|
-
const result = await PassportHelpers.executeRefreshTokenStrategy(
|
|
759
|
-
this.#strategy,
|
|
760
|
-
input.refreshToken,
|
|
761
|
-
input.scope
|
|
762
|
-
);
|
|
763
|
-
const fullProfile = await this.fetchProfile(result.accessToken);
|
|
764
|
-
return {
|
|
765
|
-
fullProfile,
|
|
766
|
-
session: {
|
|
767
|
-
accessToken: result.accessToken,
|
|
768
|
-
tokenType: result.params.token_type ?? "bearer",
|
|
769
|
-
scope: result.params.scope,
|
|
770
|
-
expiresInSeconds: result.params.expires_in,
|
|
771
|
-
idToken: result.params.id_token,
|
|
772
|
-
refreshToken: result.refreshToken
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
async fetchProfile(accessToken) {
|
|
777
|
-
const profile = await PassportHelpers.executeFetchUserProfileStrategy(
|
|
778
|
-
this.#strategy,
|
|
779
|
-
accessToken
|
|
780
|
-
);
|
|
781
|
-
return profile;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
class OAuthEnvironmentHandler {
|
|
786
|
-
constructor(handlers) {
|
|
787
|
-
this.handlers = handlers;
|
|
788
|
-
}
|
|
789
|
-
static mapConfig(config, factoryFunc) {
|
|
790
|
-
const envs = config.keys();
|
|
791
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
792
|
-
for (const env of envs) {
|
|
793
|
-
const envConfig = config.getConfig(env);
|
|
794
|
-
const handler = factoryFunc(envConfig);
|
|
795
|
-
handlers.set(env, handler);
|
|
796
|
-
}
|
|
797
|
-
return new OAuthEnvironmentHandler(handlers);
|
|
798
|
-
}
|
|
799
|
-
async start(req, res) {
|
|
800
|
-
const provider = this.getProviderForEnv(req);
|
|
801
|
-
await provider.start(req, res);
|
|
802
|
-
}
|
|
803
|
-
async frameHandler(req, res) {
|
|
804
|
-
const provider = this.getProviderForEnv(req);
|
|
805
|
-
await provider.frameHandler(req, res);
|
|
806
|
-
}
|
|
807
|
-
async refresh(req, res) {
|
|
808
|
-
const provider = this.getProviderForEnv(req);
|
|
809
|
-
await provider.refresh?.(req, res);
|
|
810
|
-
}
|
|
811
|
-
async logout(req, res) {
|
|
812
|
-
const provider = this.getProviderForEnv(req);
|
|
813
|
-
await provider.logout?.(req, res);
|
|
814
|
-
}
|
|
815
|
-
getEnvFromRequest(req) {
|
|
816
|
-
const reqEnv = req.query.env?.toString();
|
|
817
|
-
if (reqEnv) {
|
|
818
|
-
return reqEnv;
|
|
819
|
-
}
|
|
820
|
-
const stateParams = req.query.state?.toString();
|
|
821
|
-
if (!stateParams) {
|
|
822
|
-
return void 0;
|
|
823
|
-
}
|
|
824
|
-
const { env } = decodeOAuthState(stateParams);
|
|
825
|
-
return env;
|
|
826
|
-
}
|
|
827
|
-
getProviderForEnv(req) {
|
|
828
|
-
const env = this.getEnvFromRequest(req);
|
|
829
|
-
if (!env) {
|
|
830
|
-
throw new errors.InputError(`Must specify 'env' query to select environment`);
|
|
831
|
-
}
|
|
832
|
-
const handler = this.handlers.get(env);
|
|
833
|
-
if (!handler) {
|
|
834
|
-
throw new errors.NotFoundError(
|
|
835
|
-
`No configuration available for the '${env}' environment of this provider.`
|
|
836
|
-
);
|
|
837
|
-
}
|
|
838
|
-
return handler;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
function createSignInResolverFactory(options) {
|
|
843
|
-
const { optionsSchema } = options;
|
|
844
|
-
if (!optionsSchema) {
|
|
845
|
-
return (resolverOptions) => {
|
|
846
|
-
if (resolverOptions) {
|
|
847
|
-
throw new errors.InputError("sign-in resolver does not accept options");
|
|
848
|
-
}
|
|
849
|
-
return options.create(void 0);
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
const factory = (...[resolverOptions]) => {
|
|
853
|
-
const parsedOptions = optionsSchema.parse(resolverOptions);
|
|
854
|
-
return options.create(parsedOptions);
|
|
855
|
-
};
|
|
856
|
-
factory.optionsJsonSchema = zodToJsonSchema__default.default(optionsSchema);
|
|
857
|
-
return factory;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
function readDeclarativeSignInResolver(options) {
|
|
861
|
-
const resolvers = options.config.getOptionalConfigArray("signIn.resolvers")?.map((resolverConfig) => {
|
|
862
|
-
const resolverName = resolverConfig.getString("resolver");
|
|
863
|
-
if (!Object.hasOwn(options.signInResolverFactories, resolverName)) {
|
|
864
|
-
throw new Error(
|
|
865
|
-
`Sign-in resolver '${resolverName}' is not available`
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
const resolver = options.signInResolverFactories[resolverName];
|
|
869
|
-
const { resolver: _ignored, ...resolverOptions } = resolverConfig.get();
|
|
870
|
-
return resolver(
|
|
871
|
-
Object.keys(resolverOptions).length > 0 ? resolverOptions : void 0
|
|
872
|
-
);
|
|
873
|
-
}) ?? [];
|
|
874
|
-
if (resolvers.length === 0) {
|
|
875
|
-
return void 0;
|
|
876
|
-
}
|
|
877
|
-
return async (profile, context) => {
|
|
878
|
-
for (const resolver of resolvers) {
|
|
879
|
-
try {
|
|
880
|
-
return await resolver(profile, context);
|
|
881
|
-
} catch (error) {
|
|
882
|
-
if (error?.name === "NotFoundError") {
|
|
883
|
-
continue;
|
|
884
|
-
}
|
|
885
|
-
throw error;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
throw new Error(
|
|
889
|
-
"Failed to sign-in, unable to resolve user identity. Please verify that your catalog contains the expected User entities that would match your configured sign-in resolver."
|
|
890
|
-
);
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
const reEmail = /^([^@+]+)(\+[^@]+)?(@.*)$/;
|
|
895
|
-
exports.commonSignInResolvers = void 0;
|
|
896
|
-
((commonSignInResolvers2) => {
|
|
897
|
-
commonSignInResolvers2.emailMatchingUserEntityProfileEmail = createSignInResolverFactory({
|
|
898
|
-
create() {
|
|
899
|
-
return async (info, ctx) => {
|
|
900
|
-
const { profile } = info;
|
|
901
|
-
if (!profile.email) {
|
|
902
|
-
throw new Error(
|
|
903
|
-
"Login failed, user profile does not contain an email"
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
try {
|
|
907
|
-
return await ctx.signInWithCatalogUser({
|
|
908
|
-
filter: {
|
|
909
|
-
"spec.profile.email": profile.email
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
} catch (err) {
|
|
913
|
-
if (err?.name === "NotFoundError") {
|
|
914
|
-
const m = profile.email.match(reEmail);
|
|
915
|
-
if (m?.length === 4) {
|
|
916
|
-
const [_, name, _plus, domain] = m;
|
|
917
|
-
const noPlusEmail = `${name}${domain}`;
|
|
918
|
-
return ctx.signInWithCatalogUser({
|
|
919
|
-
filter: {
|
|
920
|
-
"spec.profile.email": noPlusEmail
|
|
921
|
-
}
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
throw err;
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
});
|
|
930
|
-
commonSignInResolvers2.emailLocalPartMatchingUserEntityName = createSignInResolverFactory({
|
|
931
|
-
create() {
|
|
932
|
-
return async (info, ctx) => {
|
|
933
|
-
const { profile } = info;
|
|
934
|
-
if (!profile.email) {
|
|
935
|
-
throw new Error(
|
|
936
|
-
"Login failed, user profile does not contain an email"
|
|
937
|
-
);
|
|
938
|
-
}
|
|
939
|
-
const [localPart] = profile.email.split("@");
|
|
940
|
-
return ctx.signInWithCatalogUser({
|
|
941
|
-
entityRef: { name: localPart }
|
|
942
|
-
});
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
})(exports.commonSignInResolvers || (exports.commonSignInResolvers = {}));
|
|
947
|
-
|
|
948
|
-
function createOAuthProviderFactory(options) {
|
|
949
|
-
return (ctx) => {
|
|
950
|
-
return OAuthEnvironmentHandler.mapConfig(ctx.config, (envConfig) => {
|
|
951
|
-
const signInResolver = readDeclarativeSignInResolver({
|
|
952
|
-
config: envConfig,
|
|
953
|
-
signInResolverFactories: options.signInResolverFactories ?? {}
|
|
954
|
-
}) ?? options.signInResolver;
|
|
955
|
-
return createOAuthRouteHandlers({
|
|
956
|
-
authenticator: options.authenticator,
|
|
957
|
-
appUrl: ctx.appUrl,
|
|
958
|
-
baseUrl: ctx.baseUrl,
|
|
959
|
-
config: envConfig,
|
|
960
|
-
isOriginAllowed: ctx.isOriginAllowed,
|
|
961
|
-
cookieConfigurer: ctx.cookieConfigurer,
|
|
962
|
-
providerId: ctx.providerId,
|
|
963
|
-
resolverContext: ctx.resolverContext,
|
|
964
|
-
additionalScopes: options.additionalScopes,
|
|
965
|
-
stateTransform: options.stateTransform,
|
|
966
|
-
profileTransform: options.profileTransform,
|
|
967
|
-
signInResolver
|
|
968
|
-
});
|
|
969
|
-
});
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
function createOAuthAuthenticator(authenticator) {
|
|
974
|
-
return authenticator;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
function createProxyAuthenticator(authenticator) {
|
|
978
|
-
return authenticator;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
function createProxyAuthRouteHandlers(options) {
|
|
982
|
-
const { authenticator, config, resolverContext, signInResolver } = options;
|
|
983
|
-
const profileTransform = options.profileTransform ?? authenticator.defaultProfileTransform;
|
|
984
|
-
const authenticatorCtx = authenticator.initialize({ config });
|
|
985
|
-
return {
|
|
986
|
-
async start() {
|
|
987
|
-
throw new errors.NotImplementedError("Not implemented");
|
|
988
|
-
},
|
|
989
|
-
async frameHandler() {
|
|
990
|
-
throw new errors.NotImplementedError("Not implemented");
|
|
991
|
-
},
|
|
992
|
-
async refresh(req, res) {
|
|
993
|
-
const { result, providerInfo } = await authenticator.authenticate(
|
|
994
|
-
{ req },
|
|
995
|
-
authenticatorCtx
|
|
996
|
-
);
|
|
997
|
-
const { profile } = await profileTransform(result, resolverContext);
|
|
998
|
-
const identity = await signInResolver(
|
|
999
|
-
{ profile, result },
|
|
1000
|
-
resolverContext
|
|
1001
|
-
);
|
|
1002
|
-
const response = {
|
|
1003
|
-
profile,
|
|
1004
|
-
providerInfo,
|
|
1005
|
-
backstageIdentity: prepareBackstageIdentityResponse(identity)
|
|
1006
|
-
};
|
|
1007
|
-
res.status(200).json(response);
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
function createProxyAuthProviderFactory(options) {
|
|
1013
|
-
return (ctx) => {
|
|
1014
|
-
const signInResolver = options.signInResolver ?? readDeclarativeSignInResolver({
|
|
1015
|
-
config: ctx.config,
|
|
1016
|
-
signInResolverFactories: options.signInResolverFactories ?? {}
|
|
1017
|
-
});
|
|
1018
|
-
if (!signInResolver) {
|
|
1019
|
-
throw new Error(
|
|
1020
|
-
`No sign-in resolver configured for proxy auth provider '${ctx.providerId}'`
|
|
1021
|
-
);
|
|
1022
|
-
}
|
|
1023
|
-
return createProxyAuthRouteHandlers({
|
|
1024
|
-
signInResolver,
|
|
1025
|
-
config: ctx.config,
|
|
1026
|
-
authenticator: options.authenticator,
|
|
1027
|
-
resolverContext: ctx.resolverContext,
|
|
1028
|
-
profileTransform: options.profileTransform
|
|
1029
|
-
});
|
|
1030
|
-
};
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
const tokenTypes = Object.freeze({
|
|
1034
|
-
user: Object.freeze({
|
|
1035
|
-
typParam: "vnd.backstage.user",
|
|
1036
|
-
audClaim: "backstage"
|
|
1037
|
-
}),
|
|
1038
|
-
limitedUser: Object.freeze({
|
|
1039
|
-
typParam: "vnd.backstage.limited-user"
|
|
1040
|
-
}),
|
|
1041
|
-
plugin: Object.freeze({
|
|
1042
|
-
typParam: "vnd.backstage.plugin"
|
|
1043
|
-
})
|
|
3
|
+
var AuthProvidersExtensionPoint = require('./extensions/AuthProvidersExtensionPoint.cjs.js');
|
|
4
|
+
var AuthOwnershipResolutionExtensionPoint = require('./extensions/AuthOwnershipResolutionExtensionPoint.cjs.js');
|
|
5
|
+
var sendWebMessageResponse = require('./flow/sendWebMessageResponse.cjs.js');
|
|
6
|
+
var prepareBackstageIdentityResponse = require('./identity/prepareBackstageIdentityResponse.cjs.js');
|
|
7
|
+
var getBearerTokenFromAuthorizationHeader = require('./identity/getBearerTokenFromAuthorizationHeader.cjs.js');
|
|
8
|
+
var DefaultIdentityClient = require('./identity/DefaultIdentityClient.cjs.js');
|
|
9
|
+
var IdentityClient = require('./identity/IdentityClient.cjs.js');
|
|
10
|
+
var createOAuthRouteHandlers = require('./oauth/createOAuthRouteHandlers.cjs.js');
|
|
11
|
+
var PassportOAuthAuthenticatorHelper = require('./oauth/PassportOAuthAuthenticatorHelper.cjs.js');
|
|
12
|
+
var OAuthEnvironmentHandler = require('./oauth/OAuthEnvironmentHandler.cjs.js');
|
|
13
|
+
var createOAuthProviderFactory = require('./oauth/createOAuthProviderFactory.cjs.js');
|
|
14
|
+
var state = require('./oauth/state.cjs.js');
|
|
15
|
+
var types$1 = require('./oauth/types.cjs.js');
|
|
16
|
+
var PassportHelpers = require('./passport/PassportHelpers.cjs.js');
|
|
17
|
+
var types$2 = require('./proxy/types.cjs.js');
|
|
18
|
+
var createProxyAuthProviderFactory = require('./proxy/createProxyAuthProviderFactory.cjs.js');
|
|
19
|
+
var createProxyRouteHandlers = require('./proxy/createProxyRouteHandlers.cjs.js');
|
|
20
|
+
var createSignInResolverFactory = require('./sign-in/createSignInResolverFactory.cjs.js');
|
|
21
|
+
var readDeclarativeSignInResolver = require('./sign-in/readDeclarativeSignInResolver.cjs.js');
|
|
22
|
+
var commonSignInResolvers = require('./sign-in/commonSignInResolvers.cjs.js');
|
|
23
|
+
var types = require('./types.cjs.js');
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
exports.authProvidersExtensionPoint = AuthProvidersExtensionPoint.authProvidersExtensionPoint;
|
|
28
|
+
exports.authOwnershipResolutionExtensionPoint = AuthOwnershipResolutionExtensionPoint.authOwnershipResolutionExtensionPoint;
|
|
29
|
+
exports.sendWebMessageResponse = sendWebMessageResponse.sendWebMessageResponse;
|
|
30
|
+
exports.prepareBackstageIdentityResponse = prepareBackstageIdentityResponse.prepareBackstageIdentityResponse;
|
|
31
|
+
exports.getBearerTokenFromAuthorizationHeader = getBearerTokenFromAuthorizationHeader.getBearerTokenFromAuthorizationHeader;
|
|
32
|
+
exports.DefaultIdentityClient = DefaultIdentityClient.DefaultIdentityClient;
|
|
33
|
+
exports.IdentityClient = IdentityClient.IdentityClient;
|
|
34
|
+
exports.createOAuthRouteHandlers = createOAuthRouteHandlers.createOAuthRouteHandlers;
|
|
35
|
+
exports.PassportOAuthAuthenticatorHelper = PassportOAuthAuthenticatorHelper.PassportOAuthAuthenticatorHelper;
|
|
36
|
+
exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler.OAuthEnvironmentHandler;
|
|
37
|
+
exports.createOAuthProviderFactory = createOAuthProviderFactory.createOAuthProviderFactory;
|
|
38
|
+
exports.decodeOAuthState = state.decodeOAuthState;
|
|
39
|
+
exports.encodeOAuthState = state.encodeOAuthState;
|
|
40
|
+
exports.createOAuthAuthenticator = types$1.createOAuthAuthenticator;
|
|
41
|
+
exports.PassportHelpers = PassportHelpers.PassportHelpers;
|
|
42
|
+
exports.createProxyAuthenticator = types$2.createProxyAuthenticator;
|
|
43
|
+
exports.createProxyAuthProviderFactory = createProxyAuthProviderFactory.createProxyAuthProviderFactory;
|
|
44
|
+
exports.createProxyAuthRouteHandlers = createProxyRouteHandlers.createProxyAuthRouteHandlers;
|
|
45
|
+
exports.createSignInResolverFactory = createSignInResolverFactory.createSignInResolverFactory;
|
|
46
|
+
exports.readDeclarativeSignInResolver = readDeclarativeSignInResolver.readDeclarativeSignInResolver;
|
|
47
|
+
Object.defineProperty(exports, "commonSignInResolvers", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
get: function () { return commonSignInResolvers.commonSignInResolvers; }
|
|
1044
50
|
});
|
|
1045
|
-
|
|
1046
|
-
exports.DefaultIdentityClient = DefaultIdentityClient;
|
|
1047
|
-
exports.IdentityClient = IdentityClient;
|
|
1048
|
-
exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
|
|
1049
|
-
exports.PassportHelpers = PassportHelpers;
|
|
1050
|
-
exports.PassportOAuthAuthenticatorHelper = PassportOAuthAuthenticatorHelper;
|
|
1051
|
-
exports.authOwnershipResolutionExtensionPoint = authOwnershipResolutionExtensionPoint;
|
|
1052
|
-
exports.authProvidersExtensionPoint = authProvidersExtensionPoint;
|
|
1053
|
-
exports.createOAuthAuthenticator = createOAuthAuthenticator;
|
|
1054
|
-
exports.createOAuthProviderFactory = createOAuthProviderFactory;
|
|
1055
|
-
exports.createOAuthRouteHandlers = createOAuthRouteHandlers;
|
|
1056
|
-
exports.createProxyAuthProviderFactory = createProxyAuthProviderFactory;
|
|
1057
|
-
exports.createProxyAuthRouteHandlers = createProxyAuthRouteHandlers;
|
|
1058
|
-
exports.createProxyAuthenticator = createProxyAuthenticator;
|
|
1059
|
-
exports.createSignInResolverFactory = createSignInResolverFactory;
|
|
1060
|
-
exports.decodeOAuthState = decodeOAuthState;
|
|
1061
|
-
exports.encodeOAuthState = encodeOAuthState;
|
|
1062
|
-
exports.getBearerTokenFromAuthorizationHeader = getBearerTokenFromAuthorizationHeader;
|
|
1063
|
-
exports.prepareBackstageIdentityResponse = prepareBackstageIdentityResponse;
|
|
1064
|
-
exports.readDeclarativeSignInResolver = readDeclarativeSignInResolver;
|
|
1065
|
-
exports.sendWebMessageResponse = sendWebMessageResponse;
|
|
1066
|
-
exports.tokenTypes = tokenTypes;
|
|
51
|
+
exports.tokenTypes = types.tokenTypes;
|
|
1067
52
|
//# sourceMappingURL=index.cjs.js.map
|