@adobe/helix-html-pipeline 3.0.2 → 3.1.2
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 +21 -0
- package/package.json +7 -7
- package/src/PipelineState.d.ts +2 -0
- package/src/PipelineState.js +1 -0
- package/src/steps/authenticate.js +1 -1
- package/src/utils/auth.js +44 -17
- package/src/utils/idp-configs/microsoft.js +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [3.1.2](https://github.com/adobe/helix-html-pipeline/compare/v3.1.1...v3.1.2) (2022-07-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **deps:** update dependency @adobe/helix-markdown-support to v3.1.7 ([8dd9cf2](https://github.com/adobe/helix-html-pipeline/commit/8dd9cf22bd3ea1414d2028a84ad52ff8b31b1853))
|
|
7
|
+
|
|
8
|
+
## [3.1.1](https://github.com/adobe/helix-html-pipeline/compare/v3.1.0...v3.1.1) (2022-07-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deps:** update dependency mdast-util-to-hast to v12.1.2 ([1c863bc](https://github.com/adobe/helix-html-pipeline/commit/1c863bcb06ef8af8ac3c6367121fa3a71aad38f5))
|
|
14
|
+
|
|
15
|
+
# [3.1.0](https://github.com/adobe/helix-html-pipeline/compare/v3.0.2...v3.1.0) (2022-06-24)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* improve authentication implementation ([#90](https://github.com/adobe/helix-html-pipeline/issues/90)) ([def347c](https://github.com/adobe/helix-html-pipeline/commit/def347cd0d4860d2804d71be6b702a6be30d6095)), closes [#85](https://github.com/adobe/helix-html-pipeline/issues/85)
|
|
21
|
+
|
|
1
22
|
## [3.0.2](https://github.com/adobe/helix-html-pipeline/compare/v3.0.1...v3.0.2) (2022-06-16)
|
|
2
23
|
|
|
3
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-html-pipeline",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "Helix HTML Pipeline",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"node": ">=16.x"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@adobe/helix-markdown-support": "3.1.
|
|
36
|
+
"@adobe/helix-markdown-support": "3.1.7",
|
|
37
37
|
"@adobe/helix-shared-utils": "2.0.11",
|
|
38
38
|
"cookie": "0.5.0",
|
|
39
39
|
"github-slugger": "1.4.0",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"hast-util-to-html": "8.0.3",
|
|
43
43
|
"hast-util-to-string": "2.0.0",
|
|
44
44
|
"hastscript": "7.0.2",
|
|
45
|
-
"jose": "4.8.
|
|
45
|
+
"jose": "4.8.3",
|
|
46
46
|
"mdast-util-gfm-footnote": "1.0.1",
|
|
47
47
|
"mdast-util-gfm-strikethrough": "1.0.1",
|
|
48
48
|
"mdast-util-gfm-table": "1.0.4",
|
|
49
49
|
"mdast-util-gfm-task-list-item": "1.0.1",
|
|
50
|
-
"mdast-util-to-hast": "12.1.
|
|
50
|
+
"mdast-util-to-hast": "12.1.2",
|
|
51
51
|
"mdast-util-to-string": "3.1.0",
|
|
52
52
|
"micromark-extension-gfm-footnote": "1.0.4",
|
|
53
53
|
"micromark-extension-gfm-strikethrough": "1.0.4",
|
|
@@ -75,16 +75,16 @@
|
|
|
75
75
|
"@semantic-release/git": "10.0.1",
|
|
76
76
|
"@semantic-release/npm": "9.0.1",
|
|
77
77
|
"c8": "7.11.3",
|
|
78
|
-
"eslint": "8.
|
|
78
|
+
"eslint": "8.19.0",
|
|
79
79
|
"eslint-plugin-header": "3.1.1",
|
|
80
80
|
"eslint-plugin-import": "2.26.0",
|
|
81
81
|
"esmock": "1.7.5",
|
|
82
82
|
"husky": "8.0.1",
|
|
83
83
|
"js-yaml": "4.1.0",
|
|
84
84
|
"jsdoc-to-markdown": "7.1.1",
|
|
85
|
-
"jsdom": "
|
|
85
|
+
"jsdom": "20.0.0",
|
|
86
86
|
"junit-report-builder": "3.0.0",
|
|
87
|
-
"lint-staged": "13.0.
|
|
87
|
+
"lint-staged": "13.0.3",
|
|
88
88
|
"mocha": "10.0.0",
|
|
89
89
|
"mocha-multi-reporters": "1.5.1",
|
|
90
90
|
"remark-gfm": "3.0.1",
|
package/src/PipelineState.d.ts
CHANGED
|
@@ -44,11 +44,13 @@ declare interface PipelineOptions {
|
|
|
44
44
|
path: string;
|
|
45
45
|
contentBusId: string;
|
|
46
46
|
timer: PipelineTimer;
|
|
47
|
+
env: object;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
declare class PipelineState {
|
|
50
51
|
constructor(opts: PipelineOptions);
|
|
51
52
|
log: Console;
|
|
53
|
+
env: object;
|
|
52
54
|
info: PathInfo;
|
|
53
55
|
content: PipelineContent;
|
|
54
56
|
contentBusId: string;
|
package/src/PipelineState.js
CHANGED
|
@@ -78,6 +78,6 @@ export async function authenticate(state, req, res) {
|
|
|
78
78
|
res.headers.set('x-hlx-auth-iss', authInfo.profile.iss);
|
|
79
79
|
res.headers.set('x-hlx-auth-kid', authInfo.profile.kid);
|
|
80
80
|
res.headers.set('x-hlx-auth-aud', authInfo.profile.aud);
|
|
81
|
-
res.headers.set('x-hlx-auth-
|
|
81
|
+
res.headers.set('x-hlx-auth-key', authInfo.profile.pem);
|
|
82
82
|
}
|
|
83
83
|
}
|
package/src/utils/auth.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
// eslint-disable-next-line max-classes-per-file
|
|
13
13
|
import {
|
|
14
|
-
createLocalJWKSet, createRemoteJWKSet, decodeJwt, jwtVerify, UnsecuredJWT,
|
|
14
|
+
createLocalJWKSet, createRemoteJWKSet, decodeJwt, jwtVerify, UnsecuredJWT, exportSPKI,
|
|
15
15
|
} from 'jose';
|
|
16
16
|
import { clearAuthCookie, getAuthCookie, setAuthCookie } from './auth-cookie.js';
|
|
17
17
|
|
|
@@ -64,16 +64,38 @@ export async function decodeIdToken(state, idp, idToken, lenient = false) {
|
|
|
64
64
|
payload.ttl = payload.exp - Math.floor(Date.now() / 1000);
|
|
65
65
|
|
|
66
66
|
// export the public key
|
|
67
|
-
payload.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
67
|
+
payload.pem = await exportSPKI(key);
|
|
68
|
+
// and encode it base64 url
|
|
69
|
+
payload.pem = Buffer.from(payload.pem, 'utf-8').toString('base64url');
|
|
71
70
|
payload.kid = protectedHeader.kid;
|
|
72
71
|
|
|
73
72
|
log.info(`[auth] decoded id_token${lenient ? ' (lenient)' : ''} from ${payload.iss} and validated payload.`);
|
|
74
73
|
return payload;
|
|
75
74
|
}
|
|
76
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Returns the host of the request; falls back to the configured `host`.
|
|
78
|
+
* Note that this is different from the `config.host` calculation in `fetch-config-all`,
|
|
79
|
+
* as this prefers the xfh over the config.
|
|
80
|
+
*
|
|
81
|
+
* @param {PipelineState} state
|
|
82
|
+
* @param {PipelineRequest} req
|
|
83
|
+
* @return {string}
|
|
84
|
+
*/
|
|
85
|
+
function getRequestHost(state, req) {
|
|
86
|
+
// determine the location of 'this' document based on the xfh header. so that logins to
|
|
87
|
+
// .page stay on .page. etc. but fallback to the config.host if non set
|
|
88
|
+
let host = req.headers.get('x-forwarded-host');
|
|
89
|
+
if (host) {
|
|
90
|
+
host = host.split(',')[0].trim();
|
|
91
|
+
}
|
|
92
|
+
if (!host) {
|
|
93
|
+
host = state.config.host;
|
|
94
|
+
}
|
|
95
|
+
state.log.info(`request host is: ${host}`);
|
|
96
|
+
return host;
|
|
97
|
+
}
|
|
98
|
+
|
|
77
99
|
/**
|
|
78
100
|
* AuthInfo class
|
|
79
101
|
*/
|
|
@@ -160,13 +182,7 @@ export class AuthInfo {
|
|
|
160
182
|
|
|
161
183
|
// determine the location of 'this' document based on the xfh header. so that logins to
|
|
162
184
|
// .page stay on .page. etc. but fallback to the config.host if non set
|
|
163
|
-
|
|
164
|
-
if (host) {
|
|
165
|
-
host = host.split(',')[0].trim();
|
|
166
|
-
}
|
|
167
|
-
if (!host) {
|
|
168
|
-
host = state.config.host;
|
|
169
|
-
}
|
|
185
|
+
const host = getRequestHost(state, req);
|
|
170
186
|
if (!host) {
|
|
171
187
|
log.error('[auth] unable to create login redirect: no xfh or config.host.');
|
|
172
188
|
res.status = 401;
|
|
@@ -224,7 +240,7 @@ export class AuthInfo {
|
|
|
224
240
|
|
|
225
241
|
// ensure that the request is made to the target host
|
|
226
242
|
if (req.params.state?.requestHost) {
|
|
227
|
-
const host = req
|
|
243
|
+
const host = getRequestHost(state, req);
|
|
228
244
|
if (host !== req.params.state.requestHost) {
|
|
229
245
|
const url = new URL(`https://${req.params.state.requestHost}/.auth`);
|
|
230
246
|
url.searchParams.append('state', req.params.rawState);
|
|
@@ -330,15 +346,25 @@ export function initAuthRoute(state, req, res) {
|
|
|
330
346
|
}
|
|
331
347
|
|
|
332
348
|
/**
|
|
333
|
-
* Extracts the authentication info from the cookie
|
|
349
|
+
* Extracts the authentication info from the cookie or 'authorization' header.
|
|
350
|
+
* Returns {@code null} if missing or invalid.
|
|
334
351
|
*
|
|
335
352
|
* @param {PipelineState} state
|
|
336
353
|
* @param {PipelineRequest} req
|
|
337
354
|
* @returns {Promise<AuthInfo>} the authentication info or null if the request is not authenticated
|
|
338
355
|
*/
|
|
339
|
-
async function
|
|
356
|
+
async function getAuthInfoFromCookieOrHeader(state, req) {
|
|
340
357
|
const { log } = state;
|
|
341
|
-
|
|
358
|
+
let idToken = getAuthCookie(req);
|
|
359
|
+
if (!idToken) {
|
|
360
|
+
log.info('no auth cookie');
|
|
361
|
+
const [marker, value] = (req.headers.get('authorization') || '').split(' ');
|
|
362
|
+
if (marker.toLowerCase() === 'token' && value) {
|
|
363
|
+
idToken = value.trim();
|
|
364
|
+
} else {
|
|
365
|
+
log.info('no auth header');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
342
368
|
if (idToken) {
|
|
343
369
|
let idp;
|
|
344
370
|
try {
|
|
@@ -375,6 +401,7 @@ async function getAuthInfoFromCookie(state, req) {
|
|
|
375
401
|
return AuthInfo.Default().withCookieInvalid(true);
|
|
376
402
|
}
|
|
377
403
|
}
|
|
404
|
+
log.info('no id_token');
|
|
378
405
|
return null;
|
|
379
406
|
}
|
|
380
407
|
|
|
@@ -386,7 +413,7 @@ async function getAuthInfoFromCookie(state, req) {
|
|
|
386
413
|
*/
|
|
387
414
|
export async function getAuthInfo(state, req) {
|
|
388
415
|
const { log } = state;
|
|
389
|
-
const auth = await
|
|
416
|
+
const auth = await getAuthInfoFromCookieOrHeader(state, req);
|
|
390
417
|
if (auth) {
|
|
391
418
|
if (auth.authenticated) {
|
|
392
419
|
log.info(`[auth] id-token valid: iss=${auth.profile.iss}`);
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
export default {
|
|
13
13
|
name: 'microsoft',
|
|
14
14
|
mountType: 'onedrive',
|
|
15
|
-
client: () => ({
|
|
16
|
-
clientId:
|
|
17
|
-
clientSecret:
|
|
15
|
+
client: (state) => ({
|
|
16
|
+
clientId: state.env.HLX_SITE_APP_AZURE_CLIENT_ID,
|
|
17
|
+
clientSecret: state.env.HLX_SITE_APP_AZURE_CLIENT_SECRET,
|
|
18
18
|
}),
|
|
19
19
|
scope: 'openid profile email',
|
|
20
20
|
validateIssuer: (iss) => iss.startsWith('https://login.microsoftonline.com/'),
|