@backstage/plugin-auth-node 0.5.3-next.1 → 0.5.4-next.0
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
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @backstage/plugin-auth-node
|
|
2
2
|
|
|
3
|
+
## 0.5.4-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a0a9a4a: Browsers silently drop cookies that exceed 4KB, which can be problematic for refresh tokens and other large cookies.This update ensures that large cookies, like refresh tokens, are not dropped by browsers, maintaining the integrity of the authentication process. The changes include both the implementation of the cookie splitting logic and corresponding tests to validate the new functionality.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/catalog-client@1.8.0-next.0
|
|
10
|
+
- @backstage/backend-plugin-api@1.0.2-next.0
|
|
11
|
+
- @backstage/catalog-model@1.7.0
|
|
12
|
+
- @backstage/config@1.2.0
|
|
13
|
+
- @backstage/errors@1.2.4
|
|
14
|
+
- @backstage/types@1.1.1
|
|
15
|
+
|
|
16
|
+
## 0.5.3
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- 217458a: Added a new `allowedDomains` option for the common `emailLocalPartMatchingUserEntityName` sign-in resolver.
|
|
21
|
+
- 094eaa3: Remove references to in-repo backend-common
|
|
22
|
+
- e4ad29a: Fix authentication error handling using redirect flow via `enableExperimentalRedirectFlow` config. If an error is caught during authentication, the user is redirected back to app origin with `error` query parameter containing the error message.
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @backstage/catalog-client@1.7.1
|
|
25
|
+
- @backstage/backend-plugin-api@1.0.1
|
|
26
|
+
- @backstage/catalog-model@1.7.0
|
|
27
|
+
- @backstage/config@1.2.0
|
|
28
|
+
- @backstage/errors@1.2.4
|
|
29
|
+
- @backstage/types@1.1.1
|
|
30
|
+
|
|
3
31
|
## 0.5.3-next.1
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const THOUSAND_DAYS_MS = 1e3 * 24 * 60 * 60 * 1e3;
|
|
4
4
|
const TEN_MINUTES_MS = 600 * 1e3;
|
|
5
|
+
const MAX_COOKIE_SIZE_CHARACTERS = 4e3;
|
|
5
6
|
const defaultCookieConfigurer = ({
|
|
6
7
|
callbackUrl,
|
|
7
8
|
providerId,
|
|
@@ -43,43 +44,141 @@ class OAuthCookieManager {
|
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
setNonce(res, nonce, origin) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
this.setCookie(
|
|
48
|
+
res,
|
|
49
|
+
this.nonceCookie,
|
|
50
|
+
nonce,
|
|
51
|
+
TEN_MINUTES_MS,
|
|
52
|
+
origin,
|
|
53
|
+
"/handler"
|
|
54
|
+
);
|
|
50
55
|
}
|
|
51
56
|
setRefreshToken(res, refreshToken, origin) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
this.setCookie(
|
|
58
|
+
res,
|
|
59
|
+
this.refreshTokenCookie,
|
|
60
|
+
refreshToken,
|
|
61
|
+
THOUSAND_DAYS_MS,
|
|
62
|
+
origin
|
|
63
|
+
);
|
|
56
64
|
}
|
|
57
65
|
removeRefreshToken(res, origin) {
|
|
58
|
-
|
|
59
|
-
maxAge: 0,
|
|
60
|
-
...this.getConfig(origin)
|
|
61
|
-
});
|
|
66
|
+
this.removeCookie(res, this.refreshTokenCookie, origin);
|
|
62
67
|
}
|
|
63
68
|
removeGrantedScopes(res, origin) {
|
|
64
|
-
|
|
65
|
-
maxAge: 0,
|
|
66
|
-
...this.getConfig(origin)
|
|
67
|
-
});
|
|
69
|
+
this.removeCookie(res, this.grantedScopeCookie, origin);
|
|
68
70
|
}
|
|
69
71
|
setGrantedScopes(res, scope, origin) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
this.setCookie(
|
|
73
|
+
res,
|
|
74
|
+
this.grantedScopeCookie,
|
|
75
|
+
scope,
|
|
76
|
+
THOUSAND_DAYS_MS,
|
|
77
|
+
origin
|
|
78
|
+
);
|
|
74
79
|
}
|
|
75
80
|
getNonce(req) {
|
|
76
|
-
return req
|
|
81
|
+
return this.getCookie(req, this.nonceCookie);
|
|
77
82
|
}
|
|
78
83
|
getRefreshToken(req) {
|
|
79
|
-
return req
|
|
84
|
+
return this.getCookie(req, this.refreshTokenCookie);
|
|
80
85
|
}
|
|
81
86
|
getGrantedScopes(req) {
|
|
82
|
-
return req
|
|
87
|
+
return this.getCookie(req, this.grantedScopeCookie);
|
|
88
|
+
}
|
|
89
|
+
setCookie(res, name, val, maxAge, origin, pathSuffix = "") {
|
|
90
|
+
const options = {
|
|
91
|
+
maxAge,
|
|
92
|
+
...this.getConfig(origin, pathSuffix)
|
|
93
|
+
};
|
|
94
|
+
const req = res.req;
|
|
95
|
+
let output = res;
|
|
96
|
+
if (val.length > MAX_COOKIE_SIZE_CHARACTERS) {
|
|
97
|
+
const nonChunkedFormatExists = !!req.cookies[name];
|
|
98
|
+
if (nonChunkedFormatExists) {
|
|
99
|
+
output = output.cookie(name, "", this.getRemoveCookieOptions());
|
|
100
|
+
}
|
|
101
|
+
const chunked = this.splitCookieToChunks(val, MAX_COOKIE_SIZE_CHARACTERS);
|
|
102
|
+
chunked.forEach((value, chunkNumber) => {
|
|
103
|
+
output = output.cookie(
|
|
104
|
+
OAuthCookieManager.getCookieChunkName(name, chunkNumber),
|
|
105
|
+
value,
|
|
106
|
+
options
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
return output;
|
|
110
|
+
}
|
|
111
|
+
const chunkedFormatExists = OAuthCookieManager.chunkedCookieExists(
|
|
112
|
+
req,
|
|
113
|
+
name
|
|
114
|
+
);
|
|
115
|
+
if (chunkedFormatExists) {
|
|
116
|
+
for (let chunkNumber = 0; ; chunkNumber++) {
|
|
117
|
+
const key = OAuthCookieManager.getCookieChunkName(name, chunkNumber);
|
|
118
|
+
const exists = !!req.cookies[key];
|
|
119
|
+
if (!exists) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
output = output.cookie(key, "", this.getRemoveCookieOptions());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return output.cookie(name, val, options);
|
|
126
|
+
}
|
|
127
|
+
getCookie(req, name) {
|
|
128
|
+
const isChunked = OAuthCookieManager.chunkedCookieExists(req, name);
|
|
129
|
+
if (isChunked) {
|
|
130
|
+
const chunks = [];
|
|
131
|
+
let chunkNumber = 0;
|
|
132
|
+
let chunk = req.cookies[OAuthCookieManager.getCookieChunkName(name, chunkNumber)];
|
|
133
|
+
while (chunk) {
|
|
134
|
+
chunks.push(chunk);
|
|
135
|
+
chunkNumber++;
|
|
136
|
+
chunk = req.cookies[OAuthCookieManager.getCookieChunkName(name, chunkNumber)];
|
|
137
|
+
}
|
|
138
|
+
return chunks.join("");
|
|
139
|
+
}
|
|
140
|
+
return req.cookies[name];
|
|
141
|
+
}
|
|
142
|
+
removeCookie(res, name, origin) {
|
|
143
|
+
const req = res.req;
|
|
144
|
+
const options = this.getRemoveCookieOptions(origin);
|
|
145
|
+
const isChunked = OAuthCookieManager.chunkedCookieExists(req, name);
|
|
146
|
+
if (isChunked) {
|
|
147
|
+
const nonChunkedFormatExists = !!req.cookies[name];
|
|
148
|
+
let output = nonChunkedFormatExists ? res.cookie(name, "", options) : res;
|
|
149
|
+
for (let chunkNumber = 0; ; chunkNumber++) {
|
|
150
|
+
const key = OAuthCookieManager.getCookieChunkName(name, chunkNumber);
|
|
151
|
+
const exists = !!req.cookies[key];
|
|
152
|
+
if (!exists) {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
output = output.cookie(key, "", options);
|
|
156
|
+
}
|
|
157
|
+
return output;
|
|
158
|
+
}
|
|
159
|
+
return res.cookie(name, "", options);
|
|
160
|
+
}
|
|
161
|
+
splitCookieToChunks(val, chunkSize) {
|
|
162
|
+
const numChunks = Math.ceil(val.length / chunkSize);
|
|
163
|
+
const chunks = Array(numChunks);
|
|
164
|
+
let offset = 0;
|
|
165
|
+
for (let i = 0; i < numChunks; i++) {
|
|
166
|
+
chunks[i] = val.substring(offset, offset + chunkSize);
|
|
167
|
+
offset += chunkSize;
|
|
168
|
+
}
|
|
169
|
+
return chunks;
|
|
170
|
+
}
|
|
171
|
+
static chunkedCookieExists(req, name) {
|
|
172
|
+
return !!req.cookies[OAuthCookieManager.getCookieChunkName(name, 0)];
|
|
173
|
+
}
|
|
174
|
+
static getCookieChunkName(name, chunkIndex) {
|
|
175
|
+
return `${name}-${chunkIndex}`;
|
|
176
|
+
}
|
|
177
|
+
getRemoveCookieOptions(origin) {
|
|
178
|
+
return {
|
|
179
|
+
maxAge: 0,
|
|
180
|
+
...this.getConfig(origin)
|
|
181
|
+
};
|
|
83
182
|
}
|
|
84
183
|
}
|
|
85
184
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OAuthCookieManager.cjs.js","sources":["../../src/oauth/OAuthCookieManager.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Request, Response } from 'express';\nimport { CookieConfigurer } from '../types';\n\nconst THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;\nconst TEN_MINUTES_MS = 600 * 1000;\n\nconst defaultCookieConfigurer: CookieConfigurer = ({\n callbackUrl,\n providerId,\n appOrigin,\n}) => {\n const { hostname: domain, pathname, protocol } = new URL(callbackUrl);\n const secure = protocol === 'https:';\n\n // For situations where the auth-backend is running on a\n // different domain than the app, we set the SameSite attribute\n // to 'none' to allow third-party access to the cookie, but\n // only if it's in a secure context (https).\n let sameSite: ReturnType<CookieConfigurer>['sameSite'] = 'lax';\n if (new URL(appOrigin).hostname !== domain && secure) {\n sameSite = 'none';\n }\n\n // If the provider supports callbackUrls, the pathname will\n // contain the complete path to the frame handler so we need\n // to slice off the trailing part of the path.\n const path = pathname.endsWith(`${providerId}/handler/frame`)\n ? pathname.slice(0, -'/handler/frame'.length)\n : `${pathname}/${providerId}`;\n\n return { domain, path, secure, sameSite };\n};\n\n/** @internal */\nexport class OAuthCookieManager {\n private readonly cookieConfigurer: CookieConfigurer;\n private readonly nonceCookie: string;\n private readonly refreshTokenCookie: string;\n private readonly grantedScopeCookie: string;\n\n constructor(\n private readonly options: {\n providerId: string;\n defaultAppOrigin: string;\n baseUrl: string;\n callbackUrl: string;\n cookieConfigurer?: CookieConfigurer;\n },\n ) {\n this.cookieConfigurer = options.cookieConfigurer ?? defaultCookieConfigurer;\n\n this.nonceCookie = `${options.providerId}-nonce`;\n this.refreshTokenCookie = `${options.providerId}-refresh-token`;\n this.grantedScopeCookie = `${options.providerId}-granted-scope`;\n }\n\n private getConfig(origin?: string, pathSuffix: string = '') {\n const cookieConfig = this.cookieConfigurer({\n providerId: this.options.providerId,\n baseUrl: this.options.baseUrl,\n callbackUrl: this.options.callbackUrl,\n appOrigin: origin ?? this.options.defaultAppOrigin,\n });\n return {\n httpOnly: true,\n sameSite: 'lax' as const,\n ...cookieConfig,\n path: cookieConfig.path + pathSuffix,\n };\n }\n\n setNonce(res: Response, nonce: string, origin?: string) {\n res.cookie(this.nonceCookie, nonce, {\n maxAge: TEN_MINUTES_MS,\n ...this.getConfig(origin, '/handler'),\n });\n }\n\n setRefreshToken(res: Response, refreshToken: string, origin?: string) {\n res.cookie(this.refreshTokenCookie, refreshToken, {\n maxAge: THOUSAND_DAYS_MS,\n ...this.getConfig(origin),\n });\n }\n\n removeRefreshToken(res: Response, origin?: string) {\n res.cookie(this.refreshTokenCookie, '', {\n maxAge: 0,\n ...this.getConfig(origin),\n });\n }\n\n removeGrantedScopes(res: Response, origin?: string) {\n res.cookie(this.grantedScopeCookie, '', {\n maxAge: 0,\n ...this.getConfig(origin),\n });\n }\n\n setGrantedScopes(res: Response, scope: string, origin?: string) {\n res.cookie(this.grantedScopeCookie, scope, {\n maxAge: THOUSAND_DAYS_MS,\n ...this.getConfig(origin),\n });\n }\n\n getNonce(req: Request): string | undefined {\n return req.cookies[this.nonceCookie];\n }\n\n getRefreshToken(req: Request): string | undefined {\n return req.cookies[this.refreshTokenCookie];\n }\n\n getGrantedScopes(req: Request): string | undefined {\n return req.cookies[this.grantedScopeCookie];\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OAuthCookieManager.cjs.js","sources":["../../src/oauth/OAuthCookieManager.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CookieOptions, Request, Response } from 'express';\nimport { CookieConfigurer } from '../types';\n\nconst THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;\nconst TEN_MINUTES_MS = 600 * 1000;\n\nconst MAX_COOKIE_SIZE_CHARACTERS = 4000;\n\nconst defaultCookieConfigurer: CookieConfigurer = ({\n callbackUrl,\n providerId,\n appOrigin,\n}) => {\n const { hostname: domain, pathname, protocol } = new URL(callbackUrl);\n const secure = protocol === 'https:';\n\n // For situations where the auth-backend is running on a\n // different domain than the app, we set the SameSite attribute\n // to 'none' to allow third-party access to the cookie, but\n // only if it's in a secure context (https).\n let sameSite: ReturnType<CookieConfigurer>['sameSite'] = 'lax';\n if (new URL(appOrigin).hostname !== domain && secure) {\n sameSite = 'none';\n }\n\n // If the provider supports callbackUrls, the pathname will\n // contain the complete path to the frame handler so we need\n // to slice off the trailing part of the path.\n const path = pathname.endsWith(`${providerId}/handler/frame`)\n ? pathname.slice(0, -'/handler/frame'.length)\n : `${pathname}/${providerId}`;\n\n return { domain, path, secure, sameSite };\n};\n\n/** @internal */\nexport class OAuthCookieManager {\n private readonly cookieConfigurer: CookieConfigurer;\n private readonly nonceCookie: string;\n private readonly refreshTokenCookie: string;\n private readonly grantedScopeCookie: string;\n\n constructor(\n private readonly options: {\n providerId: string;\n defaultAppOrigin: string;\n baseUrl: string;\n callbackUrl: string;\n cookieConfigurer?: CookieConfigurer;\n },\n ) {\n this.cookieConfigurer = options.cookieConfigurer ?? defaultCookieConfigurer;\n\n this.nonceCookie = `${options.providerId}-nonce`;\n this.refreshTokenCookie = `${options.providerId}-refresh-token`;\n this.grantedScopeCookie = `${options.providerId}-granted-scope`;\n }\n\n private getConfig(origin?: string, pathSuffix: string = '') {\n const cookieConfig = this.cookieConfigurer({\n providerId: this.options.providerId,\n baseUrl: this.options.baseUrl,\n callbackUrl: this.options.callbackUrl,\n appOrigin: origin ?? this.options.defaultAppOrigin,\n });\n return {\n httpOnly: true,\n sameSite: 'lax' as const,\n ...cookieConfig,\n path: cookieConfig.path + pathSuffix,\n };\n }\n\n setNonce(res: Response, nonce: string, origin?: string): void {\n this.setCookie(\n res,\n this.nonceCookie,\n nonce,\n TEN_MINUTES_MS,\n origin,\n '/handler',\n );\n }\n\n setRefreshToken(res: Response, refreshToken: string, origin?: string): void {\n this.setCookie(\n res,\n this.refreshTokenCookie,\n refreshToken,\n THOUSAND_DAYS_MS,\n origin,\n );\n }\n\n removeRefreshToken(res: Response, origin?: string): void {\n this.removeCookie(res, this.refreshTokenCookie, origin);\n }\n\n removeGrantedScopes(res: Response, origin?: string): void {\n this.removeCookie(res, this.grantedScopeCookie, origin);\n }\n\n setGrantedScopes(res: Response, scope: string, origin?: string): void {\n this.setCookie(\n res,\n this.grantedScopeCookie,\n scope,\n THOUSAND_DAYS_MS,\n origin,\n );\n }\n\n getNonce(req: Request): string | undefined {\n return this.getCookie(req, this.nonceCookie);\n }\n\n getRefreshToken(req: Request): string | undefined {\n return this.getCookie(req, this.refreshTokenCookie);\n }\n\n getGrantedScopes(req: Request): string | undefined {\n return this.getCookie(req, this.grantedScopeCookie);\n }\n\n private setCookie(\n res: Response,\n name: string,\n val: string,\n maxAge: number,\n origin?: string,\n pathSuffix: string = '',\n ): Response {\n const options = {\n maxAge,\n ...this.getConfig(origin, pathSuffix),\n };\n const req = res.req;\n let output = res;\n if (val.length > MAX_COOKIE_SIZE_CHARACTERS) {\n const nonChunkedFormatExists = !!req.cookies[name];\n if (nonChunkedFormatExists) {\n output = output.cookie(name, '', this.getRemoveCookieOptions());\n }\n\n const chunked = this.splitCookieToChunks(val, MAX_COOKIE_SIZE_CHARACTERS);\n chunked.forEach((value, chunkNumber) => {\n output = output.cookie(\n OAuthCookieManager.getCookieChunkName(name, chunkNumber),\n value,\n options,\n );\n });\n return output;\n }\n\n const chunkedFormatExists = OAuthCookieManager.chunkedCookieExists(\n req,\n name,\n );\n if (chunkedFormatExists) {\n for (let chunkNumber = 0; ; chunkNumber++) {\n const key = OAuthCookieManager.getCookieChunkName(name, chunkNumber);\n const exists = !!req.cookies[key];\n if (!exists) {\n break;\n }\n output = output.cookie(key, '', this.getRemoveCookieOptions());\n }\n }\n\n return output.cookie(name, val, options);\n }\n\n private getCookie(req: Request, name: string): string | undefined {\n const isChunked = OAuthCookieManager.chunkedCookieExists(req, name);\n if (isChunked) {\n const chunks: string[] = [];\n let chunkNumber = 0;\n let chunk =\n req.cookies[OAuthCookieManager.getCookieChunkName(name, chunkNumber)];\n while (chunk) {\n chunks.push(chunk);\n chunkNumber++;\n chunk =\n req.cookies[OAuthCookieManager.getCookieChunkName(name, chunkNumber)];\n }\n return chunks.join('');\n }\n return req.cookies[name];\n }\n\n private removeCookie(res: Response, name: string, origin?: string): Response {\n const req = res.req;\n const options = this.getRemoveCookieOptions(origin);\n const isChunked = OAuthCookieManager.chunkedCookieExists(req, name);\n if (isChunked) {\n const nonChunkedFormatExists = !!req.cookies[name];\n let output: Response = nonChunkedFormatExists\n ? res.cookie(name, '', options)\n : res;\n for (let chunkNumber = 0; ; chunkNumber++) {\n const key = OAuthCookieManager.getCookieChunkName(name, chunkNumber);\n const exists = !!req.cookies[key];\n if (!exists) {\n break;\n }\n output = output.cookie(key, '', options);\n }\n return output;\n }\n return res.cookie(name, '', options);\n }\n\n private splitCookieToChunks(val: string, chunkSize: number): string[] {\n const numChunks = Math.ceil(val.length / chunkSize);\n const chunks: string[] = Array<string>(numChunks);\n\n let offset: number = 0;\n for (let i = 0; i < numChunks; i++) {\n chunks[i] = val.substring(offset, offset + chunkSize);\n offset += chunkSize;\n }\n return chunks;\n }\n\n private static chunkedCookieExists(req: Request, name: string): boolean {\n return !!req.cookies[OAuthCookieManager.getCookieChunkName(name, 0)];\n }\n\n private static getCookieChunkName(name: string, chunkIndex: number): string {\n return `${name}-${chunkIndex}`;\n }\n\n private getRemoveCookieOptions(origin?: string): CookieOptions {\n return {\n maxAge: 0,\n ...this.getConfig(origin),\n };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -73,8 +73,9 @@ function createOAuthRouteHandlers(options) {
|
|
|
73
73
|
},
|
|
74
74
|
async frameHandler(req, res) {
|
|
75
75
|
let origin = defaultAppOrigin;
|
|
76
|
+
let state$1;
|
|
76
77
|
try {
|
|
77
|
-
|
|
78
|
+
state$1 = state.decodeOAuthState(req.query.state?.toString() ?? "");
|
|
78
79
|
if (state$1.origin) {
|
|
79
80
|
try {
|
|
80
81
|
origin = new url.URL(state$1.origin).origin;
|
|
@@ -138,10 +139,16 @@ function createOAuthRouteHandlers(options) {
|
|
|
138
139
|
});
|
|
139
140
|
} catch (error) {
|
|
140
141
|
const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
error
|
|
144
|
-
|
|
142
|
+
if (state$1?.flow === "redirect" && state$1?.redirectUrl) {
|
|
143
|
+
const redirectUrl = new url.URL(state$1.redirectUrl);
|
|
144
|
+
redirectUrl.searchParams.set("error", message);
|
|
145
|
+
res.redirect(redirectUrl.toString());
|
|
146
|
+
} else {
|
|
147
|
+
sendWebMessageResponse.sendWebMessageResponse(res, origin, {
|
|
148
|
+
type: "authorization_response",
|
|
149
|
+
error: { name, message }
|
|
150
|
+
});
|
|
151
|
+
}
|
|
145
152
|
}
|
|
146
153
|
},
|
|
147
154
|
async logout(req, res) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createOAuthRouteHandlers.cjs.js","sources":["../../src/oauth/createOAuthRouteHandlers.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express from 'express';\nimport crypto from 'crypto';\nimport { URL } from 'url';\nimport {\n AuthenticationError,\n InputError,\n isError,\n NotAllowedError,\n} from '@backstage/errors';\nimport {\n encodeOAuthState,\n decodeOAuthState,\n OAuthStateTransform,\n} from './state';\nimport { sendWebMessageResponse } from '../flow';\nimport { prepareBackstageIdentityResponse } from '../identity';\nimport { OAuthCookieManager } from './OAuthCookieManager';\nimport {\n AuthProviderRouteHandlers,\n AuthResolverContext,\n ClientAuthResponse,\n CookieConfigurer,\n ProfileTransform,\n SignInResolver,\n} from '../types';\nimport { OAuthAuthenticator, OAuthAuthenticatorResult } from './types';\nimport { Config } from '@backstage/config';\nimport { CookieScopeManager } from './CookieScopeManager';\n\n/** @public */\nexport interface OAuthRouteHandlersOptions<TProfile> {\n authenticator: OAuthAuthenticator<any, TProfile>;\n appUrl: string;\n baseUrl: string;\n isOriginAllowed: (origin: string) => boolean;\n providerId: string;\n config: Config;\n resolverContext: AuthResolverContext;\n additionalScopes?: string[];\n stateTransform?: OAuthStateTransform;\n profileTransform?: ProfileTransform<OAuthAuthenticatorResult<TProfile>>;\n cookieConfigurer?: CookieConfigurer;\n signInResolver?: SignInResolver<OAuthAuthenticatorResult<TProfile>>;\n}\n\n/** @internal */\ntype ClientOAuthResponse = ClientAuthResponse<{\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * (Optional) Id token issued for the signed in user.\n */\n idToken?: string;\n /**\n * Expiry of the access token in seconds.\n */\n expiresInSeconds?: number;\n /**\n * Scopes granted for the access token.\n */\n scope: string;\n}>;\n\n/** @public */\nexport function createOAuthRouteHandlers<TProfile>(\n options: OAuthRouteHandlersOptions<TProfile>,\n): AuthProviderRouteHandlers {\n const {\n authenticator,\n config,\n baseUrl,\n appUrl,\n providerId,\n isOriginAllowed,\n cookieConfigurer,\n resolverContext,\n signInResolver,\n } = options;\n\n const defaultAppOrigin = new URL(appUrl).origin;\n const callbackUrl =\n config.getOptionalString('callbackUrl') ??\n `${baseUrl}/${providerId}/handler/frame`;\n\n const stateTransform = options.stateTransform ?? (state => ({ state }));\n const profileTransform =\n options.profileTransform ?? authenticator.defaultProfileTransform;\n const authenticatorCtx = authenticator.initialize({ config, callbackUrl });\n const cookieManager = new OAuthCookieManager({\n baseUrl,\n callbackUrl,\n defaultAppOrigin,\n providerId,\n cookieConfigurer,\n });\n\n const scopeManager = CookieScopeManager.create({\n config,\n authenticator,\n cookieManager,\n additionalScopes: options.additionalScopes,\n });\n\n return {\n async start(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const env = req.query.env?.toString();\n const origin = req.query.origin?.toString();\n const redirectUrl = req.query.redirectUrl?.toString();\n const flow = req.query.flow?.toString();\n\n if (!env) {\n throw new InputError('No env provided in request query parameters');\n }\n\n const nonce = crypto.randomBytes(16).toString('base64');\n // set a nonce cookie before redirecting to oauth provider\n cookieManager.setNonce(res, nonce, origin);\n\n const { scope, scopeState } = await scopeManager.start(req);\n\n const state = { nonce, env, origin, redirectUrl, flow, ...scopeState };\n const { state: transformedState } = await stateTransform(state, { req });\n\n const { url, status } = await options.authenticator.start(\n {\n req,\n scope,\n state: encodeOAuthState(transformedState),\n },\n authenticatorCtx,\n );\n\n res.statusCode = status || 302;\n res.setHeader('Location', url);\n res.setHeader('Content-Length', '0');\n res.end();\n },\n\n async frameHandler(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n let origin = defaultAppOrigin;\n\n try {\n const state = decodeOAuthState(req.query.state?.toString() ?? '');\n\n if (state.origin) {\n try {\n origin = new URL(state.origin).origin;\n } catch {\n throw new NotAllowedError('App origin is invalid, failed to parse');\n }\n if (!isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n }\n\n // The same nonce is passed through cookie and state, and they must match\n const cookieNonce = cookieManager.getNonce(req);\n const stateNonce = state.nonce;\n if (!cookieNonce) {\n throw new NotAllowedError('Auth response is missing cookie nonce');\n }\n if (cookieNonce !== stateNonce) {\n throw new NotAllowedError('Invalid nonce');\n }\n\n const result = await authenticator.authenticate(\n { req },\n authenticatorCtx,\n );\n const { profile } = await profileTransform(result, resolverContext);\n\n const signInResult =\n signInResolver &&\n (await signInResolver({ profile, result }, resolverContext));\n\n const grantedScopes = await scopeManager.handleCallback(req, {\n result,\n state,\n origin,\n });\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScopes,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n ...(signInResult && {\n backstageIdentity: prepareBackstageIdentityResponse(signInResult),\n }),\n };\n\n if (result.session.refreshToken) {\n // set new refresh token\n cookieManager.setRefreshToken(\n res,\n result.session.refreshToken,\n origin,\n );\n }\n\n // When using the redirect flow we rely on refresh token we just\n // acquired to get a new session once we're back in the app.\n if (state.flow === 'redirect') {\n if (!state.redirectUrl) {\n throw new InputError(\n 'No redirectUrl provided in request query parameters',\n );\n }\n res.redirect(state.redirectUrl);\n return;\n }\n\n // post message back to popup if successful\n sendWebMessageResponse(res, origin, {\n type: 'authorization_response',\n response,\n });\n } catch (error) {\n const { name, message } = isError(error)\n ? error\n : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value\n // post error message back to popup if failure\n sendWebMessageResponse(res, origin, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n },\n\n async logout(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n if (authenticator.logout) {\n const refreshToken = cookieManager.getRefreshToken(req);\n await authenticator.logout({ req, refreshToken }, authenticatorCtx);\n }\n\n // remove refresh token cookie if it is set\n cookieManager.removeRefreshToken(res, req.get('origin'));\n\n // remove persisted scopes\n await scopeManager.clear(req);\n\n res.status(200).end();\n },\n\n async refresh(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n try {\n const refreshToken = cookieManager.getRefreshToken(req);\n\n // throw error if refresh token is missing in the request\n if (!refreshToken) {\n throw new InputError('Missing session cookie');\n }\n\n const scopeRefresh = await scopeManager.refresh(req);\n\n const result = await authenticator.refresh(\n { req, scope: scopeRefresh.scope, refreshToken },\n authenticatorCtx,\n );\n\n const grantedScope = await scopeRefresh.commit(result);\n\n const { profile } = await profileTransform(result, resolverContext);\n\n const newRefreshToken = result.session.refreshToken;\n if (newRefreshToken && newRefreshToken !== refreshToken) {\n cookieManager.setRefreshToken(\n res,\n newRefreshToken,\n req.get('origin'),\n );\n }\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScope,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n };\n\n if (signInResolver) {\n const identity = await signInResolver(\n { profile, result },\n resolverContext,\n );\n response.backstageIdentity =\n prepareBackstageIdentityResponse(identity);\n }\n\n res.status(200).json(response);\n } catch (error) {\n throw new AuthenticationError('Refresh failed', error);\n }\n },\n };\n}\n"],"names":["URL","OAuthCookieManager","CookieScopeManager","InputError","crypto","state","encodeOAuthState","decodeOAuthState","NotAllowedError","prepareBackstageIdentityResponse","sendWebMessageResponse","isError","AuthenticationError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createOAuthRouteHandlers.cjs.js","sources":["../../src/oauth/createOAuthRouteHandlers.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express from 'express';\nimport crypto from 'crypto';\nimport { URL } from 'url';\nimport {\n AuthenticationError,\n InputError,\n isError,\n NotAllowedError,\n} from '@backstage/errors';\nimport {\n encodeOAuthState,\n decodeOAuthState,\n OAuthStateTransform,\n} from './state';\nimport { sendWebMessageResponse } from '../flow';\nimport { prepareBackstageIdentityResponse } from '../identity';\nimport { OAuthCookieManager } from './OAuthCookieManager';\nimport {\n AuthProviderRouteHandlers,\n AuthResolverContext,\n ClientAuthResponse,\n CookieConfigurer,\n ProfileTransform,\n SignInResolver,\n} from '../types';\nimport { OAuthAuthenticator, OAuthAuthenticatorResult } from './types';\nimport { Config } from '@backstage/config';\nimport { CookieScopeManager } from './CookieScopeManager';\n\n/** @public */\nexport interface OAuthRouteHandlersOptions<TProfile> {\n authenticator: OAuthAuthenticator<any, TProfile>;\n appUrl: string;\n baseUrl: string;\n isOriginAllowed: (origin: string) => boolean;\n providerId: string;\n config: Config;\n resolverContext: AuthResolverContext;\n additionalScopes?: string[];\n stateTransform?: OAuthStateTransform;\n profileTransform?: ProfileTransform<OAuthAuthenticatorResult<TProfile>>;\n cookieConfigurer?: CookieConfigurer;\n signInResolver?: SignInResolver<OAuthAuthenticatorResult<TProfile>>;\n}\n\n/** @internal */\ntype ClientOAuthResponse = ClientAuthResponse<{\n /**\n * An access token issued for the signed in user.\n */\n accessToken: string;\n /**\n * (Optional) Id token issued for the signed in user.\n */\n idToken?: string;\n /**\n * Expiry of the access token in seconds.\n */\n expiresInSeconds?: number;\n /**\n * Scopes granted for the access token.\n */\n scope: string;\n}>;\n\n/** @public */\nexport function createOAuthRouteHandlers<TProfile>(\n options: OAuthRouteHandlersOptions<TProfile>,\n): AuthProviderRouteHandlers {\n const {\n authenticator,\n config,\n baseUrl,\n appUrl,\n providerId,\n isOriginAllowed,\n cookieConfigurer,\n resolverContext,\n signInResolver,\n } = options;\n\n const defaultAppOrigin = new URL(appUrl).origin;\n const callbackUrl =\n config.getOptionalString('callbackUrl') ??\n `${baseUrl}/${providerId}/handler/frame`;\n\n const stateTransform = options.stateTransform ?? (state => ({ state }));\n const profileTransform =\n options.profileTransform ?? authenticator.defaultProfileTransform;\n const authenticatorCtx = authenticator.initialize({ config, callbackUrl });\n const cookieManager = new OAuthCookieManager({\n baseUrl,\n callbackUrl,\n defaultAppOrigin,\n providerId,\n cookieConfigurer,\n });\n\n const scopeManager = CookieScopeManager.create({\n config,\n authenticator,\n cookieManager,\n additionalScopes: options.additionalScopes,\n });\n\n return {\n async start(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const env = req.query.env?.toString();\n const origin = req.query.origin?.toString();\n const redirectUrl = req.query.redirectUrl?.toString();\n const flow = req.query.flow?.toString();\n\n if (!env) {\n throw new InputError('No env provided in request query parameters');\n }\n\n const nonce = crypto.randomBytes(16).toString('base64');\n // set a nonce cookie before redirecting to oauth provider\n cookieManager.setNonce(res, nonce, origin);\n\n const { scope, scopeState } = await scopeManager.start(req);\n\n const state = { nonce, env, origin, redirectUrl, flow, ...scopeState };\n const { state: transformedState } = await stateTransform(state, { req });\n\n const { url, status } = await options.authenticator.start(\n {\n req,\n scope,\n state: encodeOAuthState(transformedState),\n },\n authenticatorCtx,\n );\n\n res.statusCode = status || 302;\n res.setHeader('Location', url);\n res.setHeader('Content-Length', '0');\n res.end();\n },\n\n async frameHandler(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n let origin = defaultAppOrigin;\n let state;\n\n try {\n state = decodeOAuthState(req.query.state?.toString() ?? '');\n\n if (state.origin) {\n try {\n origin = new URL(state.origin).origin;\n } catch {\n throw new NotAllowedError('App origin is invalid, failed to parse');\n }\n if (!isOriginAllowed(origin)) {\n throw new NotAllowedError(`Origin '${origin}' is not allowed`);\n }\n }\n\n // The same nonce is passed through cookie and state, and they must match\n const cookieNonce = cookieManager.getNonce(req);\n const stateNonce = state.nonce;\n if (!cookieNonce) {\n throw new NotAllowedError('Auth response is missing cookie nonce');\n }\n if (cookieNonce !== stateNonce) {\n throw new NotAllowedError('Invalid nonce');\n }\n\n const result = await authenticator.authenticate(\n { req },\n authenticatorCtx,\n );\n const { profile } = await profileTransform(result, resolverContext);\n\n const signInResult =\n signInResolver &&\n (await signInResolver({ profile, result }, resolverContext));\n\n const grantedScopes = await scopeManager.handleCallback(req, {\n result,\n state,\n origin,\n });\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScopes,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n ...(signInResult && {\n backstageIdentity: prepareBackstageIdentityResponse(signInResult),\n }),\n };\n\n if (result.session.refreshToken) {\n // set new refresh token\n cookieManager.setRefreshToken(\n res,\n result.session.refreshToken,\n origin,\n );\n }\n\n // When using the redirect flow we rely on refresh token we just\n // acquired to get a new session once we're back in the app.\n if (state.flow === 'redirect') {\n if (!state.redirectUrl) {\n throw new InputError(\n 'No redirectUrl provided in request query parameters',\n );\n }\n res.redirect(state.redirectUrl);\n return;\n }\n\n // post message back to popup if successful\n sendWebMessageResponse(res, origin, {\n type: 'authorization_response',\n response,\n });\n } catch (error) {\n const { name, message } = isError(error)\n ? error\n : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value\n\n if (state?.flow === 'redirect' && state?.redirectUrl) {\n const redirectUrl = new URL(state.redirectUrl);\n redirectUrl.searchParams.set('error', message);\n\n // set the error in a cookie and redirect user back to sign in where the error can be rendered\n res.redirect(redirectUrl.toString());\n } else {\n // post error message back to popup if failure\n sendWebMessageResponse(res, origin, {\n type: 'authorization_response',\n error: { name, message },\n });\n }\n }\n },\n\n async logout(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n if (authenticator.logout) {\n const refreshToken = cookieManager.getRefreshToken(req);\n await authenticator.logout({ req, refreshToken }, authenticatorCtx);\n }\n\n // remove refresh token cookie if it is set\n cookieManager.removeRefreshToken(res, req.get('origin'));\n\n // remove persisted scopes\n await scopeManager.clear(req);\n\n res.status(200).end();\n },\n\n async refresh(\n this: never,\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n // We use this as a lightweight CSRF protection\n if (req.header('X-Requested-With') !== 'XMLHttpRequest') {\n throw new AuthenticationError('Invalid X-Requested-With header');\n }\n\n try {\n const refreshToken = cookieManager.getRefreshToken(req);\n\n // throw error if refresh token is missing in the request\n if (!refreshToken) {\n throw new InputError('Missing session cookie');\n }\n\n const scopeRefresh = await scopeManager.refresh(req);\n\n const result = await authenticator.refresh(\n { req, scope: scopeRefresh.scope, refreshToken },\n authenticatorCtx,\n );\n\n const grantedScope = await scopeRefresh.commit(result);\n\n const { profile } = await profileTransform(result, resolverContext);\n\n const newRefreshToken = result.session.refreshToken;\n if (newRefreshToken && newRefreshToken !== refreshToken) {\n cookieManager.setRefreshToken(\n res,\n newRefreshToken,\n req.get('origin'),\n );\n }\n\n const response: ClientOAuthResponse = {\n profile,\n providerInfo: {\n idToken: result.session.idToken,\n accessToken: result.session.accessToken,\n scope: grantedScope,\n expiresInSeconds: result.session.expiresInSeconds,\n },\n };\n\n if (signInResolver) {\n const identity = await signInResolver(\n { profile, result },\n resolverContext,\n );\n response.backstageIdentity =\n prepareBackstageIdentityResponse(identity);\n }\n\n res.status(200).json(response);\n } catch (error) {\n throw new AuthenticationError('Refresh failed', error);\n }\n },\n };\n}\n"],"names":["URL","OAuthCookieManager","CookieScopeManager","InputError","crypto","state","encodeOAuthState","decodeOAuthState","NotAllowedError","prepareBackstageIdentityResponse","sendWebMessageResponse","isError","AuthenticationError"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-auth-node",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4-next.0",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "node-library",
|
|
6
6
|
"pluginId": "auth",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@backstage/backend-common": "^0.25.0",
|
|
41
|
-
"@backstage/backend-plugin-api": "1.0.
|
|
42
|
-
"@backstage/catalog-client": "1.
|
|
41
|
+
"@backstage/backend-plugin-api": "1.0.2-next.0",
|
|
42
|
+
"@backstage/catalog-client": "1.8.0-next.0",
|
|
43
43
|
"@backstage/catalog-model": "1.7.0",
|
|
44
44
|
"@backstage/config": "1.2.0",
|
|
45
45
|
"@backstage/errors": "1.2.4",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"zod-validation-error": "^3.4.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@backstage/backend-test-utils": "1.0.
|
|
61
|
-
"@backstage/cli": "0.
|
|
60
|
+
"@backstage/backend-test-utils": "1.0.3-next.0",
|
|
61
|
+
"@backstage/cli": "0.29.0-next.0",
|
|
62
62
|
"cookie-parser": "^1.4.6",
|
|
63
63
|
"express-promise-router": "^4.1.1",
|
|
64
64
|
"lodash": "^4.17.21",
|