diff --git a/packages/sign-up-with-password/.eslintrc.cjs b/packages/sign-up-with-password/.eslintrc.cjs new file mode 100644 index 0000000..0b1a0e0 --- /dev/null +++ b/packages/sign-up-with-password/.eslintrc.cjs @@ -0,0 +1,24 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: '@silverhand/eslint-config', + rules: { + 'jsx-a11y/no-autofocus': 'off', + 'unicorn/prefer-string-replace-all': 'off', + 'no-restricted-syntax': 'off', + '@silverhand/fp/no-mutation': 'off', + }, + overrides: [ + { + files: ['*.config.js', '*.config.ts', '*.d.ts'], + rules: { + 'import/no-unused-modules': 'off', + }, + }, + { + files: ['*.d.ts'], + rules: { + 'import/no-unassigned-import': 'off', + }, + }, + ], +}; diff --git a/packages/sign-up-with-password/favicon.ico b/packages/sign-up-with-password/favicon.ico new file mode 100644 index 0000000..3cf672d Binary files /dev/null and b/packages/sign-up-with-password/favicon.ico differ diff --git a/packages/sign-up-with-password/index.html b/packages/sign-up-with-password/index.html new file mode 100644 index 0000000..a7c7efd --- /dev/null +++ b/packages/sign-up-with-password/index.html @@ -0,0 +1,36 @@ + + + + + + Logto experience sample + + + + + + +
+
+ +
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + diff --git a/packages/sign-up-with-password/package.json b/packages/sign-up-with-password/package.json new file mode 100644 index 0000000..9a70ca9 --- /dev/null +++ b/packages/sign-up-with-password/package.json @@ -0,0 +1,32 @@ +{ + "name": "@logto/experience-sample-password-sign-up", + "description": "A sample project demonstrates how to use Logto Experience API to build a identifier & password sign-up page.", + "author": "Silverhand Inc. ", + "license": "MIT", + "version": "0.0.0", + "type": "module", + "scripts": { + "precommit": "lint-staged", + "start": "vite", + "dev": "logto-tunnel --verbose & vite", + "build": "tsc -b && vite build", + "lint": "eslint --ext .ts src", + "preview": "vite preview" + }, + "devDependencies": { + "@logto/experience-sample-shared": "workspace:^", + "@logto/schemas": "^1.19.0", + "@silverhand/eslint-config": "^6.0.1", + "@silverhand/ts-config": "^6.0.0", + "eslint": "^8.56.0", + "lint-staged": "^15.0.0", + "prettier": "^3.0.0", + "stylelint": "^15.0.0", + "typescript": "^5.5.3", + "vite": "^5.4.0" + }, + "stylelint": { + "extends": "@silverhand/eslint-config/.stylelintrc" + }, + "prettier": "@silverhand/eslint-config/.prettierrc" +} diff --git a/packages/sign-up-with-password/src/include.d/vite-env.d.ts b/packages/sign-up-with-password/src/include.d/vite-env.d.ts new file mode 100644 index 0000000..e058f21 --- /dev/null +++ b/packages/sign-up-with-password/src/include.d/vite-env.d.ts @@ -0,0 +1 @@ +import 'vite/client'; diff --git a/packages/sign-up-with-password/src/index.ts b/packages/sign-up-with-password/src/index.ts new file mode 100644 index 0000000..4424a45 --- /dev/null +++ b/packages/sign-up-with-password/src/index.ts @@ -0,0 +1,64 @@ +import { Api } from '@logto/experience-sample-shared/api'; +import { clearError, handleError, setSubmitLoading } from '@logto/experience-sample-shared/utils'; +import { InteractionEvent } from '@logto/schemas'; + +import '@logto/experience-sample-shared/scss/normalized.scss'; + +const api = new Api({ baseUrl: window.location.origin }); + +window.addEventListener('load', () => { + const form = document.querySelector('form'); + + form?.addEventListener('submit', async (event) => { + event.preventDefault(); + setSubmitLoading(true); + clearError(); + + try { + const formData = new FormData(form); + const username = formData.get('username')?.toString(); + const password = formData.get('password')?.toString(); + const confirmPassword = formData.get('confirm-password')?.toString(); + + if (!username || !password) { + throw new Error('Username and password are required.'); + } + + if (password !== confirmPassword) { + throw new Error('Passwords do not match.'); + } + + /** + * Step 1: Initialize a register type interaction. + */ + await api.experience.initInteraction({ interactionEvent: InteractionEvent.Register }); + + /** + * Step 2: Ensure the username is not already taken. + */ + await api.experience.addUserProfile({ type: 'username', value: username }); + + /** + * Step 3: Continue registering with password. + */ + await api.experience.addUserProfile({ type: 'password', value: password }); + + /** + * Step 4: Identify the user. + * + * Note: Unlike registering with verification code or social, register with identifier and password + * does not require a verification step in prior, but the identification step is still required. + */ + await api.experience.identifyUser({}); + + /** + * Step 5: Submit the interaction and redirect back to your app after the interaction is completed. + */ + const { redirectTo } = await api.experience.submitInteraction(); + window.location.replace(redirectTo); + } catch (error) { + handleError(error); + setSubmitLoading(false); + } + }); +}); diff --git a/packages/sign-up-with-password/tsconfig.json b/packages/sign-up-with-password/tsconfig.json new file mode 100644 index 0000000..b962e88 --- /dev/null +++ b/packages/sign-up-with-password/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@silverhand/ts-config/tsconfig.base", + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 062851c..2d11e42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,6 +84,39 @@ importers: specifier: ^5.4.6 version: 5.4.6(@types/node@22.3.0)(sass-embedded@1.77.8) + packages/sign-up-with-password: + devDependencies: + '@logto/experience-sample-shared': + specifier: workspace:^ + version: link:../shared + '@logto/schemas': + specifier: ^1.19.0 + version: 1.19.0(zod@3.23.8) + '@silverhand/eslint-config': + specifier: ^6.0.1 + version: 6.0.1(eslint@8.57.0)(prettier@3.3.3)(typescript@5.5.4) + '@silverhand/ts-config': + specifier: ^6.0.0 + version: 6.0.0(typescript@5.5.4) + eslint: + specifier: ^8.56.0 + version: 8.57.0 + lint-staged: + specifier: ^15.0.0 + version: 15.2.9 + prettier: + specifier: ^3.0.0 + version: 3.3.3 + stylelint: + specifier: ^15.0.0 + version: 15.11.0(typescript@5.5.4) + typescript: + specifier: ^5.5.3 + version: 5.5.4 + vite: + specifier: ^5.4.0 + version: 5.4.6(@types/node@22.3.0)(sass-embedded@1.77.8) + packages: '@babel/code-frame@7.24.7': @@ -3240,10 +3273,10 @@ snapshots: eslint-config-prettier: 9.1.0(eslint@8.57.0) eslint-config-xo: 0.44.0(eslint@8.57.0) eslint-config-xo-typescript: 4.0.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4))(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-consistent-default-export-name: 0.0.15 eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-n: 17.10.2(eslint@8.57.0) eslint-plugin-no-use-extend-native: 0.5.0 eslint-plugin-prettier: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3) @@ -3930,13 +3963,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -3947,14 +3980,14 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -3976,7 +4009,7 @@ snapshots: eslint: 8.57.0 ignore: 5.3.2 - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -3986,7 +4019,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3