@atproto/oauth-provider 0.5.2 → 0.6.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 +37 -0
- package/dist/account/account-manager.d.ts +7 -5
- package/dist/account/account-manager.d.ts.map +1 -1
- package/dist/account/account-manager.js +34 -25
- package/dist/account/account-manager.js.map +1 -1
- package/dist/account/account-store.d.ts +7 -0
- package/dist/account/account-store.d.ts.map +1 -1
- package/dist/account/account-store.js.map +1 -1
- package/dist/account/account.d.ts +1 -11
- package/dist/account/account.d.ts.map +1 -1
- package/dist/account/{sign-up-data.d.ts → sign-up-input.d.ts} +3 -3
- package/dist/account/sign-up-input.d.ts.map +1 -0
- package/dist/account/{sign-up-data.js → sign-up-input.js} +3 -3
- package/dist/account/sign-up-input.js.map +1 -0
- package/dist/assets/assets-middleware.d.ts +2 -0
- package/dist/assets/assets-middleware.d.ts.map +1 -1
- package/dist/assets/assets-middleware.js +12 -14
- package/dist/assets/assets-middleware.js.map +1 -1
- package/dist/lib/csp/index.d.ts +5 -6
- package/dist/lib/csp/index.d.ts.map +1 -1
- package/dist/lib/csp/index.js +14 -11
- package/dist/lib/csp/index.js.map +1 -1
- package/dist/lib/hcaptcha.d.ts +15 -12
- package/dist/lib/hcaptcha.d.ts.map +1 -1
- package/dist/lib/hcaptcha.js +11 -7
- package/dist/lib/hcaptcha.js.map +1 -1
- package/dist/lib/html/build-document.d.ts +2 -2
- package/dist/lib/html/build-document.d.ts.map +1 -1
- package/dist/lib/html/build-document.js +11 -7
- package/dist/lib/html/build-document.js.map +1 -1
- package/dist/lib/html/html.d.ts.map +1 -1
- package/dist/lib/html/html.js +10 -13
- package/dist/lib/html/html.js.map +1 -1
- package/dist/lib/html/util.d.ts +0 -1
- package/dist/lib/html/util.d.ts.map +1 -1
- package/dist/lib/html/util.js +0 -4
- package/dist/lib/html/util.js.map +1 -1
- package/dist/lib/http/response.d.ts +3 -1
- package/dist/lib/http/response.d.ts.map +1 -1
- package/dist/lib/http/response.js +3 -0
- package/dist/lib/http/response.js.map +1 -1
- package/dist/lib/http/security-headers.d.ts +48 -0
- package/dist/lib/http/security-headers.d.ts.map +1 -0
- package/dist/lib/http/security-headers.js +62 -0
- package/dist/lib/http/security-headers.js.map +1 -0
- package/dist/lib/util/type.d.ts +8 -0
- package/dist/lib/util/type.d.ts.map +1 -1
- package/dist/lib/util/type.js.map +1 -1
- package/dist/oauth-hooks.d.ts +4 -25
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-provider.js +2 -2
- package/dist/oauth-provider.js.map +1 -1
- package/dist/output/backend-data.d.ts +4 -0
- package/dist/output/backend-data.d.ts.map +1 -0
- package/dist/output/backend-data.js +19 -0
- package/dist/output/backend-data.js.map +1 -0
- package/dist/output/build-authorize-data.d.ts +3 -19
- package/dist/output/build-authorize-data.d.ts.map +1 -1
- package/dist/output/build-authorize-data.js.map +1 -1
- package/dist/output/build-customization-data.d.ts +11 -18
- package/dist/output/build-customization-data.d.ts.map +1 -1
- package/dist/output/build-customization-data.js +1 -1
- package/dist/output/build-customization-data.js.map +1 -1
- package/dist/output/build-error-data.d.ts +3 -0
- package/dist/output/build-error-data.d.ts.map +1 -0
- package/dist/output/build-error-data.js +10 -0
- package/dist/output/build-error-data.js.map +1 -0
- package/dist/output/build-error-payload.d.ts +2 -1
- package/dist/output/build-error-payload.d.ts.map +1 -1
- package/dist/output/build-error-payload.js.map +1 -1
- package/dist/output/output-manager.d.ts +10 -4
- package/dist/output/output-manager.d.ts.map +1 -1
- package/dist/output/output-manager.js +68 -39
- package/dist/output/output-manager.js.map +1 -1
- package/dist/output/send-web-page.d.ts +6 -10
- package/dist/output/send-web-page.d.ts.map +1 -1
- package/dist/output/send-web-page.js +27 -47
- package/dist/output/send-web-page.js.map +1 -1
- package/dist/signer/signed-token-payload.d.ts +3 -3
- package/dist/signer/signer.d.ts +2 -2
- package/package.json +8 -41
- package/src/account/account-manager.ts +55 -34
- package/src/account/account-store.ts +8 -0
- package/src/account/account.ts +1 -14
- package/src/account/{sign-up-data.ts → sign-up-input.ts} +2 -2
- package/src/assets/assets-middleware.ts +11 -17
- package/src/lib/csp/index.ts +16 -13
- package/src/lib/hcaptcha.ts +14 -10
- package/src/lib/html/build-document.ts +15 -8
- package/src/lib/html/html.ts +11 -18
- package/src/lib/html/util.ts +0 -4
- package/src/lib/http/response.ts +9 -1
- package/src/lib/http/security-headers.ts +91 -0
- package/src/lib/util/type.ts +18 -0
- package/src/oauth-hooks.ts +4 -25
- package/src/oauth-provider.ts +2 -2
- package/src/output/backend-data.ts +18 -0
- package/src/output/build-authorize-data.ts +3 -26
- package/src/output/build-customization-data.ts +2 -13
- package/src/output/build-error-data.ts +8 -0
- package/src/output/build-error-payload.ts +4 -2
- package/src/output/output-manager.ts +86 -47
- package/src/output/send-web-page.ts +29 -58
- package/tsconfig.backend.json +1 -2
- package/tsconfig.backend.tsbuildinfo +1 -1
- package/tsconfig.json +1 -5
- package/.linguirc +0 -57
- package/dist/account/sign-up-data.d.ts.map +0 -1
- package/dist/account/sign-up-data.js.map +0 -1
- package/dist/assets/app/bundle-manifest.json +0 -614
- package/dist/assets/app/index-DZHZ9kCP.js +0 -36
- package/dist/assets/app/index-DZHZ9kCP.js.map +0 -1
- package/dist/assets/app/main-B_dNxQo_.js +0 -4
- package/dist/assets/app/main-B_dNxQo_.js.map +0 -1
- package/dist/assets/app/main-Dr6y26KY.css +0 -3
- package/dist/assets/app/main-Dr6y26KY.js +0 -306
- package/dist/assets/app/main-Dr6y26KY.js.map +0 -1
- package/dist/assets/app/messages-6_mYuGzB.js +0 -4
- package/dist/assets/app/messages-6_mYuGzB.js.map +0 -1
- package/dist/assets/app/messages-7wdeBTpD.js +0 -4
- package/dist/assets/app/messages-7wdeBTpD.js.map +0 -1
- package/dist/assets/app/messages-B-YFoWKc.js +0 -4
- package/dist/assets/app/messages-B-YFoWKc.js.map +0 -1
- package/dist/assets/app/messages-B10DUOE-.js +0 -4
- package/dist/assets/app/messages-B10DUOE-.js.map +0 -1
- package/dist/assets/app/messages-B4AwFEeZ.js +0 -4
- package/dist/assets/app/messages-B4AwFEeZ.js.map +0 -1
- package/dist/assets/app/messages-BDP8MyEC.js +0 -4
- package/dist/assets/app/messages-BDP8MyEC.js.map +0 -1
- package/dist/assets/app/messages-BIS87lxQ.js +0 -4
- package/dist/assets/app/messages-BIS87lxQ.js.map +0 -1
- package/dist/assets/app/messages-BI_Wbjdt.js +0 -4
- package/dist/assets/app/messages-BI_Wbjdt.js.map +0 -1
- package/dist/assets/app/messages-BMAouhRx.js +0 -4
- package/dist/assets/app/messages-BMAouhRx.js.map +0 -1
- package/dist/assets/app/messages-BdckMnJj.js +0 -4
- package/dist/assets/app/messages-BdckMnJj.js.map +0 -1
- package/dist/assets/app/messages-BgBLzc46.js +0 -4
- package/dist/assets/app/messages-BgBLzc46.js.map +0 -1
- package/dist/assets/app/messages-BobD78yK.js +0 -4
- package/dist/assets/app/messages-BobD78yK.js.map +0 -1
- package/dist/assets/app/messages-BtThT9UZ.js +0 -4
- package/dist/assets/app/messages-BtThT9UZ.js.map +0 -1
- package/dist/assets/app/messages-BwKHkbeh.js +0 -4
- package/dist/assets/app/messages-BwKHkbeh.js.map +0 -1
- package/dist/assets/app/messages-C417YUvA.js +0 -4
- package/dist/assets/app/messages-C417YUvA.js.map +0 -1
- package/dist/assets/app/messages-C4CxO4bO.js +0 -4
- package/dist/assets/app/messages-C4CxO4bO.js.map +0 -1
- package/dist/assets/app/messages-C5vd04e6.js +0 -4
- package/dist/assets/app/messages-C5vd04e6.js.map +0 -1
- package/dist/assets/app/messages-CAri2Wnz.js +0 -4
- package/dist/assets/app/messages-CAri2Wnz.js.map +0 -1
- package/dist/assets/app/messages-CPtWTZeG.js +0 -4
- package/dist/assets/app/messages-CPtWTZeG.js.map +0 -1
- package/dist/assets/app/messages-CiaM5zm8.js +0 -4
- package/dist/assets/app/messages-CiaM5zm8.js.map +0 -1
- package/dist/assets/app/messages-CkL-L2R6.js +0 -4
- package/dist/assets/app/messages-CkL-L2R6.js.map +0 -1
- package/dist/assets/app/messages-Cy_4XLNe.js +0 -4
- package/dist/assets/app/messages-Cy_4XLNe.js.map +0 -1
- package/dist/assets/app/messages-D5_ad-Eo.js +0 -4
- package/dist/assets/app/messages-D5_ad-Eo.js.map +0 -1
- package/dist/assets/app/messages-DChMl9mT.js +0 -4
- package/dist/assets/app/messages-DChMl9mT.js.map +0 -1
- package/dist/assets/app/messages-DWX-DIfv.js +0 -4
- package/dist/assets/app/messages-DWX-DIfv.js.map +0 -1
- package/dist/assets/app/messages-DgfsOphe.js +0 -4
- package/dist/assets/app/messages-DgfsOphe.js.map +0 -1
- package/dist/assets/app/messages-Dj5B_DR6.js +0 -4
- package/dist/assets/app/messages-Dj5B_DR6.js.map +0 -1
- package/dist/assets/app/messages-Dwzqo4eA.js +0 -4
- package/dist/assets/app/messages-Dwzqo4eA.js.map +0 -1
- package/dist/assets/app/messages-ESCIXJR7.js +0 -4
- package/dist/assets/app/messages-ESCIXJR7.js.map +0 -1
- package/dist/assets/app/messages-dglB2edb.js +0 -4
- package/dist/assets/app/messages-dglB2edb.js.map +0 -1
- package/dist/assets/app/messages-e_ClRrWc.js +0 -4
- package/dist/assets/app/messages-e_ClRrWc.js.map +0 -1
- package/dist/assets/app/messages-evvDxmrP.js +0 -4
- package/dist/assets/app/messages-evvDxmrP.js.map +0 -1
- package/dist/assets/app/messages-pPbdLb5B.js +0 -4
- package/dist/assets/app/messages-pPbdLb5B.js.map +0 -1
- package/dist/assets/app/messages-tJv8gHL2.js +0 -4
- package/dist/assets/app/messages-tJv8gHL2.js.map +0 -1
- package/dist/assets/app/messages-vLRVEw96.js +0 -4
- package/dist/assets/app/messages-vLRVEw96.js.map +0 -1
- package/dist/assets/asset.d.ts +0 -9
- package/dist/assets/asset.d.ts.map +0 -1
- package/dist/assets/asset.js +0 -3
- package/dist/assets/asset.js.map +0 -1
- package/dist/assets/index.d.ts +0 -5
- package/dist/assets/index.d.ts.map +0 -1
- package/dist/assets/index.js +0 -78
- package/dist/assets/index.js.map +0 -1
- package/rollup.config.js +0 -98
- package/src/assets/app/app.tsx +0 -43
- package/src/assets/app/backend-data.ts +0 -27
- package/src/assets/app/backend-types.ts +0 -66
- package/src/assets/app/components/forms/button-toggle-visibility.tsx +0 -43
- package/src/assets/app/components/forms/button.tsx +0 -60
- package/src/assets/app/components/forms/fieldset.tsx +0 -55
- package/src/assets/app/components/forms/form-card-async.tsx +0 -103
- package/src/assets/app/components/forms/form-card.tsx +0 -49
- package/src/assets/app/components/forms/input-checkbox.tsx +0 -78
- package/src/assets/app/components/forms/input-container.tsx +0 -107
- package/src/assets/app/components/forms/input-email-address.tsx +0 -65
- package/src/assets/app/components/forms/input-new-password.tsx +0 -62
- package/src/assets/app/components/forms/input-password.tsx +0 -87
- package/src/assets/app/components/forms/input-text.tsx +0 -82
- package/src/assets/app/components/forms/input-token.tsx +0 -94
- package/src/assets/app/components/forms/wizard-card.tsx +0 -116
- package/src/assets/app/components/layouts/layout-title-page.tsx +0 -77
- package/src/assets/app/components/layouts/layout-welcome.tsx +0 -73
- package/src/assets/app/components/utils/account-identifier.tsx +0 -23
- package/src/assets/app/components/utils/account-image.tsx +0 -33
- package/src/assets/app/components/utils/admonition.tsx +0 -52
- package/src/assets/app/components/utils/client-name.tsx +0 -45
- package/src/assets/app/components/utils/error-card.tsx +0 -93
- package/src/assets/app/components/utils/error-message.tsx +0 -88
- package/src/assets/app/components/utils/help-card.tsx +0 -46
- package/src/assets/app/components/utils/icons.tsx +0 -88
- package/src/assets/app/components/utils/link-anchor.tsx +0 -28
- package/src/assets/app/components/utils/link-title.tsx +0 -26
- package/src/assets/app/components/utils/multi-lang-string.tsx +0 -56
- package/src/assets/app/components/utils/password-strength-label.tsx +0 -37
- package/src/assets/app/components/utils/password-strength-meter.tsx +0 -58
- package/src/assets/app/components/utils/url-viewer.tsx +0 -73
- package/src/assets/app/cookies.ts +0 -11
- package/src/assets/app/hooks/use-api.ts +0 -178
- package/src/assets/app/hooks/use-async-action.ts +0 -120
- package/src/assets/app/hooks/use-bound-dispatch.ts +0 -5
- package/src/assets/app/hooks/use-browser-color-scheme.ts +0 -31
- package/src/assets/app/hooks/use-csrf-token.ts +0 -5
- package/src/assets/app/hooks/use-random-string.ts +0 -37
- package/src/assets/app/hooks/use-stepper.ts +0 -87
- package/src/assets/app/index.html +0 -182
- package/src/assets/app/lib/api.ts +0 -289
- package/src/assets/app/lib/clsx.ts +0 -6
- package/src/assets/app/lib/json-client.ts +0 -94
- package/src/assets/app/lib/password.ts +0 -98
- package/src/assets/app/lib/ref.ts +0 -17
- package/src/assets/app/lib/util.ts +0 -13
- package/src/assets/app/locales/an/messages.po +0 -490
- package/src/assets/app/locales/ast/messages.po +0 -490
- package/src/assets/app/locales/ca/messages.po +0 -490
- package/src/assets/app/locales/da/messages.po +0 -490
- package/src/assets/app/locales/de/messages.po +0 -490
- package/src/assets/app/locales/el/messages.po +0 -490
- package/src/assets/app/locales/en/messages.po +0 -490
- package/src/assets/app/locales/en-GB/messages.po +0 -490
- package/src/assets/app/locales/es/messages.po +0 -490
- package/src/assets/app/locales/eu/messages.po +0 -490
- package/src/assets/app/locales/fi/messages.po +0 -490
- package/src/assets/app/locales/fr/messages.po +0 -490
- package/src/assets/app/locales/ga/messages.po +0 -490
- package/src/assets/app/locales/gl/messages.po +0 -490
- package/src/assets/app/locales/hi/messages.po +0 -490
- package/src/assets/app/locales/hu/messages.po +0 -490
- package/src/assets/app/locales/ia/messages.po +0 -490
- package/src/assets/app/locales/id/messages.po +0 -490
- package/src/assets/app/locales/it/messages.po +0 -490
- package/src/assets/app/locales/ja/messages.po +0 -490
- package/src/assets/app/locales/km/messages.po +0 -490
- package/src/assets/app/locales/ko/messages.po +0 -490
- package/src/assets/app/locales/load.ts +0 -8
- package/src/assets/app/locales/locale-context.ts +0 -19
- package/src/assets/app/locales/locale-provider.tsx +0 -112
- package/src/assets/app/locales/locale-selector.tsx +0 -58
- package/src/assets/app/locales/locales.ts +0 -168
- package/src/assets/app/locales/ne/messages.po +0 -490
- package/src/assets/app/locales/nl/messages.po +0 -490
- package/src/assets/app/locales/pl/messages.po +0 -490
- package/src/assets/app/locales/pt-BR/messages.po +0 -490
- package/src/assets/app/locales/ro/messages.po +0 -490
- package/src/assets/app/locales/ru/messages.po +0 -490
- package/src/assets/app/locales/sv/messages.po +0 -490
- package/src/assets/app/locales/th/messages.po +0 -490
- package/src/assets/app/locales/tr/messages.po +0 -490
- package/src/assets/app/locales/uk/messages.po +0 -490
- package/src/assets/app/locales/vi/messages.po +0 -490
- package/src/assets/app/locales/zh-CN/messages.po +0 -490
- package/src/assets/app/locales/zh-HK/messages.po +0 -490
- package/src/assets/app/locales/zh-TW/messages.po +0 -490
- package/src/assets/app/main.css +0 -33
- package/src/assets/app/main.tsx +0 -44
- package/src/assets/app/views/authorize/accept/accept-form.tsx +0 -150
- package/src/assets/app/views/authorize/accept/accept-view.tsx +0 -70
- package/src/assets/app/views/authorize/authorize-view.tsx +0 -180
- package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +0 -88
- package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +0 -80
- package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +0 -127
- package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +0 -242
- package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +0 -116
- package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +0 -145
- package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +0 -142
- package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +0 -51
- package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +0 -287
- package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +0 -108
- package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +0 -158
- package/src/assets/app/views/authorize/welcome/welcome-view.tsx +0 -56
- package/src/assets/app/views/error/error-view.tsx +0 -31
- package/src/assets/asset.ts +0 -9
- package/src/assets/index.ts +0 -86
- package/tailwind.config.js +0 -31
- package/tsconfig.frontend.json +0 -11
- package/tsconfig.frontend.tsbuildinfo +0 -1
- package/tsconfig.tools.json +0 -8
- package/tsconfig.tools.tsbuildinfo +0 -1
- package/vite.config.mjs +0 -16
@@ -1,33 +1,27 @@
|
|
1
|
+
import { assets } from '@atproto/oauth-provider-ui'
|
1
2
|
import {
|
2
3
|
Middleware,
|
3
4
|
validateFetchDest,
|
4
5
|
validateFetchSite,
|
5
6
|
writeStream,
|
6
7
|
} from '../lib/http/index.js'
|
7
|
-
|
8
|
-
|
8
|
+
|
9
|
+
export const ASSETS_URL_PREFIX = '/@atproto/oauth-provider/~assets/'
|
10
|
+
|
11
|
+
export function buildAssetUrl(filename: string): string {
|
12
|
+
return `${ASSETS_URL_PREFIX}${encodeURIComponent(filename)}`
|
13
|
+
}
|
9
14
|
|
10
15
|
export function authorizeAssetsMiddleware(): Middleware {
|
11
16
|
return async function assetsMiddleware(req, res, next): Promise<void> {
|
12
17
|
if (req.method !== 'GET' && req.method !== 'HEAD') return next()
|
13
18
|
if (!req.url?.startsWith(ASSETS_URL_PREFIX)) return next()
|
14
19
|
|
15
|
-
const
|
16
|
-
string,
|
17
|
-
string | undefined,
|
18
|
-
]
|
19
|
-
if (query) return next()
|
20
|
-
|
21
|
-
const filename = pathname.slice(ASSETS_URL_PREFIX.length)
|
20
|
+
const filename = req.url.slice(ASSETS_URL_PREFIX.length)
|
22
21
|
if (!filename) return next()
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
asset = getAsset(filename)
|
27
|
-
} catch {
|
28
|
-
// Filename not found or not valid
|
29
|
-
return next()
|
30
|
-
}
|
23
|
+
const asset = assets.get(filename)
|
24
|
+
if (!asset) return next()
|
31
25
|
|
32
26
|
try {
|
33
27
|
// Allow "null" (ie. no header) to allow loading assets outside of a
|
@@ -45,6 +39,6 @@ export function authorizeAssetsMiddleware(): Middleware {
|
|
45
39
|
res.setHeader('ETag', asset.sha256)
|
46
40
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
47
41
|
|
48
|
-
writeStream(res, asset.
|
42
|
+
writeStream(res, asset.stream(), { contentType: asset.mime })
|
49
43
|
}
|
50
44
|
}
|
package/src/lib/csp/index.ts
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
import { Simplify } from '../util/type.js'
|
1
|
+
import { CombinedTuple, Simplify } from '../util/type.js'
|
2
2
|
|
3
3
|
export type CspValue =
|
4
4
|
| `data:`
|
5
|
+
| `http:${string}`
|
5
6
|
| `https:${string}`
|
6
7
|
| `'none'`
|
7
8
|
| `'self'`
|
@@ -35,7 +36,7 @@ export type CspConfig = Simplify<
|
|
35
36
|
} & {
|
36
37
|
[K in (typeof STRING_DIRECTIVES)[number]]?: CspValue
|
37
38
|
} & {
|
38
|
-
[K in (typeof ARRAY_DIRECTIVES)[number]]?:
|
39
|
+
[K in (typeof ARRAY_DIRECTIVES)[number]]?: Iterable<CspValue>
|
39
40
|
}
|
40
41
|
>
|
41
42
|
|
@@ -53,25 +54,27 @@ export function buildCsp(config: CspConfig): string {
|
|
53
54
|
}
|
54
55
|
|
55
56
|
for (const name of ARRAY_DIRECTIVES) {
|
56
|
-
|
57
|
+
// Remove duplicate values by using a Set
|
58
|
+
const val = config[name] ? new Set(config[name]) : undefined
|
59
|
+
if (val?.size) values.push(`${name} ${Array.from(val).join(' ')}`)
|
57
60
|
}
|
58
61
|
|
59
62
|
return values.join('; ')
|
60
63
|
}
|
61
64
|
|
62
|
-
export function mergeCsp(
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
if (!b) return a
|
65
|
+
export function mergeCsp<C extends (CspConfig | null | undefined)[]>(
|
66
|
+
...configs: C
|
67
|
+
) {
|
68
|
+
return configs.filter((v) => v != null).reduce(combineCsp) as CombinedTuple<C>
|
69
|
+
}
|
68
70
|
|
71
|
+
export function combineCsp(a: CspConfig, b: CspConfig): CspConfig {
|
69
72
|
const result: CspConfig = {}
|
70
73
|
|
71
74
|
for (const name of BOOLEAN_DIRECTIVES) {
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
// @NOTE b (if defined) takes precedence
|
76
|
+
const value = b[name] ?? a[name]
|
77
|
+
if (value != null) result[name] = value
|
75
78
|
}
|
76
79
|
|
77
80
|
for (const name of STRING_DIRECTIVES) {
|
@@ -90,7 +93,7 @@ export function mergeCsp(a?: CspConfig, b?: CspConfig): CspConfig | undefined {
|
|
90
93
|
if (set.size > 1 && set.has(NONE)) set.delete(NONE)
|
91
94
|
result[name] = [...set]
|
92
95
|
} else if (a[name] || b[name]) {
|
93
|
-
result[name] =
|
96
|
+
result[name] = a[name] || b[name]
|
94
97
|
}
|
95
98
|
}
|
96
99
|
|
package/src/lib/hcaptcha.ts
CHANGED
@@ -27,9 +27,11 @@ export const hcaptchaConfigSchema = z.object({
|
|
27
27
|
*/
|
28
28
|
tokenSalt: z.string().min(1),
|
29
29
|
/**
|
30
|
-
* The risk score
|
30
|
+
* The risk score above which the user is considered a threat and will be
|
31
31
|
* denied access. This will be ignored if the enterprise features are not
|
32
32
|
* available.
|
33
|
+
*
|
34
|
+
* Note: Score values ranges from 0.0 (no risk) to 1.0 (confirmed threat).
|
33
35
|
*/
|
34
36
|
scoreThreshold: z.number().optional(),
|
35
37
|
})
|
@@ -50,11 +52,12 @@ export const hcaptchaVerifyResultSchema = z.object({
|
|
50
52
|
/**
|
51
53
|
* the hostname of the site where the challenge was passed
|
52
54
|
*/
|
53
|
-
hostname: z.string(),
|
55
|
+
hostname: z.string().nullable(),
|
54
56
|
/**
|
55
|
-
* optional: any error codes
|
57
|
+
* optional: any error codes returned by the hCaptcha API.
|
58
|
+
* @see {@link https://docs.hcaptcha.com/#siteverify-error-codes-table}
|
56
59
|
*/
|
57
|
-
'error-codes': z.array(z.string()),
|
60
|
+
'error-codes': z.array(z.string()).optional(),
|
58
61
|
/**
|
59
62
|
* ENTERPRISE feature: a score denoting malicious activity. Value ranges from
|
60
63
|
* 0.0 (no risk) to 1.0 (confirmed threat).
|
@@ -128,7 +131,7 @@ export class HCaptchaClient {
|
|
128
131
|
this.fetch = bindFetch(fetch)
|
129
132
|
}
|
130
133
|
|
131
|
-
async verify(
|
134
|
+
public async verify(
|
132
135
|
behaviorType: 'login' | 'signup',
|
133
136
|
response: string,
|
134
137
|
remoteip: string,
|
@@ -160,20 +163,21 @@ export class HCaptchaClient {
|
|
160
163
|
}
|
161
164
|
}
|
162
165
|
|
163
|
-
isAllowed({ success, hostname, score }: HcaptchaVerifyResult) {
|
166
|
+
protected isAllowed({ success, hostname, score }: HcaptchaVerifyResult) {
|
164
167
|
return (
|
165
168
|
success &&
|
166
169
|
// Fool-proofing: If this is false, the user is trying to use a token
|
167
170
|
// generated for the same siteKey, but on another domain.
|
168
171
|
hostname === this.hostname &&
|
169
172
|
// Ignore if enterprise feature is not enabled
|
170
|
-
score
|
171
|
-
|
172
|
-
|
173
|
+
(score == null ||
|
174
|
+
// Ignore if disabled through config
|
175
|
+
this.config.scoreThreshold == null ||
|
176
|
+
score < this.config.scoreThreshold)
|
173
177
|
)
|
174
178
|
}
|
175
179
|
|
176
|
-
hashToken(value: string) {
|
180
|
+
protected hashToken(value: string) {
|
177
181
|
const hash = createHash('sha256')
|
178
182
|
hash.update(this.config.tokenSalt)
|
179
183
|
hash.update(value)
|
@@ -4,7 +4,6 @@ import { html } from './tags.js'
|
|
4
4
|
|
5
5
|
export type AssetRef = {
|
6
6
|
url: string
|
7
|
-
sha256: string
|
8
7
|
}
|
9
8
|
|
10
9
|
export type Attrs = Record<string, boolean | string | undefined>
|
@@ -59,6 +58,7 @@ export type BuildDocumentOptions = {
|
|
59
58
|
base?: URL
|
60
59
|
meta?: readonly MetaAttrs[]
|
61
60
|
links?: readonly LinkAttrs[]
|
61
|
+
preloads?: readonly AssetRef[]
|
62
62
|
head?: HtmlValue
|
63
63
|
title?: HtmlValue
|
64
64
|
scripts?: readonly (Html | AssetRef)[]
|
@@ -76,6 +76,7 @@ export const buildDocument = ({
|
|
76
76
|
base,
|
77
77
|
meta,
|
78
78
|
links,
|
79
|
+
preloads,
|
79
80
|
scripts,
|
80
81
|
styles,
|
81
82
|
}: BuildDocumentOptions) => html`<!doctype html>
|
@@ -86,8 +87,7 @@ export const buildDocument = ({
|
|
86
87
|
${base && html`<base href="${base.href}" />`}
|
87
88
|
${meta?.some(isViewportMeta) ? null : defaultViewport}
|
88
89
|
${meta?.map(metaToHtml)}
|
89
|
-
${
|
90
|
-
${scripts?.map(linkPreload('script'))}
|
90
|
+
${preloads?.map(linkPreload)}
|
91
91
|
${links?.map(linkToHtml)}
|
92
92
|
${head}
|
93
93
|
${styles?.map(styleToHtml)}
|
@@ -120,11 +120,18 @@ function* attrsToHtml(attrs?: Attrs) {
|
|
120
120
|
}
|
121
121
|
}
|
122
122
|
|
123
|
-
function linkPreload(
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
function linkPreload(asset: AssetRef) {
|
124
|
+
const [path] = asset.url.split('?', 2)
|
125
|
+
|
126
|
+
if (path.endsWith('.js')) {
|
127
|
+
return html`<link rel="modulepreload" href="${asset.url}" />`
|
128
|
+
}
|
129
|
+
|
130
|
+
if (path.endsWith('.css')) {
|
131
|
+
return html`<link rel="preload" href="${asset.url}" as="style" />`
|
132
|
+
}
|
133
|
+
|
134
|
+
return undefined
|
128
135
|
}
|
129
136
|
|
130
137
|
function scriptToHtml(script: Html | AssetRef) {
|
package/src/lib/html/html.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
import { isString } from './util'
|
2
|
-
|
3
1
|
const symbol = Symbol('Html.dangerouslyCreate')
|
4
2
|
|
5
3
|
/**
|
@@ -7,34 +5,29 @@ const symbol = Symbol('Html.dangerouslyCreate')
|
|
7
5
|
* or used as fragments to build a larger HTML document.
|
8
6
|
*/
|
9
7
|
export class Html implements Iterable<string> {
|
10
|
-
#fragments:
|
8
|
+
readonly #fragments: readonly (Html | string)[]
|
11
9
|
|
12
10
|
private constructor(fragments: Iterable<Html | string>, guard: symbol) {
|
13
11
|
if (guard !== symbol) {
|
14
|
-
//
|
12
|
+
// Forces developers to use `Html.dangerouslyCreate` to create an Html
|
15
13
|
// instance, to make it clear that the content needs to be trusted.
|
16
14
|
throw new TypeError(
|
17
15
|
'Use Html.dangerouslyCreate() to create an Html instance',
|
18
16
|
)
|
19
17
|
}
|
20
18
|
|
21
|
-
|
19
|
+
// Transform into an array in case iterable can be consumed only once
|
20
|
+
// (e.g. a generator function).
|
21
|
+
this.#fragments = Array.from(fragments)
|
22
22
|
}
|
23
23
|
|
24
24
|
toString(): string {
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
//
|
29
|
-
|
30
|
-
|
31
|
-
this.#fragments.length > 1 ||
|
32
|
-
!this.#fragments.every(isString)
|
33
|
-
) {
|
34
|
-
this.#fragments = result ? [result] : []
|
35
|
-
}
|
36
|
-
|
37
|
-
return result
|
25
|
+
// More efficient than `return this.#fragments.join('')` because it avoids
|
26
|
+
// creating intermediate strings when items of this.#fragments are Html
|
27
|
+
// instances (as all their toString() would end-up being called, creating
|
28
|
+
// lots of intermediary strings). The approach here allows to do a full scan
|
29
|
+
// of all the child nodes and concatenate them in a single pass.
|
30
|
+
return Array.from(this).join('')
|
38
31
|
}
|
39
32
|
|
40
33
|
[Symbol.toPrimitive](hint): string {
|
package/src/lib/html/util.ts
CHANGED
package/src/lib/http/response.ts
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
import type { ServerResponse } from 'node:http'
|
2
2
|
import { type Readable, pipeline } from 'node:stream'
|
3
|
+
import {
|
4
|
+
SecurityHeadersOptions,
|
5
|
+
setSecurityHeaders,
|
6
|
+
} from './security-headers.js'
|
3
7
|
import type { Handler, Middleware } from './types.js'
|
4
8
|
|
5
9
|
export function appendHeader(
|
@@ -88,11 +92,15 @@ export function staticJsonMiddleware(
|
|
88
92
|
}
|
89
93
|
}
|
90
94
|
|
95
|
+
export type WriteHtmlOptions = WriteResponseOptions & SecurityHeadersOptions
|
96
|
+
|
91
97
|
export function writeHtml(
|
92
98
|
res: ServerResponse,
|
93
99
|
html: Buffer | string,
|
94
|
-
{ contentType = 'text/html', ...options }:
|
100
|
+
{ contentType = 'text/html', ...options }: WriteHtmlOptions = {},
|
95
101
|
): void {
|
102
|
+
// HTML pages should always be served with safety protection headers
|
103
|
+
setSecurityHeaders(res, options)
|
96
104
|
writeBuffer(res, html, { ...options, contentType })
|
97
105
|
}
|
98
106
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import type { ServerResponse } from 'node:http'
|
2
|
+
import { type CspConfig, buildCsp } from '../csp/index.js'
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy COEP on MDN}
|
6
|
+
*/
|
7
|
+
export enum CrossOriginEmbedderPolicy {
|
8
|
+
unsafeNone = 'unsafe-none',
|
9
|
+
requireCorp = 'require-corp',
|
10
|
+
credentialless = 'credentialless',
|
11
|
+
}
|
12
|
+
|
13
|
+
/**
|
14
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy CORP on MDN}
|
15
|
+
*/
|
16
|
+
export enum CrossOriginResourcePolicy {
|
17
|
+
sameSite = 'same-site',
|
18
|
+
sameOrigin = 'same-origin',
|
19
|
+
crossOrigin = 'cross-origin',
|
20
|
+
}
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy COOP on MDN}
|
24
|
+
*/
|
25
|
+
export enum CrossOriginOpenerPolicy {
|
26
|
+
unsafeNone = 'unsafe-none',
|
27
|
+
sameOriginAllowPopups = 'same-origin-allow-popups',
|
28
|
+
sameOrigin = 'same-origin',
|
29
|
+
noopenerAllowPopups = 'noopener-allow-popups',
|
30
|
+
}
|
31
|
+
|
32
|
+
export type HTTPStrictTransportSecurityConfig = {
|
33
|
+
maxAge: number
|
34
|
+
includeSubDomains?: boolean
|
35
|
+
preload?: boolean
|
36
|
+
}
|
37
|
+
|
38
|
+
export type SecurityHeadersOptions = {
|
39
|
+
/**
|
40
|
+
* Defaults to `default-src: 'none'`. Use an empty object to disable CSP.
|
41
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy CSP on MDN}
|
42
|
+
*/
|
43
|
+
csp?: CspConfig
|
44
|
+
coep?: CrossOriginEmbedderPolicy
|
45
|
+
corp?: CrossOriginResourcePolicy
|
46
|
+
coop?: CrossOriginOpenerPolicy
|
47
|
+
/**
|
48
|
+
* Defaults to 2 years. Use `false` to disable HSTS.
|
49
|
+
*/
|
50
|
+
hsts?: HTTPStrictTransportSecurityConfig | false
|
51
|
+
}
|
52
|
+
|
53
|
+
export function setSecurityHeaders(
|
54
|
+
res: ServerResponse,
|
55
|
+
{
|
56
|
+
csp = { 'default-src': ["'none'"] },
|
57
|
+
coep = CrossOriginEmbedderPolicy.requireCorp,
|
58
|
+
corp = CrossOriginResourcePolicy.sameOrigin,
|
59
|
+
coop = CrossOriginOpenerPolicy.sameOrigin,
|
60
|
+
hsts = { maxAge: 63072000 },
|
61
|
+
}: SecurityHeadersOptions,
|
62
|
+
): void {
|
63
|
+
// @NOTE Never set CSP through http-equiv meta as not all directives will
|
64
|
+
// be honored. Always set it through the Content-Security-Policy header.
|
65
|
+
const cspString = buildCsp(csp)
|
66
|
+
if (cspString) {
|
67
|
+
res.setHeader('Content-Security-Policy', cspString)
|
68
|
+
}
|
69
|
+
|
70
|
+
res.setHeader('Cross-Origin-Embedder-Policy', coep)
|
71
|
+
res.setHeader('Cross-Origin-Resource-Policy', corp)
|
72
|
+
res.setHeader('Cross-Origin-Opener-Policy', coop)
|
73
|
+
|
74
|
+
if (hsts) {
|
75
|
+
res.setHeader('Strict-Transport-Security', buildHstsValue(hsts))
|
76
|
+
}
|
77
|
+
|
78
|
+
// @TODO: make these headers configurable (?)
|
79
|
+
res.setHeader('Permissions-Policy', 'otp-credentials=*, document-domain=()')
|
80
|
+
res.setHeader('Referrer-Policy', 'same-origin')
|
81
|
+
res.setHeader('X-Frame-Options', 'DENY')
|
82
|
+
res.setHeader('X-Content-Type-Options', 'nosniff')
|
83
|
+
res.setHeader('X-XSS-Protection', '0')
|
84
|
+
}
|
85
|
+
|
86
|
+
function buildHstsValue(config: HTTPStrictTransportSecurityConfig): string {
|
87
|
+
let value = `max-age=${config.maxAge}`
|
88
|
+
if (config.includeSubDomains) value += '; includeSubDomains'
|
89
|
+
if (config.preload) value += '; preload'
|
90
|
+
return value
|
91
|
+
}
|
package/src/lib/util/type.ts
CHANGED
@@ -9,6 +9,24 @@ export type Override<T, V> = Simplify<{
|
|
9
9
|
}>
|
10
10
|
export type Awaitable<T> = T | Promise<T>
|
11
11
|
|
12
|
+
/**
|
13
|
+
* Converts a tuple to the equivalent type of combining every item into a single
|
14
|
+
* one. If any of the item in the tuple is non nullish, the result will be non
|
15
|
+
* nullish.
|
16
|
+
*/
|
17
|
+
export type CombinedTuple<T extends readonly unknown[]> = T extends []
|
18
|
+
? undefined
|
19
|
+
: Exclude<
|
20
|
+
T[number],
|
21
|
+
// If any item in the tuple is never `null` (resp. `undefined`), exclude
|
22
|
+
// `null` (resp. `undefined`) from `T[number]`
|
23
|
+
{
|
24
|
+
[K in keyof T]-?:
|
25
|
+
| (null extends T[K] ? never : null)
|
26
|
+
| (undefined extends T[K] ? never : undefined)
|
27
|
+
}[keyof T]
|
28
|
+
>
|
29
|
+
|
12
30
|
/**
|
13
31
|
* Similar to {@link Required} but also ensures that all values are defined.
|
14
32
|
*/
|
package/src/oauth-hooks.ts
CHANGED
@@ -7,7 +7,7 @@ import {
|
|
7
7
|
} from '@atproto/oauth-types'
|
8
8
|
import { Account } from './account/account.js'
|
9
9
|
import { SignInData } from './account/sign-in-data.js'
|
10
|
-
import {
|
10
|
+
import { SignUpInput } from './account/sign-up-input.js'
|
11
11
|
import { ClientAuth } from './client/client-auth.js'
|
12
12
|
import { ClientId } from './client/client-id.js'
|
13
13
|
import { ClientInfo } from './client/client-info.js'
|
@@ -17,7 +17,7 @@ import { HcaptchaConfig, HcaptchaVerifyResult } from './lib/hcaptcha.js'
|
|
17
17
|
import { RequestMetadata } from './lib/http/request.js'
|
18
18
|
import { Awaitable } from './lib/util/type.js'
|
19
19
|
import { AccessDeniedError, OAuthError } from './oauth-errors.js'
|
20
|
-
import { DeviceAccountInfo, DeviceId } from './oauth-store.js'
|
20
|
+
import { DeviceAccountInfo, DeviceId, SignUpData } from './oauth-store.js'
|
21
21
|
|
22
22
|
// Make sure all types needed to implement the OAuthHooks are exported
|
23
23
|
export {
|
@@ -42,6 +42,7 @@ export {
|
|
42
42
|
type RequestMetadata,
|
43
43
|
type SignInData,
|
44
44
|
type SignUpData,
|
45
|
+
type SignUpInput,
|
45
46
|
}
|
46
47
|
|
47
48
|
export type OAuthHooks = {
|
@@ -71,36 +72,14 @@ export type OAuthHooks = {
|
|
71
72
|
account: Account
|
72
73
|
}) => Awaitable<undefined | OAuthAuthorizationDetails>
|
73
74
|
|
74
|
-
/**
|
75
|
-
* This hook is called whenever an hcaptcha challenge is verified
|
76
|
-
* during sign-up (if hcaptcha is enabled).
|
77
|
-
*
|
78
|
-
* @throws {InvalidRequestError} to deny the sign-up
|
79
|
-
*/
|
80
|
-
onSignupHcaptchaResult?: (data: {
|
81
|
-
data: SignUpData
|
82
|
-
/**
|
83
|
-
* This indicates not only wether the hCaptcha challenge succeeded, but also
|
84
|
-
* if the score was low enough according to the
|
85
|
-
* {@link HcaptchaConfig.scoreThreshold}.
|
86
|
-
*
|
87
|
-
* @see {@link HCaptchaClient.isAllowed}
|
88
|
-
*/
|
89
|
-
allowed: boolean
|
90
|
-
result: HcaptchaVerifyResult
|
91
|
-
deviceId: DeviceId
|
92
|
-
deviceMetadata: RequestMetadata
|
93
|
-
}) => Awaitable<void>
|
94
|
-
|
95
75
|
/**
|
96
76
|
* This hook is called when a user attempts to sign up, after every validation
|
97
77
|
* has passed (including hcaptcha).
|
98
78
|
*/
|
99
79
|
onSignupAttempt?: (data: {
|
100
|
-
|
80
|
+
input: SignUpInput
|
101
81
|
deviceId: DeviceId
|
102
82
|
deviceMetadata: RequestMetadata
|
103
|
-
hcaptchaResult?: HcaptchaVerifyResult
|
104
83
|
}) => Awaitable<void>
|
105
84
|
|
106
85
|
/**
|
package/src/oauth-provider.ts
CHANGED
@@ -45,7 +45,7 @@ import {
|
|
45
45
|
} from './account/account-store.js'
|
46
46
|
import { Account } from './account/account.js'
|
47
47
|
import { signInDataSchema } from './account/sign-in-data.js'
|
48
|
-
import {
|
48
|
+
import { signUpInputSchema } from './account/sign-up-input.js'
|
49
49
|
import { authorizeAssetsMiddleware } from './assets/assets-middleware.js'
|
50
50
|
import { ClientAuth, authJwkThumbprint } from './client/client-auth.js'
|
51
51
|
import {
|
@@ -1588,7 +1588,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
1588
1588
|
|
1589
1589
|
router.post(
|
1590
1590
|
'/oauth/authorize/sign-up',
|
1591
|
-
apiHandler(
|
1591
|
+
apiHandler(signUpInputSchema, async function (req, res, data, ctx) {
|
1592
1592
|
return server.signUp(ctx, data)
|
1593
1593
|
}),
|
1594
1594
|
)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { Html, js } from '../lib/html/index.js'
|
2
|
+
|
3
|
+
export function declareBackendData(values: Record<string, unknown>): Html {
|
4
|
+
return Html.dangerouslyCreate(backendDataGenerator(values))
|
5
|
+
}
|
6
|
+
|
7
|
+
export function* backendDataGenerator(
|
8
|
+
values: Record<string, unknown>,
|
9
|
+
): Generator<Html> {
|
10
|
+
for (const [key, val] of Object.entries(values)) {
|
11
|
+
yield js`window[${key}]=${val};`
|
12
|
+
}
|
13
|
+
// The script tag is removed after the data is assigned to the global
|
14
|
+
// variables to prevent other scripts from reading the values. The "app"
|
15
|
+
// script will read the global variable and then unset it. See
|
16
|
+
// `readBackendData()` in "src/assets/app/backend-data.ts".
|
17
|
+
yield js`document.currentScript.remove();`
|
18
|
+
}
|
@@ -1,7 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
OAuthClientMetadata,
|
4
|
-
} from '@atproto/oauth-types'
|
1
|
+
import type { AuthorizeData, Session } from '@atproto/oauth-provider-api'
|
2
|
+
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
5
3
|
import { DeviceAccountInfo } from '../account/account-store.js'
|
6
4
|
import { Account } from '../account/account.js'
|
7
5
|
import { Client } from '../client/client.js'
|
@@ -30,28 +28,7 @@ export type AuthorizationResultAuthorize = {
|
|
30
28
|
}
|
31
29
|
}
|
32
30
|
|
33
|
-
|
34
|
-
// (app/backend-types.ts)
|
35
|
-
|
36
|
-
type Session = {
|
37
|
-
account: Account
|
38
|
-
info?: never // Prevent accidental leaks to frontend
|
39
|
-
|
40
|
-
selected: boolean
|
41
|
-
loginRequired: boolean
|
42
|
-
consentRequired: boolean
|
43
|
-
}
|
44
|
-
|
45
|
-
export type AuthorizeData = {
|
46
|
-
clientId: string
|
47
|
-
clientMetadata: OAuthClientMetadata
|
48
|
-
clientTrusted: boolean
|
49
|
-
requestUri: string
|
50
|
-
loginHint?: string
|
51
|
-
scopeDetails?: ScopeDetail[]
|
52
|
-
newSessionsRequireConsent: boolean
|
53
|
-
sessions: Session[]
|
54
|
-
}
|
31
|
+
export type { AuthorizeData, Session }
|
55
32
|
|
56
33
|
export function buildAuthorizeData(
|
57
34
|
data: AuthorizationResultAuthorize,
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { z } from 'zod'
|
2
|
+
import { CustomizationData } from '@atproto/oauth-provider-api'
|
2
3
|
import { hcaptchaConfigSchema } from '../lib/hcaptcha.js'
|
3
4
|
import { isLinkRel } from '../lib/html/build-document.js'
|
4
5
|
import { multiLangStringSchema } from '../lib/locale.js'
|
@@ -59,7 +60,7 @@ export const brandingConfigSchema = z.object({
|
|
59
60
|
name: z.string().optional(),
|
60
61
|
logo: z.string().optional(),
|
61
62
|
colors: colorsDefinitionSchema.optional(),
|
62
|
-
links: z.array(linkDefinitionSchema).
|
63
|
+
links: z.array(linkDefinitionSchema).optional(),
|
63
64
|
})
|
64
65
|
export type BrandingInput = z.input<typeof brandingConfigSchema>
|
65
66
|
export type Branding = z.infer<typeof brandingConfigSchema>
|
@@ -86,18 +87,6 @@ export const customizationSchema = z.object({
|
|
86
87
|
export type CustomizationInput = z.input<typeof customizationSchema>
|
87
88
|
export type Customization = z.infer<typeof customizationSchema>
|
88
89
|
|
89
|
-
export type CustomizationData = {
|
90
|
-
// Functional customization
|
91
|
-
hcaptchaSiteKey?: string
|
92
|
-
inviteCodeRequired?: boolean
|
93
|
-
availableUserDomains?: string[]
|
94
|
-
|
95
|
-
// Aesthetic customization
|
96
|
-
name?: string
|
97
|
-
logo?: string
|
98
|
-
links?: readonly LinkDefinition[]
|
99
|
-
}
|
100
|
-
|
101
90
|
export function buildCustomizationData({
|
102
91
|
branding,
|
103
92
|
availableUserDomains,
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { ErrorData } from '@atproto/oauth-provider-api'
|
2
|
+
import { buildErrorPayload } from './build-error-payload.js'
|
3
|
+
|
4
|
+
// @NOTE: The primary role of this function is to ensure that the ErrorPayload
|
5
|
+
// and ErrorData types are in sync.
|
6
|
+
export function buildErrorData(error: unknown): ErrorData {
|
7
|
+
return buildErrorPayload(error)
|
8
|
+
}
|
@@ -50,10 +50,12 @@ export function buildErrorStatus(error: unknown): number {
|
|
50
50
|
return 500
|
51
51
|
}
|
52
52
|
|
53
|
-
export
|
53
|
+
export type ErrorPayload = {
|
54
54
|
error: string
|
55
55
|
error_description: string
|
56
|
-
}
|
56
|
+
}
|
57
|
+
|
58
|
+
export function buildErrorPayload(error: unknown): ErrorPayload {
|
57
59
|
if (error instanceof OAuthError) {
|
58
60
|
return error.toJSON()
|
59
61
|
}
|