@atproto/oauth-provider-ui 0.0.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/.linguirc +57 -0
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +6 -0
- package/LICENSE.txt +7 -0
- package/dist/assets/COdVzed-.css +3 -0
- package/dist/assets/COdVzed-.js +100 -0
- package/dist/assets/COdVzed-.js.map +1 -0
- package/dist/assets/Cqnfnbvc.js +6 -0
- package/dist/assets/Cqnfnbvc.js.map +1 -0
- package/dist/assets/bundle-manifest.json +630 -0
- package/dist/assets/error-view-Bu4y7Nd8.js +208 -0
- package/dist/assets/error-view-Bu4y7Nd8.js.map +1 -0
- package/dist/assets/index-DXlCRM6V.js +36 -0
- package/dist/assets/index-DXlCRM6V.js.map +1 -0
- package/dist/assets/messages-2GoTm2qL.js +4 -0
- package/dist/assets/messages-2GoTm2qL.js.map +1 -0
- package/dist/assets/messages-6Cn2Jbhw.js +4 -0
- package/dist/assets/messages-6Cn2Jbhw.js.map +1 -0
- package/dist/assets/messages-75hFgOK2.js +4 -0
- package/dist/assets/messages-75hFgOK2.js.map +1 -0
- package/dist/assets/messages-B3OK4k0O.js +4 -0
- package/dist/assets/messages-B3OK4k0O.js.map +1 -0
- package/dist/assets/messages-BNXlPzKV.js +4 -0
- package/dist/assets/messages-BNXlPzKV.js.map +1 -0
- package/dist/assets/messages-BUygB8mD.js +4 -0
- package/dist/assets/messages-BUygB8mD.js.map +1 -0
- package/dist/assets/messages-BVPPcwNr.js +4 -0
- package/dist/assets/messages-BVPPcwNr.js.map +1 -0
- package/dist/assets/messages-BbbWUQS8.js +4 -0
- package/dist/assets/messages-BbbWUQS8.js.map +1 -0
- package/dist/assets/messages-BibKCYyW.js +4 -0
- package/dist/assets/messages-BibKCYyW.js.map +1 -0
- package/dist/assets/messages-BlPrr9_7.js +4 -0
- package/dist/assets/messages-BlPrr9_7.js.map +1 -0
- package/dist/assets/messages-ByVCw40U.js +4 -0
- package/dist/assets/messages-ByVCw40U.js.map +1 -0
- package/dist/assets/messages-C5DU1neP.js +4 -0
- package/dist/assets/messages-C5DU1neP.js.map +1 -0
- package/dist/assets/messages-C6IgUtbX.js +4 -0
- package/dist/assets/messages-C6IgUtbX.js.map +1 -0
- package/dist/assets/messages-C92Zzt2o.js +4 -0
- package/dist/assets/messages-C92Zzt2o.js.map +1 -0
- package/dist/assets/messages-CGZqYT14.js +4 -0
- package/dist/assets/messages-CGZqYT14.js.map +1 -0
- package/dist/assets/messages-CGlsy4wt.js +4 -0
- package/dist/assets/messages-CGlsy4wt.js.map +1 -0
- package/dist/assets/messages-CPT1nd0u.js +4 -0
- package/dist/assets/messages-CPT1nd0u.js.map +1 -0
- package/dist/assets/messages-CTTdXyw_.js +4 -0
- package/dist/assets/messages-CTTdXyw_.js.map +1 -0
- package/dist/assets/messages-ChK_C_Pj.js +4 -0
- package/dist/assets/messages-ChK_C_Pj.js.map +1 -0
- package/dist/assets/messages-CjJbk7Uf.js +4 -0
- package/dist/assets/messages-CjJbk7Uf.js.map +1 -0
- package/dist/assets/messages-CoiLjLYO.js +4 -0
- package/dist/assets/messages-CoiLjLYO.js.map +1 -0
- package/dist/assets/messages-Cwx6B4Ti.js +4 -0
- package/dist/assets/messages-Cwx6B4Ti.js.map +1 -0
- package/dist/assets/messages-D0uXAp_H.js +4 -0
- package/dist/assets/messages-D0uXAp_H.js.map +1 -0
- package/dist/assets/messages-DG0_arU0.js +4 -0
- package/dist/assets/messages-DG0_arU0.js.map +1 -0
- package/dist/assets/messages-DOXFJh9K.js +4 -0
- package/dist/assets/messages-DOXFJh9K.js.map +1 -0
- package/dist/assets/messages-DPK7nOoC.js +4 -0
- package/dist/assets/messages-DPK7nOoC.js.map +1 -0
- package/dist/assets/messages-Duccgtu0.js +4 -0
- package/dist/assets/messages-Duccgtu0.js.map +1 -0
- package/dist/assets/messages-DxTqgsHq.js +4 -0
- package/dist/assets/messages-DxTqgsHq.js.map +1 -0
- package/dist/assets/messages-E5_lTg7A.js +4 -0
- package/dist/assets/messages-E5_lTg7A.js.map +1 -0
- package/dist/assets/messages-UhunAjh1.js +4 -0
- package/dist/assets/messages-UhunAjh1.js.map +1 -0
- package/dist/assets/messages-Xg_3YLGw.js +4 -0
- package/dist/assets/messages-Xg_3YLGw.js.map +1 -0
- package/dist/assets/messages-iliBQHY2.js +4 -0
- package/dist/assets/messages-iliBQHY2.js.map +1 -0
- package/dist/assets/messages-lRprpIl-.js +4 -0
- package/dist/assets/messages-lRprpIl-.js.map +1 -0
- package/dist/assets/messages-pbPHQbz1.js +4 -0
- package/dist/assets/messages-pbPHQbz1.js.map +1 -0
- package/dist/assets/messages-q-O7ZQGs.js +4 -0
- package/dist/assets/messages-q-O7ZQGs.js.map +1 -0
- package/dist/lib/index.d.ts +19 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +47 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/tsconfig.backend.tsbuildinfo +1 -0
- package/lib/index.ts +72 -0
- package/package.json +73 -0
- package/rollup.config.js +102 -0
- package/src/authorization-page.html +183 -0
- package/src/authorization-page.tsx +55 -0
- package/src/backend-data.ts +35 -0
- package/src/components/forms/button-toggle-visibility.tsx +43 -0
- package/src/components/forms/button.tsx +60 -0
- package/src/components/forms/fieldset.tsx +55 -0
- package/src/components/forms/form-card-async.tsx +103 -0
- package/src/components/forms/form-card.tsx +49 -0
- package/src/components/forms/input-checkbox.tsx +78 -0
- package/src/components/forms/input-container.tsx +107 -0
- package/src/components/forms/input-email-address.tsx +65 -0
- package/src/components/forms/input-new-password.tsx +62 -0
- package/src/components/forms/input-password.tsx +87 -0
- package/src/components/forms/input-text.tsx +82 -0
- package/src/components/forms/input-token.tsx +94 -0
- package/src/components/forms/wizard-card.tsx +116 -0
- package/src/components/layouts/layout-title-page.tsx +77 -0
- package/src/components/layouts/layout-welcome.tsx +73 -0
- package/src/components/utils/account-identifier.tsx +23 -0
- package/src/components/utils/account-image.tsx +33 -0
- package/src/components/utils/admonition.tsx +52 -0
- package/src/components/utils/client-name.tsx +45 -0
- package/src/components/utils/error-card.tsx +93 -0
- package/src/components/utils/error-message.tsx +88 -0
- package/src/components/utils/help-card.tsx +46 -0
- package/src/components/utils/icons.tsx +88 -0
- package/src/components/utils/link-anchor.tsx +28 -0
- package/src/components/utils/link-title.tsx +26 -0
- package/src/components/utils/multi-lang-string.tsx +56 -0
- package/src/components/utils/password-strength-label.tsx +37 -0
- package/src/components/utils/password-strength-meter.tsx +58 -0
- package/src/components/utils/url-viewer.tsx +73 -0
- package/src/cookies.ts +11 -0
- package/src/error-page.html +125 -0
- package/src/error-page.tsx +29 -0
- package/src/hooks/use-api.ts +182 -0
- package/src/hooks/use-async-action.ts +120 -0
- package/src/hooks/use-bound-dispatch.ts +5 -0
- package/src/hooks/use-browser-color-scheme.ts +31 -0
- package/src/hooks/use-csrf-token.ts +5 -0
- package/src/hooks/use-random-string.ts +37 -0
- package/src/hooks/use-stepper.ts +87 -0
- package/src/index.html +13 -0
- package/src/lib/api.ts +234 -0
- package/src/lib/backend-data.ts +6 -0
- package/src/lib/clsx.ts +6 -0
- package/src/lib/json-client.ts +97 -0
- package/src/lib/password.ts +98 -0
- package/src/lib/ref.ts +17 -0
- package/src/lib/util.ts +13 -0
- package/src/locales/an/messages.po +487 -0
- package/src/locales/ast/messages.po +487 -0
- package/src/locales/ca/messages.po +487 -0
- package/src/locales/da/messages.po +487 -0
- package/src/locales/de/messages.po +487 -0
- package/src/locales/el/messages.po +487 -0
- package/src/locales/en/messages.po +487 -0
- package/src/locales/en-GB/messages.po +487 -0
- package/src/locales/es/messages.po +487 -0
- package/src/locales/eu/messages.po +487 -0
- package/src/locales/fi/messages.po +487 -0
- package/src/locales/fr/messages.po +487 -0
- package/src/locales/ga/messages.po +487 -0
- package/src/locales/gl/messages.po +487 -0
- package/src/locales/hi/messages.po +487 -0
- package/src/locales/hu/messages.po +487 -0
- package/src/locales/ia/messages.po +487 -0
- package/src/locales/id/messages.po +487 -0
- package/src/locales/it/messages.po +487 -0
- package/src/locales/ja/messages.po +487 -0
- package/src/locales/km/messages.po +487 -0
- package/src/locales/ko/messages.po +487 -0
- package/src/locales/load.ts +8 -0
- package/src/locales/locale-context.ts +19 -0
- package/src/locales/locale-provider.tsx +112 -0
- package/src/locales/locale-selector.tsx +58 -0
- package/src/locales/locales.ts +168 -0
- package/src/locales/ne/messages.po +487 -0
- package/src/locales/nl/messages.po +487 -0
- package/src/locales/pl/messages.po +487 -0
- package/src/locales/pt-BR/messages.po +487 -0
- package/src/locales/ro/messages.po +487 -0
- package/src/locales/ru/messages.po +487 -0
- package/src/locales/sv/messages.po +487 -0
- package/src/locales/th/messages.po +487 -0
- package/src/locales/tr/messages.po +487 -0
- package/src/locales/uk/messages.po +487 -0
- package/src/locales/vi/messages.po +487 -0
- package/src/locales/zh-CN/messages.po +487 -0
- package/src/locales/zh-HK/messages.po +487 -0
- package/src/locales/zh-TW/messages.po +487 -0
- package/src/styles.css +33 -0
- package/src/views/authorize/accept/accept-form.tsx +150 -0
- package/src/views/authorize/accept/accept-view.tsx +70 -0
- package/src/views/authorize/authorize-view.tsx +183 -0
- package/src/views/authorize/reset-password/reset-password-confirm-form.tsx +88 -0
- package/src/views/authorize/reset-password/reset-password-request-form.tsx +80 -0
- package/src/views/authorize/reset-password/reset-password-view.tsx +127 -0
- package/src/views/authorize/sign-in/sign-in-form.tsx +242 -0
- package/src/views/authorize/sign-in/sign-in-picker.tsx +116 -0
- package/src/views/authorize/sign-in/sign-in-view.tsx +145 -0
- package/src/views/authorize/sign-up/sign-up-account-form.tsx +142 -0
- package/src/views/authorize/sign-up/sign-up-disclaimer.tsx +51 -0
- package/src/views/authorize/sign-up/sign-up-handle-form.tsx +287 -0
- package/src/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +108 -0
- package/src/views/authorize/sign-up/sign-up-view.tsx +158 -0
- package/src/views/authorize/welcome/welcome-view.tsx +56 -0
- package/src/views/error/error-view.tsx +31 -0
- package/tailwind.config.js +31 -0
- package/tsconfig.backend.json +8 -0
- package/tsconfig.frontend.json +10 -0
- package/tsconfig.frontend.tsbuildinfo +1 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tools.json +8 -0
- package/tsconfig.tools.tsbuildinfo +1 -0
- package/vite.config.mjs +16 -0
package/lib/index.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// This file allows exposing the result of the build of the `../src` folder as
|
|
2
|
+
// assets that can be used from a backend service. Specifically, the assets map
|
|
3
|
+
// bellow exposes both the asset items from their manifest, as well as a method
|
|
4
|
+
// to get the stream data (as a Readable stream) of the asset.
|
|
5
|
+
|
|
6
|
+
// When this library is used as a NodeJS dependency (typically from
|
|
7
|
+
// node_modules), the assets will simply read on disk from the node_modules
|
|
8
|
+
// directory. However, if this file is bundled (e.g. via rollup), the assets
|
|
9
|
+
// need to be copied to the bundler's output directory for the code bellow to
|
|
10
|
+
// work. Most bundlers support this (webpack, rollup, etc.) by re-writing `new
|
|
11
|
+
// URL('./path', import.meta.url)` calls to point to the correct output
|
|
12
|
+
// directory.
|
|
13
|
+
|
|
14
|
+
// However, that syntax only works in ESM modules. Atproto uses CJS modules, so,
|
|
15
|
+
// at the moment, the logic bellow is **not** compatible with bundlers. To allow
|
|
16
|
+
// bundling this file as a CJS module, the code bellow should not be dependent
|
|
17
|
+
// on reading the files on disk. This can be done by modifying the build system
|
|
18
|
+
// to embed the asset bytes directly into the bundle-manifest.json (see the
|
|
19
|
+
// `data` option of "@atproto-labs/rollup-plugin-bundle-manifest" in
|
|
20
|
+
// rollup.config.js).
|
|
21
|
+
|
|
22
|
+
// https://github.com/evanw/esbuild/issues/795
|
|
23
|
+
// https://www.npmjs.com/package/@web/rollup-plugin-import-meta-assets
|
|
24
|
+
|
|
25
|
+
// Note that the bundle-manifest -- being a JSON file -- can be imported
|
|
26
|
+
// directly without any special handling. This is because both bundlers and
|
|
27
|
+
// NodeJS, support JSON imports out of the box.
|
|
28
|
+
|
|
29
|
+
import { createReadStream } from 'node:fs'
|
|
30
|
+
import { join } from 'node:path'
|
|
31
|
+
import { Readable } from 'node:stream'
|
|
32
|
+
import type { ManifestItem } from '@atproto-labs/rollup-plugin-bundle-manifest'
|
|
33
|
+
// @ts-expect-error: This file is generated at build time
|
|
34
|
+
// eslint-disable-next-line import/no-unresolved
|
|
35
|
+
import bundleManifest from '../assets/bundle-manifest.json'
|
|
36
|
+
|
|
37
|
+
// @NOTE Not relying on ManifestItem to describe this type to avoid dependency
|
|
38
|
+
// of built code on '@atproto-labs/rollup-plugin-bundle-manifest'.
|
|
39
|
+
export type Asset =
|
|
40
|
+
| {
|
|
41
|
+
type: 'asset'
|
|
42
|
+
mime?: string
|
|
43
|
+
sha256: string
|
|
44
|
+
stream: () => Readable
|
|
45
|
+
}
|
|
46
|
+
| {
|
|
47
|
+
type: 'chunk'
|
|
48
|
+
mime: string
|
|
49
|
+
sha256: string
|
|
50
|
+
dynamicImports: string[]
|
|
51
|
+
isDynamicEntry: boolean
|
|
52
|
+
isEntry: boolean
|
|
53
|
+
isImplicitEntry: boolean
|
|
54
|
+
name: string
|
|
55
|
+
stream: () => Readable
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const assets = new Map<string, Asset>(
|
|
59
|
+
Object.entries<ManifestItem>(bundleManifest).map(
|
|
60
|
+
([filename, { data, ...item }]) => {
|
|
61
|
+
const buffer = data ? Buffer.from(data, 'base64') : null
|
|
62
|
+
const stream = buffer
|
|
63
|
+
? () => Readable.from(buffer)
|
|
64
|
+
: () =>
|
|
65
|
+
// ESM version:
|
|
66
|
+
// createReadStream(new URL(`../assets/${filename}`, import.meta.url))
|
|
67
|
+
// CJS version:
|
|
68
|
+
createReadStream(join(__dirname, '..', 'assets', filename))
|
|
69
|
+
return [filename, { ...item, stream }]
|
|
70
|
+
},
|
|
71
|
+
),
|
|
72
|
+
)
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atproto/oauth-provider-ui",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Sign-in & Sign-up UI for the @atproto/oauth-provider",
|
|
6
|
+
"homepage": "https://atproto.com",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/bluesky-social/atproto",
|
|
10
|
+
"directory": "packages/oauth/oauth-provider-ui"
|
|
11
|
+
},
|
|
12
|
+
"type": "commonjs",
|
|
13
|
+
"main": "dist/lib/index.js",
|
|
14
|
+
"types": "dist/lib/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/lib/index.d.ts",
|
|
18
|
+
"default": "./dist/lib/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.7.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@hcaptcha/react-hcaptcha": "^1.11.2",
|
|
26
|
+
"@lingui/cli": "^5.2.0",
|
|
27
|
+
"@lingui/core": "^5.2.0",
|
|
28
|
+
"@lingui/react": "^5.2.0",
|
|
29
|
+
"@lingui/swc-plugin": "^5.4.0",
|
|
30
|
+
"@lingui/vite-plugin": "^5.2.0",
|
|
31
|
+
"@rollup/plugin-commonjs": "^28.0.2",
|
|
32
|
+
"@rollup/plugin-dynamic-import-vars": "^2.1.5",
|
|
33
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
34
|
+
"@rollup/plugin-swc": "^0.4.0",
|
|
35
|
+
"@swc/core": "^1.10.18",
|
|
36
|
+
"@swc/helpers": "^0.5.15",
|
|
37
|
+
"@types/react": "^19.0.10",
|
|
38
|
+
"@types/react-dom": "^19.0.4",
|
|
39
|
+
"@vitejs/plugin-react-swc": "^3.8.0",
|
|
40
|
+
"@web/rollup-plugin-import-meta-assets": "^2.2.1",
|
|
41
|
+
"autoprefixer": "^10.4.17",
|
|
42
|
+
"postcss": "^8.4.38",
|
|
43
|
+
"react": "^19.0.0",
|
|
44
|
+
"react-dom": "^19.0.0",
|
|
45
|
+
"react-error-boundary": "^5.0.0",
|
|
46
|
+
"rollup": "^4.13.0",
|
|
47
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
48
|
+
"tailwindcss": "^3.4.3",
|
|
49
|
+
"typescript": "^5.6.3",
|
|
50
|
+
"vite": "^6.2.0",
|
|
51
|
+
"@atproto-labs/fetch": "0.2.2",
|
|
52
|
+
"@atproto-labs/rollup-plugin-bundle-manifest": "0.1.2",
|
|
53
|
+
"@atproto/oauth-provider-api": "0.0.1",
|
|
54
|
+
"@atproto/oauth-types": "0.2.4"
|
|
55
|
+
},
|
|
56
|
+
"postcss": {
|
|
57
|
+
"plugins": {
|
|
58
|
+
"tailwindcss": {},
|
|
59
|
+
"autoprefixer": {}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"po:extract": "lingui extract --clean",
|
|
64
|
+
"po:compile": "lingui compile --typescript",
|
|
65
|
+
"prebuild:frontend": "pnpm run po:compile",
|
|
66
|
+
"build:frontend": "rollup --config rollup.config.js",
|
|
67
|
+
"build:backend": "tsc --build tsconfig.backend.json",
|
|
68
|
+
"dev:ui": "vite",
|
|
69
|
+
"dev:frontend": "rollup --config rollup.config.js --watch",
|
|
70
|
+
"dev:catalogs": "pnpm run po:extract --debounce 250 --watch > /dev/null",
|
|
71
|
+
"dev:messages": "pnpm run po:compile --debounce 500 --watch"
|
|
72
|
+
}
|
|
73
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
|
|
3
|
+
const { default: commonjs } = require('@rollup/plugin-commonjs')
|
|
4
|
+
const {
|
|
5
|
+
default: dynamicImportVars,
|
|
6
|
+
} = require('@rollup/plugin-dynamic-import-vars')
|
|
7
|
+
const { default: nodeResolve } = require('@rollup/plugin-node-resolve')
|
|
8
|
+
const { default: swc } = require('@rollup/plugin-swc')
|
|
9
|
+
const {
|
|
10
|
+
default: manifest,
|
|
11
|
+
} = require('@atproto-labs/rollup-plugin-bundle-manifest')
|
|
12
|
+
const postcss = ((m) => m.default || m)(require('rollup-plugin-postcss'))
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @type {import('rollup').RollupOptionsFunction}
|
|
16
|
+
*/
|
|
17
|
+
module.exports = (commandLineArguments) => {
|
|
18
|
+
const NODE_ENV =
|
|
19
|
+
process.env['NODE_ENV'] ??
|
|
20
|
+
(commandLineArguments.watch ? 'development' : 'production')
|
|
21
|
+
|
|
22
|
+
const devMode = NODE_ENV === 'development'
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
input: [`src/authorization-page.tsx`, `src/error-page.tsx`],
|
|
26
|
+
output: {
|
|
27
|
+
manualChunks: undefined,
|
|
28
|
+
sourcemap: true,
|
|
29
|
+
dir: 'dist/assets',
|
|
30
|
+
format: 'module',
|
|
31
|
+
entryFileNames: devMode ? '[name]-[hash].js' : '[hash].js',
|
|
32
|
+
},
|
|
33
|
+
plugins: [
|
|
34
|
+
{
|
|
35
|
+
name: 'resolve-swc-helpers',
|
|
36
|
+
resolveId(src) {
|
|
37
|
+
// For some reason, "nodeResolve" doesn't resolve these:
|
|
38
|
+
if (src.startsWith('@swc/helpers/')) return require.resolve(src)
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
nodeResolve({
|
|
42
|
+
preferBuiltins: false,
|
|
43
|
+
browser: true,
|
|
44
|
+
exportConditions: ['browser', 'module', 'import', 'default'],
|
|
45
|
+
}),
|
|
46
|
+
commonjs(),
|
|
47
|
+
postcss({ config: true, extract: true, minimize: !devMode }),
|
|
48
|
+
swc({
|
|
49
|
+
swc: {
|
|
50
|
+
swcrc: false,
|
|
51
|
+
configFile: false,
|
|
52
|
+
sourceMaps: true,
|
|
53
|
+
minify: !devMode,
|
|
54
|
+
jsc: {
|
|
55
|
+
experimental: {
|
|
56
|
+
// @NOTE Because of the experimental nature of SWC plugins, A
|
|
57
|
+
// very particular version of @swc/core needs to be used. The
|
|
58
|
+
// link below allows to determine with version of @swc/core is
|
|
59
|
+
// compatible based on the version of @lingui/swc-plugin used
|
|
60
|
+
// (click on the swc_core version in the right column to see
|
|
61
|
+
// which version of the @swc/core is compatible)
|
|
62
|
+
//
|
|
63
|
+
// https://github.com/lingui/swc-plugin?tab=readme-ov-file#compatibility
|
|
64
|
+
plugins: [['@lingui/swc-plugin', {}]],
|
|
65
|
+
},
|
|
66
|
+
minify: {
|
|
67
|
+
compress: true,
|
|
68
|
+
mangle: true,
|
|
69
|
+
},
|
|
70
|
+
externalHelpers: true,
|
|
71
|
+
target: 'es2020',
|
|
72
|
+
parser: { syntax: 'typescript', tsx: true },
|
|
73
|
+
transform: {
|
|
74
|
+
useDefineForClassFields: true,
|
|
75
|
+
react: { runtime: 'automatic' },
|
|
76
|
+
optimizer: {
|
|
77
|
+
simplify: true,
|
|
78
|
+
globals: {
|
|
79
|
+
vars: {
|
|
80
|
+
'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
dynamicImportVars({ errorWhenNoFilesFound: true }),
|
|
89
|
+
|
|
90
|
+
// Change `data` to `true` to include assets data in the manifest,
|
|
91
|
+
// allowing for easier bundling of the backend code (eg. using esbuild) as
|
|
92
|
+
// bundlers know how to bundle JSON files but not how to bundle assets
|
|
93
|
+
// referenced at runtime.
|
|
94
|
+
manifest({ data: false }),
|
|
95
|
+
],
|
|
96
|
+
onwarn(warning, warn) {
|
|
97
|
+
// 'use client' directives are fine
|
|
98
|
+
if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return
|
|
99
|
+
warn(warning)
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Mock - OAuth Provider</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script>
|
|
11
|
+
/*
|
|
12
|
+
* This file's purpose is to provide a way to develop the UI without
|
|
13
|
+
* running a full featured OAuth server. It mocks the server responses and
|
|
14
|
+
* provides configuration data to the UI.
|
|
15
|
+
*
|
|
16
|
+
* This file is not part of the production build.
|
|
17
|
+
*
|
|
18
|
+
* Start the development server with the following command from the
|
|
19
|
+
* oauth-provider root:
|
|
20
|
+
*
|
|
21
|
+
* ```sh
|
|
22
|
+
* pnpm run start:ui
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Then open the browser at http://localhost:5173/
|
|
26
|
+
*/
|
|
27
|
+
</script>
|
|
28
|
+
<style>
|
|
29
|
+
/*
|
|
30
|
+
* PDS branding configuration (colors), in R G B format.
|
|
31
|
+
*
|
|
32
|
+
* The variables here are meant to override the default values defined in
|
|
33
|
+
* main.css. These values are typically generated by the backend and
|
|
34
|
+
* injected into the HTML. The colors suffixed with "-c" denote the
|
|
35
|
+
* "contrast" color of the corresponding color name. These are also
|
|
36
|
+
* automatically generated by the backend from the branding colors.
|
|
37
|
+
*
|
|
38
|
+
* The default colors can be seen by commenting out a color name (and
|
|
39
|
+
* corresponding "-c" contrast color) below:
|
|
40
|
+
*/
|
|
41
|
+
:root {
|
|
42
|
+
--color-brand: 10 122 255;
|
|
43
|
+
--color-brand-c: 255 255 255;
|
|
44
|
+
--color-error: 244 11 66;
|
|
45
|
+
--color-error-c: 255 255 255;
|
|
46
|
+
--color-warning: 251 86 7;
|
|
47
|
+
--color-warning-c: 255 255 255;
|
|
48
|
+
--color-success: 2 195 154;
|
|
49
|
+
--color-success-c: 0 0 0;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
<script type="module">
|
|
53
|
+
/*
|
|
54
|
+
* PDS branding configuration
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
const name = 'Bluesky'
|
|
58
|
+
const links = [
|
|
59
|
+
{
|
|
60
|
+
title: { en: 'Home' },
|
|
61
|
+
href: 'https://bsky.social/',
|
|
62
|
+
rel: 'canonical', // prevents the login page from being indexed by search engines
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
title: { en: 'Terms of Service' },
|
|
66
|
+
href: 'https://bsky.social/about/support/tos',
|
|
67
|
+
rel: 'terms-of-service',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
title: { en: 'Privacy Policy' },
|
|
71
|
+
href: 'https://bsky.social/about/support/privacy-policy',
|
|
72
|
+
rel: 'privacy-policy',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
title: { en: 'Support' },
|
|
76
|
+
href: 'https://blueskyweb.zendesk.com/hc/en-us',
|
|
77
|
+
rel: 'help',
|
|
78
|
+
},
|
|
79
|
+
]
|
|
80
|
+
const logo = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 320 286"><path fill="rgb(10,122,255)" d="M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z" /></svg>')}`
|
|
81
|
+
|
|
82
|
+
// Provide a value here to test the "sing-in only" flow
|
|
83
|
+
const loginHint = undefined // 'alice.test'
|
|
84
|
+
|
|
85
|
+
// Use empty array to disable the "sing-up" flow, use a single value to
|
|
86
|
+
// disable the domain selector.
|
|
87
|
+
const availableUserDomains = ['.bsky.social', '.bsky.team']
|
|
88
|
+
|
|
89
|
+
// Use non empty string to enable hCaptcha during "sing-up" flow
|
|
90
|
+
const hcaptchaSiteKey = undefined
|
|
91
|
+
|
|
92
|
+
/*
|
|
93
|
+
* Client branding configuration
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
// Use an "http://" URL to test the "an app on your device" flow
|
|
97
|
+
const clientId = 'https://example.com/client.json'
|
|
98
|
+
const clientName = 'My App'
|
|
99
|
+
const clientPolicyUri = 'https://bsky.app'
|
|
100
|
+
const clientTosUri = 'https://bsky.app'
|
|
101
|
+
const clientLogoUri = 'https://bsky.app'
|
|
102
|
+
|
|
103
|
+
// Mock data
|
|
104
|
+
|
|
105
|
+
const requestUri = 'foo-bar'
|
|
106
|
+
|
|
107
|
+
document.cookie = `csrf-${requestUri}=xyz; path=/`
|
|
108
|
+
|
|
109
|
+
window.__availableLocales = ['en', 'fr']
|
|
110
|
+
|
|
111
|
+
window.__customizationData = {
|
|
112
|
+
availableUserDomains,
|
|
113
|
+
inviteCodeRequired: false,
|
|
114
|
+
hcaptchaSiteKey,
|
|
115
|
+
name,
|
|
116
|
+
links,
|
|
117
|
+
logo,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
window.__authorizeData = {
|
|
121
|
+
clientId: clientId,
|
|
122
|
+
clientMetadata: {
|
|
123
|
+
client_id: clientId,
|
|
124
|
+
client_name: clientName,
|
|
125
|
+
policy_uri: clientPolicyUri,
|
|
126
|
+
tos_uri: clientTosUri,
|
|
127
|
+
logo_uri: clientLogoUri,
|
|
128
|
+
},
|
|
129
|
+
clientTrusted: false,
|
|
130
|
+
requestUri,
|
|
131
|
+
loginHint,
|
|
132
|
+
newSessionsRequireConsent: true,
|
|
133
|
+
sessions: [],
|
|
134
|
+
scopeDetails: [
|
|
135
|
+
{ scope: 'atproto' },
|
|
136
|
+
{ scope: 'transition:generic' },
|
|
137
|
+
{ scope: 'transition:chat.bsky' },
|
|
138
|
+
],
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const origFetch = window.fetch
|
|
142
|
+
|
|
143
|
+
async function mockFetch(...args) {
|
|
144
|
+
const [input, init] = args
|
|
145
|
+
|
|
146
|
+
if (typeof input === 'string' && init.method === 'POST') {
|
|
147
|
+
const url = new URL(input, window.location)
|
|
148
|
+
switch (url.pathname) {
|
|
149
|
+
case '/oauth/authorize/sign-up':
|
|
150
|
+
case '/oauth/authorize/sign-in':
|
|
151
|
+
return new Response(
|
|
152
|
+
JSON.stringify({
|
|
153
|
+
consentRequired: false,
|
|
154
|
+
account: {
|
|
155
|
+
sub: 'did:plc:123',
|
|
156
|
+
name: 'Alice',
|
|
157
|
+
email: 'alice@test.com',
|
|
158
|
+
email_verified: false,
|
|
159
|
+
preferred_username: 'alice.test',
|
|
160
|
+
picture: 'https://cat.com/cat.jpg',
|
|
161
|
+
},
|
|
162
|
+
}),
|
|
163
|
+
{ status: 200 },
|
|
164
|
+
)
|
|
165
|
+
case '/oauth/authorize/verify-handle-availability':
|
|
166
|
+
case '/oauth/authorize/reset-password-request':
|
|
167
|
+
case '/oauth/authorize/reset-password-confirm':
|
|
168
|
+
return new Response(null, { status: 204 })
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return origFetch.call(this, ...args)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
Object.defineProperty(window, 'fetch', {
|
|
176
|
+
value: mockFetch,
|
|
177
|
+
writable: true,
|
|
178
|
+
configurable: true,
|
|
179
|
+
})
|
|
180
|
+
</script>
|
|
181
|
+
<script src="./authorization-page.tsx" type="module"></script>
|
|
182
|
+
</body>
|
|
183
|
+
</html>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import './styles.css'
|
|
2
|
+
|
|
3
|
+
import { StrictMode } from 'react'
|
|
4
|
+
import { createRoot } from 'react-dom/client'
|
|
5
|
+
import { ErrorBoundary } from 'react-error-boundary'
|
|
6
|
+
import type {
|
|
7
|
+
AuthorizeData,
|
|
8
|
+
AvailableLocales,
|
|
9
|
+
CustomizationData,
|
|
10
|
+
} from '@atproto/oauth-provider-api'
|
|
11
|
+
import { readBackendData } from './lib/backend-data.ts'
|
|
12
|
+
import { LocaleProvider } from './locales/locale-provider.tsx'
|
|
13
|
+
import { AuthorizeView } from './views/authorize/authorize-view.tsx'
|
|
14
|
+
import { ErrorView } from './views/error/error-view.tsx'
|
|
15
|
+
|
|
16
|
+
export const availableLocales =
|
|
17
|
+
readBackendData<AvailableLocales>('__availableLocales')
|
|
18
|
+
export const customizationData = readBackendData<CustomizationData>(
|
|
19
|
+
'__customizationData',
|
|
20
|
+
)
|
|
21
|
+
export const authorizeData = readBackendData<AuthorizeData>('__authorizeData')
|
|
22
|
+
|
|
23
|
+
if (authorizeData) {
|
|
24
|
+
// When the user is logging in, make sure the page URL contains the
|
|
25
|
+
// "request_uri" in case the user refreshes the page.
|
|
26
|
+
const url = new URL(window.location.href)
|
|
27
|
+
if (
|
|
28
|
+
url.pathname === '/oauth/authorize' &&
|
|
29
|
+
!url.searchParams.has('request_uri')
|
|
30
|
+
) {
|
|
31
|
+
url.search = ''
|
|
32
|
+
url.searchParams.set('client_id', authorizeData.clientId)
|
|
33
|
+
url.searchParams.set('request_uri', authorizeData.requestUri)
|
|
34
|
+
window.history.replaceState(history.state, '', url.pathname + url.search)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const container = document.getElementById('root')!
|
|
39
|
+
|
|
40
|
+
createRoot(container).render(
|
|
41
|
+
<StrictMode>
|
|
42
|
+
<LocaleProvider availableLocales={availableLocales}>
|
|
43
|
+
<ErrorBoundary
|
|
44
|
+
fallbackRender={({ error }) => (
|
|
45
|
+
<ErrorView error={error} customizationData={customizationData} />
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<AuthorizeView
|
|
49
|
+
customizationData={customizationData}
|
|
50
|
+
authorizeData={authorizeData}
|
|
51
|
+
/>
|
|
52
|
+
</ErrorBoundary>
|
|
53
|
+
</LocaleProvider>
|
|
54
|
+
</StrictMode>,
|
|
55
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuthorizeData,
|
|
3
|
+
AvailableLocales,
|
|
4
|
+
CustomizationData,
|
|
5
|
+
ErrorData,
|
|
6
|
+
} from '@atproto/oauth-provider-api'
|
|
7
|
+
|
|
8
|
+
function readBackendData<T>(key: string, required: true): T
|
|
9
|
+
function readBackendData<T>(key: string, requires?: false): T | undefined
|
|
10
|
+
function readBackendData<T>(key: string, required = false): T | undefined {
|
|
11
|
+
const value = window[key] as T | undefined
|
|
12
|
+
delete window[key] // Prevent accidental usage / potential leaks to dependencies
|
|
13
|
+
if (required && value === undefined) {
|
|
14
|
+
throw new TypeError(`Backend data "${key}" is missing`)
|
|
15
|
+
}
|
|
16
|
+
return value
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// These values are injected by the backend when it builds the
|
|
20
|
+
// page HTML. See "declareBackendData()" in the backend.
|
|
21
|
+
|
|
22
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
|
23
|
+
export const availableLocales = readBackendData<AvailableLocales>(
|
|
24
|
+
'__availableLocales',
|
|
25
|
+
true,
|
|
26
|
+
)
|
|
27
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
|
28
|
+
export const customizationData = readBackendData<CustomizationData>(
|
|
29
|
+
'__customizationData',
|
|
30
|
+
true,
|
|
31
|
+
)
|
|
32
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
|
33
|
+
export const errorData = readBackendData<ErrorData>('__errorData')
|
|
34
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
|
35
|
+
export const authorizeData = readBackendData<AuthorizeData>('__authorizeData')
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useLingui } from '@lingui/react/macro'
|
|
2
|
+
import { Override } from '../../lib/util.ts'
|
|
3
|
+
import { EyeIcon, EyeSlashIcon } from '../utils/icons.tsx'
|
|
4
|
+
import { Button, ButtonProps } from './button.tsx'
|
|
5
|
+
|
|
6
|
+
export type ButtonToggleVisibilityProps = Override<
|
|
7
|
+
Omit<ButtonProps, 'aria-label' | 'square'>,
|
|
8
|
+
{
|
|
9
|
+
visible: boolean
|
|
10
|
+
toggleVisible: () => void
|
|
11
|
+
}
|
|
12
|
+
>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generic button to toggle visibility of an item (e.g. password).
|
|
16
|
+
*/
|
|
17
|
+
export function ButtonToggleVisibility({
|
|
18
|
+
visible,
|
|
19
|
+
toggleVisible,
|
|
20
|
+
|
|
21
|
+
// button
|
|
22
|
+
onClick,
|
|
23
|
+
...props
|
|
24
|
+
}: ButtonToggleVisibilityProps) {
|
|
25
|
+
const { t } = useLingui()
|
|
26
|
+
return (
|
|
27
|
+
<Button
|
|
28
|
+
{...props}
|
|
29
|
+
square
|
|
30
|
+
onClick={(event) => {
|
|
31
|
+
onClick?.(event)
|
|
32
|
+
if (!event.defaultPrevented) toggleVisible()
|
|
33
|
+
}}
|
|
34
|
+
aria-label={visible ? t`Hide` : t`Make visible`}
|
|
35
|
+
>
|
|
36
|
+
{visible ? (
|
|
37
|
+
<EyeIcon className="w-5" aria-hidden />
|
|
38
|
+
) : (
|
|
39
|
+
<EyeSlashIcon className="w-5" aria-hidden />
|
|
40
|
+
)}
|
|
41
|
+
</Button>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { JSX } from 'react'
|
|
2
|
+
import { clsx } from '../../lib/clsx.ts'
|
|
3
|
+
import { Override } from '../../lib/util.ts'
|
|
4
|
+
|
|
5
|
+
export type ButtonProps = Override<
|
|
6
|
+
JSX.IntrinsicElements['button'],
|
|
7
|
+
{
|
|
8
|
+
color?: 'brand' | 'grey'
|
|
9
|
+
loading?: boolean
|
|
10
|
+
transparent?: boolean
|
|
11
|
+
square?: boolean
|
|
12
|
+
}
|
|
13
|
+
>
|
|
14
|
+
|
|
15
|
+
export function Button({
|
|
16
|
+
color = 'grey',
|
|
17
|
+
transparent = false,
|
|
18
|
+
loading = undefined,
|
|
19
|
+
square = false,
|
|
20
|
+
|
|
21
|
+
// button
|
|
22
|
+
children,
|
|
23
|
+
className,
|
|
24
|
+
type = 'button',
|
|
25
|
+
role = 'Button',
|
|
26
|
+
disabled = false,
|
|
27
|
+
...props
|
|
28
|
+
}: ButtonProps) {
|
|
29
|
+
return (
|
|
30
|
+
<button
|
|
31
|
+
role={role}
|
|
32
|
+
type={type}
|
|
33
|
+
disabled={disabled || loading === true}
|
|
34
|
+
{...props}
|
|
35
|
+
className={clsx(
|
|
36
|
+
'rounded-lg truncate cursor-pointer touch-manipulation tracking-wide overflow-hidden',
|
|
37
|
+
square ? 'p-2' : 'py-2 px-6',
|
|
38
|
+
color === 'brand'
|
|
39
|
+
? clsx(
|
|
40
|
+
'accent-slate-100',
|
|
41
|
+
transparent
|
|
42
|
+
? 'bg-transparent text-brand'
|
|
43
|
+
: 'bg-brand text-brand-c',
|
|
44
|
+
)
|
|
45
|
+
: color === 'grey'
|
|
46
|
+
? clsx(
|
|
47
|
+
'accent-brand',
|
|
48
|
+
'text-slate-600 dark:text-slate-300',
|
|
49
|
+
'hover:bg-gray-200 dark:hover:bg-gray-700',
|
|
50
|
+
transparent ? 'bg-transparent' : 'bg-gray-100 dark:bg-gray-800',
|
|
51
|
+
)
|
|
52
|
+
: undefined,
|
|
53
|
+
'disabled:opacity-50',
|
|
54
|
+
className,
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
{children}
|
|
58
|
+
</button>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { JSX, ReactNode, createContext, useMemo } from 'react'
|
|
2
|
+
import { useRandomString } from '../../hooks/use-random-string.ts'
|
|
3
|
+
import { Override } from '../../lib/util.ts'
|
|
4
|
+
|
|
5
|
+
export type FieldsetContextValue = {
|
|
6
|
+
disabled: boolean
|
|
7
|
+
labelId?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const FieldsetContext = createContext<FieldsetContextValue>({
|
|
11
|
+
disabled: false,
|
|
12
|
+
})
|
|
13
|
+
FieldsetContext.displayName = 'FieldsetContext'
|
|
14
|
+
|
|
15
|
+
export type FieldsetCardProps = Override<
|
|
16
|
+
Omit<JSX.IntrinsicElements['fieldset'], 'aria-labelledby'>,
|
|
17
|
+
{
|
|
18
|
+
label?: ReactNode
|
|
19
|
+
}
|
|
20
|
+
>
|
|
21
|
+
|
|
22
|
+
export function Fieldset({
|
|
23
|
+
label,
|
|
24
|
+
children,
|
|
25
|
+
disabled,
|
|
26
|
+
...props
|
|
27
|
+
}: FieldsetCardProps) {
|
|
28
|
+
const labelId = useRandomString({ prefix: 'fieldset-' })
|
|
29
|
+
|
|
30
|
+
const contextValue = useMemo(
|
|
31
|
+
() => ({
|
|
32
|
+
disabled: disabled ?? false,
|
|
33
|
+
labelId: label ? labelId : undefined,
|
|
34
|
+
}),
|
|
35
|
+
[disabled, label, labelId],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<fieldset {...props} aria-labelledby={labelId} disabled={disabled}>
|
|
40
|
+
{label && (
|
|
41
|
+
<legend
|
|
42
|
+
id={labelId}
|
|
43
|
+
key="title"
|
|
44
|
+
className="mb-1 text-slate-600 dark:text-slate-400 text-sm font-medium"
|
|
45
|
+
>
|
|
46
|
+
{label}
|
|
47
|
+
</legend>
|
|
48
|
+
)}
|
|
49
|
+
|
|
50
|
+
<div className="flex flex-col space-y-4">
|
|
51
|
+
<FieldsetContext value={contextValue}>{children}</FieldsetContext>
|
|
52
|
+
</div>
|
|
53
|
+
</fieldset>
|
|
54
|
+
)
|
|
55
|
+
}
|