├── src ├── routes │ ├── logout │ │ ├── +page.svelte │ │ └── +page.ts │ ├── +layout.ts │ ├── +layout.svelte │ ├── +page.svelte │ ├── dashboard │ │ ├── +layout.svelte │ │ └── +page.svelte │ ├── recovery │ │ └── +page.svelte │ ├── settings │ │ └── +page.svelte │ ├── registration │ │ └── +page.svelte │ ├── login │ │ └── +page.svelte │ └── verification │ │ └── +page.svelte ├── lib │ ├── config.ts │ ├── stores │ │ └── kratos │ │ │ └── identity.ts │ ├── http │ │ └── index.ts │ ├── components │ │ └── kratos │ │ │ ├── fieldset-hidden.svelte │ │ │ ├── messages.svelte │ │ │ ├── fieldset-submit.svelte │ │ │ ├── form.svelte │ │ │ ├── fieldset-email.svelte │ │ │ ├── fieldset-text.svelte │ │ │ ├── fieldset-password.svelte │ │ │ ├── fieldsets.svelte │ │ │ └── fieldset-password-toggle.svelte │ └── kratos │ │ ├── index.ts │ │ └── types.ts ├── app.d.ts └── app.html ├── .npmrc ├── static └── favicon.png ├── .eslintignore ├── .prettierignore ├── .gitignore ├── vite.config.js ├── .prettierrc ├── svelte.config.js ├── tsconfig.json ├── .eslintrc.cjs ├── package.json ├── README.md └── docs ├── my-kratos.yml └── local-test.md /src/routes/logout/+page.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrahcom/kratos-selfservice-svelte-node/HEAD/static/favicon.png -------------------------------------------------------------------------------- /src/lib/config.ts: -------------------------------------------------------------------------------- 1 | export const KRATOS = "https://kratos.mydomain.corp"; 2 | export const APP = "https://app.mydomain.corp"; 3 | -------------------------------------------------------------------------------- /src/lib/stores/kratos/identity.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | import type { KratosIdentity } from "$lib/kratos/types"; 3 | 4 | export default writable({} as KratosIdentity); 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | *~ 12 | *.sw? 13 | *.log 14 | package-lock.json 15 | yarn.lock 16 | -------------------------------------------------------------------------------- /src/lib/http/index.ts: -------------------------------------------------------------------------------- 1 | export async function get(url: string) { 2 | const res = await fetch(url, { 3 | credentials: "include", 4 | headers: { 5 | "Accept": "application/json", 6 | }, 7 | mode: "cors", 8 | }); 9 | 10 | return res; 11 | } 12 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from "@sveltejs/kit/vite"; 2 | import { defineConfig } from "vite"; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | server: { 7 | hmr: { 8 | clientPort: 3000, 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface Platform {} 9 | } 10 | } 11 | 12 | export {}; 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], 3 | "plugins": ["prettier-plugin-svelte"], 4 | "pluginSearchDirs": ["."], 5 | "printWidth": 80, 6 | "quoteProps": "preserve", 7 | "singleQuote": false, 8 | "tabWidth": 2, 9 | "trailingComma": "all", 10 | "useTabs": false 11 | } 12 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldset-hidden.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lib/components/kratos/messages.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {#if messages} 9 | {#each messages as msg} 10 | {msg.id} - 11 | {msg.type} - 12 | {msg.text}
13 | {/each} 14 | {/if} 15 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | import { getIdentity } from "$lib/kratos"; 2 | import identity from "$lib/stores/kratos/identity"; 3 | 4 | // ----------------------------------------------------------------------------- 5 | export async function load() { 6 | await getIdentity() 7 | .then((_identity) => { 8 | identity.set(_identity); 9 | }) 10 | .catch(() => { 11 | //no identity 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/logout/+page.ts: -------------------------------------------------------------------------------- 1 | import { browser } from "$app/environment"; 2 | import { getLogoutDataModels } from "$lib/kratos"; 3 | 4 | // ----------------------------------------------------------------------------- 5 | export async function load() { 6 | if (!browser) return {}; 7 | 8 | const dm = await getLogoutDataModels(); 9 | 10 | if (dm.instanceOf === "KratosLogout") { 11 | window.location.replace(dm.logout_url); 12 | } else { 13 | window.location.replace("/"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | //import adapter from '@sveltejs/adapter-auto'; 2 | import adapter from "@sveltejs/adapter-node"; 3 | import { vitePreprocess } from "@sveltejs/kit/vite"; 4 | 5 | /** @type {import('@sveltejs/kit').Config} */ 6 | const config = { 7 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 8 | // for more information about preprocessors 9 | preprocess: vitePreprocess(), 10 | 11 | kit: { 12 | adapter: adapter(), 13 | }, 14 | }; 15 | 16 | export default config; 17 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldset-submit.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 |
11 |

welcome

12 | 13 | {#if !_identity.id} 14 |

login

15 | {:else} 16 |

Dashboard

17 | {/if} 18 |
19 | -------------------------------------------------------------------------------- /src/routes/dashboard/+layout.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/kratos/form.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 14 | // 15 | // If you want to overwrite includes/excludes, make sure to copy over the 16 | // relevant includes/excludes from the referenced tsconfig.json 17 | // TypeScript does not merge them in 18 | } 19 | -------------------------------------------------------------------------------- /src/routes/dashboard/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 |
14 |

Dashboard

15 | 16 |

Hello {email}

17 |

Settings

18 |

Logout

19 |
20 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | "eslint:recommended", 5 | "plugin:@typescript-eslint/recommended", 6 | "plugin:svelte/recommended", 7 | "prettier", 8 | ], 9 | parser: "@typescript-eslint/parser", 10 | plugins: ["@typescript-eslint"], 11 | parserOptions: { 12 | sourceType: "module", 13 | ecmaVersion: 2020, 14 | extraFileExtensions: [".svelte"], 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true, 20 | }, 21 | overrides: [ 22 | { 23 | files: ["*.svelte"], 24 | parser: "svelte-eslint-parser", 25 | parserOptions: { 26 | parser: "@typescript-eslint/parser", 27 | }, 28 | }, 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldset-email.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 |
16 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldset-text.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 |
16 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldset-password.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 |
18 | 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldsets.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | {#each nodes as node} 14 | {#if node.attributes.type === "hidden"} 15 | 16 | {:else if node.attributes.type === "password"} 17 | 18 | {:else if node.attributes.type === "text"} 19 | 20 | {:else if node.attributes.type === "email"} 21 | 22 | {:else if node.attributes.type === "submit"} 23 | 24 | {:else} 25 | unknow type 26 | {/if} 27 | {/each} 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kratos-svelte-ui", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 12 | "format": "prettier --plugin-search-dir . --write ." 13 | }, 14 | "devDependencies": { 15 | "@sveltejs/adapter-auto": "^2.0.0", 16 | "@sveltejs/adapter-node": "^1.3.1", 17 | "@sveltejs/kit": "^1.20.4", 18 | "@typescript-eslint/eslint-plugin": "^5.45.0", 19 | "@typescript-eslint/parser": "^5.45.0", 20 | "eslint": "^8.28.0", 21 | "eslint-config-prettier": "^8.5.0", 22 | "eslint-plugin-svelte": "^2.30.0", 23 | "prettier": "^2.8.0", 24 | "prettier-plugin-svelte": "^2.10.1", 25 | "svelte": "^4.0.5", 26 | "svelte-check": "^3.4.3", 27 | "tslib": "^2.4.1", 28 | "typescript": "^5.0.0", 29 | "vite": "^4.4.2" 30 | }, 31 | "type": "module" 32 | } 33 | -------------------------------------------------------------------------------- /src/routes/recovery/+page.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 |
18 | {#await pr then dm} 19 | {#if dm.instanceOf === "KratosForm"} 20 |
21 |

recovery

22 | 23 | {#if dm.ui.messages} 24 | 25 | {:else} 26 |
27 | {/if} 28 |
29 | {:else} 30 |

Something went wrong

31 | {/if} 32 | {/await} 33 |
34 | -------------------------------------------------------------------------------- /src/routes/settings/+page.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 |
18 | {#await pr then dm} 19 | {#if dm.instanceOf === "KratosForm"} 20 |
21 |

Settings

22 | 23 | {#if dm.ui.messages} 24 | 25 | {:else} 26 | 27 |
28 | 29 | {/if} 30 |
31 | {:else} 32 |

Something went wrong

33 | {/if} 34 | {/await} 35 |
36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kratos-selfservice-svelte-node 2 | 3 | Self-service [Svelte](https://svelte.dev/) node for 4 | [Ory Kratos](https://github.com/ory/kratos). It has no style or decoration. 5 | Apply your custom style according to your application. 6 | 7 | ## Install 8 | 9 | - `git`, `nodejs` and `yarn` must be installed 10 | - a working `Ory Kratos` is needed 11 | 12 | ```bash 13 | git clone https://github.com/emrahcom/kratos-selfservice-svelte-node.git 14 | 15 | cd kratos-selfservice-svelte-node 16 | yarn install 17 | ``` 18 | 19 | ## Config 20 | 21 | Change `src/lib/config.ts` according to your environment. 22 | 23 | ```javascript 24 | export const KRATOS = "https://kratos.mydomain.corp"; 25 | export const APP = "https://app.mydomain.corp"; 26 | ``` 27 | 28 | ## Run (dev) 29 | 30 | ```bash 31 | yarn run dev --host --port 3000 32 | ``` 33 | 34 | ## Run (prod) 35 | 36 | ```bash 37 | yarn run build 38 | node build/index.js 39 | ``` 40 | 41 | ## Pages 42 | 43 | - Landing page 44 | 45 | `/` 46 | 47 | - Secure dashboard 48 | 49 | `/dashboard` 50 | 51 | - Registration 52 | 53 | `/registration` 54 | 55 | - Login 56 | 57 | `/login` 58 | 59 | - Settings 60 | 61 | `/settings` 62 | 63 | - Recovery 64 | 65 | `/recovery` 66 | 67 | - Verification 68 | 69 | `/verification` 70 | 71 | - Logout 72 | 73 | `/logout` 74 | -------------------------------------------------------------------------------- /src/routes/registration/+page.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 |
18 | {#await pr then dm} 19 | {#if dm.instanceOf === "KratosForm"} 20 |
21 |

Registration

22 | 23 | {#if dm.ui.messages} 24 | 25 | {/if} 26 | 27 | 28 |
29 | 30 |
31 |

Already have an account?

32 |
33 |
34 | {:else} 35 |

Something went wrong

36 | {/if} 37 | {/await} 38 |
39 | -------------------------------------------------------------------------------- /src/routes/login/+page.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 |
18 | {#await pr then dm} 19 | {#if dm.instanceOf === "KratosForm"} 20 |
21 |

Sign in

22 | 23 | {#if dm.ui.messages} 24 | 25 | {/if} 26 | 27 | 28 |
29 | 30 |
31 |

Forget password?

32 |

Don't have an account?

33 |
34 |
35 | {:else} 36 |

Something went wrong

37 | {/if} 38 | {/await} 39 |
40 | -------------------------------------------------------------------------------- /src/routes/verification/+page.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 |
27 | {#await pr then dm} 28 | {#if dm.instanceOf === "KratosForm"} 29 |
30 |

verification

31 | 32 | {#if dm.ui.messages} 33 | 34 | {:else} 35 | 36 | {/if} 37 |
38 | {:else} 39 |

Something went wrong

40 | {/if} 41 | {/await} 42 |
43 | -------------------------------------------------------------------------------- /docs/my-kratos.yml: -------------------------------------------------------------------------------- 1 | version: v0.13.0 2 | 3 | dsn: memory 4 | 5 | serve: 6 | public: 7 | base_url: https://kratos.mydomain.corp/ 8 | cors: 9 | enabled: true 10 | allowed_origins: 11 | - https://app.mydomain.corp 12 | admin: 13 | base_url: http://kratos.mydomain.corp:4434/ 14 | 15 | selfservice: 16 | default_browser_return_url: https://app.mydomain.corp/ 17 | allowed_return_urls: 18 | - https://app.mydomain.corp 19 | 20 | methods: 21 | password: 22 | enabled: true 23 | totp: 24 | config: 25 | issuer: Kratos 26 | enabled: true 27 | lookup_secret: 28 | enabled: true 29 | link: 30 | enabled: true 31 | code: 32 | enabled: true 33 | 34 | flows: 35 | error: 36 | ui_url: https://app.mydomain.corp/error 37 | 38 | settings: 39 | ui_url: https://app.mydomain.corp/settings 40 | privileged_session_max_age: 15m 41 | required_aal: highest_available 42 | 43 | recovery: 44 | enabled: true 45 | ui_url: https://app.mydomain.corp/recovery 46 | use: code 47 | 48 | verification: 49 | enabled: true 50 | ui_url: https://app.mydomain.corp/verification 51 | use: code 52 | after: 53 | default_browser_return_url: https://app.mydomain.corp/ 54 | 55 | logout: 56 | after: 57 | default_browser_return_url: https://app.mydomain.corp/login 58 | 59 | login: 60 | ui_url: https://app.mydomain.corp/login 61 | lifespan: 10m 62 | 63 | registration: 64 | lifespan: 10m 65 | ui_url: https://app.mydomain.corp/registration 66 | after: 67 | password: 68 | hooks: 69 | - hook: session 70 | - hook: show_verification_ui 71 | 72 | log: 73 | level: debug 74 | format: text 75 | leak_sensitive_values: true 76 | 77 | secrets: 78 | cookie: 79 | - PLEASE-CHANGE-ME-I-AM-VERY-INSECURE 80 | cipher: 81 | - 32-LONG-SECRET-NOT-SECURE-AT-ALL 82 | 83 | ciphers: 84 | algorithm: xchacha20-poly1305 85 | 86 | hashers: 87 | algorithm: bcrypt 88 | bcrypt: 89 | cost: 8 90 | 91 | identity: 92 | default_schema_id: default 93 | schemas: 94 | - id: default 95 | url: file:///etc/config/kratos/identity.schema.json 96 | 97 | courier: 98 | smtp: 99 | connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true 100 | -------------------------------------------------------------------------------- /docs/local-test.md: -------------------------------------------------------------------------------- 1 | # Local Test Environment 2 | 3 | ## Kratos 4 | 5 | ```bash 6 | git clone https://github.com/ory/kratos.git 7 | cd kratos 8 | git checkout v1.0.0 9 | ``` 10 | 11 | Update `./contrib/quickstart/kratos/email-password/kratos.yml` according to 12 | `my-kratos.yml` in this folder. 13 | 14 | ```bash 15 | docker-compose -f quickstart.yml up --build --force-recreate 16 | ``` 17 | 18 | ## App 19 | 20 | ```bash 21 | cd kratos-selfservice-svelte-node 22 | yarn run dev --host --port 3000 23 | ``` 24 | 25 | ## Nginx 26 | 27 | `/etc/nginx/sites-available/kratos.conf` 28 | 29 | ```conf 30 | # ------------------------------------------------------------------------------ 31 | # kratos.mydomain.corp 32 | # ------------------------------------------------------------------------------ 33 | server { 34 | listen 443 ssl; 35 | listen [::]:443 ssl; 36 | 37 | include snippets/snakeoil.conf; 38 | server_name kratos.mydomain.corp; 39 | 40 | location / { 41 | proxy_pass http://172.18.18.1:4433; 42 | proxy_http_version 1.1; 43 | proxy_set_header Host $http_host; 44 | proxy_set_header X-Real-IP $remote_addr; 45 | proxy_set_header X-Forwarded-For $remote_addr; 46 | tcp_nodelay on; 47 | } 48 | } 49 | 50 | # ------------------------------------------------------------------------------ 51 | # app.mydomain.corp 52 | # ------------------------------------------------------------------------------ 53 | server { 54 | listen 443 ssl; 55 | listen [::]:443 ssl; 56 | 57 | include snippets/snakeoil.conf; 58 | server_name app.mydomain.corp; 59 | 60 | location / { 61 | proxy_pass http://172.18.18.1:3000; 62 | proxy_http_version 1.1; 63 | proxy_set_header Host $http_host; 64 | proxy_set_header X-Real-IP $remote_addr; 65 | proxy_set_header X-Forwarded-For $remote_addr; 66 | tcp_nodelay on; 67 | } 68 | } 69 | 70 | server { 71 | listen 3000 ssl; 72 | listen [::]:3000 ssl; 73 | 74 | include snippets/snakeoil.conf; 75 | server_name app.mydomain.corp; 76 | 77 | location / { 78 | proxy_pass http://172.18.18.1:3000; 79 | proxy_http_version 1.1; 80 | proxy_set_header Host $http_host; 81 | proxy_set_header X-Real-IP $remote_addr; 82 | proxy_set_header X-Forwarded-For $remote_addr; 83 | proxy_set_header Upgrade $http_upgrade; 84 | proxy_set_header Connection "upgrade"; 85 | tcp_nodelay on; 86 | } 87 | } 88 | ``` 89 | 90 | ```bash 91 | ln -s ../sites-available/kratos.conf /etc/nginx/sites-enabled/ 92 | systemctl restart nginx 93 | ``` 94 | -------------------------------------------------------------------------------- /src/lib/kratos/index.ts: -------------------------------------------------------------------------------- 1 | import { browser } from "$app/environment"; 2 | import { KRATOS } from "$lib/config"; 3 | import { get } from "$lib/http"; 4 | import type { 5 | KratosError, 6 | KratosForm, 7 | KratosIdentity, 8 | KratosLogout, 9 | } from "$lib/kratos/types"; 10 | 11 | // ----------------------------------------------------------------------------- 12 | export function getFlowId(urlSearch: string): string { 13 | const qs = new URLSearchParams(urlSearch); 14 | const flowId = qs.get("flow"); 15 | 16 | if (flowId) return flowId; 17 | 18 | return ""; 19 | } 20 | 21 | // ----------------------------------------------------------------------------- 22 | export async function getIdentity(): Promise { 23 | if (!browser) throw new Error("no browser environment"); 24 | 25 | const url = `${KRATOS}/sessions/whoami`; 26 | const res = await get(url); 27 | 28 | if (res.status !== 200) { 29 | throw new Error("no identity"); 30 | } 31 | 32 | const dm = await res.json(); 33 | return dm.identity; 34 | } 35 | 36 | // ----------------------------------------------------------------------------- 37 | export async function getDataModels( 38 | flow: string, 39 | flowId: string, 40 | ): Promise { 41 | if (!flowId) throw new Error("no flowId"); 42 | if (!browser) throw new Error("no browser environment"); 43 | 44 | const url = `${KRATOS}/self-service/${flow}/flows?id=${flowId}`; 45 | const res = await get(url); 46 | const dm = await res.json(); 47 | 48 | if (dm.error) { 49 | dm.instanceOf = "KratosError"; 50 | 51 | if (dm.error.details && dm.error.details.redirect_to) { 52 | window.location.href = dm.error.details.redirect_to; 53 | } 54 | } else if (dm.ui) { 55 | dm.instanceOf = "KratosForm"; 56 | } else { 57 | throw new Error("unexpected Kratos object"); 58 | } 59 | 60 | return dm; 61 | } 62 | 63 | // ----------------------------------------------------------------------------- 64 | export async function getLogoutDataModels(): Promise< 65 | KratosLogout | KratosError 66 | > { 67 | if (!browser) throw new Error("no browser environment"); 68 | 69 | const url = `${KRATOS}/self-service/logout/browser`; 70 | const res = await get(url); 71 | const dm = await res.json(); 72 | 73 | if (dm.error) { 74 | dm.instanceOf = "KratosError"; 75 | } else if (dm.logout_url) { 76 | dm.instanceOf = "KratosLogout"; 77 | } else { 78 | throw new Error("unexpected Kratos object"); 79 | } 80 | 81 | return dm; 82 | } 83 | -------------------------------------------------------------------------------- /src/lib/components/kratos/fieldset-password-toggle.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 17 | {#if isHidden} 18 | 39 | {:else} 40 | 81 | {/if} 82 | 83 | -------------------------------------------------------------------------------- /src/lib/kratos/types.ts: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | export interface Attributes { 3 | name: string; 4 | type: string; 5 | value?: string; 6 | disabled: boolean; 7 | required?: boolean; 8 | } 9 | 10 | // ----------------------------------------------------------------------------- 11 | export interface Label { 12 | id: number; 13 | type: string; 14 | text: string; 15 | context?: unknown; 16 | } 17 | 18 | // ----------------------------------------------------------------------------- 19 | export interface Message { 20 | id: number; 21 | type: string; 22 | text: string; 23 | context?: unknown; 24 | } 25 | 26 | // ----------------------------------------------------------------------------- 27 | export interface Meta { 28 | label?: Label; 29 | } 30 | 31 | // ----------------------------------------------------------------------------- 32 | export interface Node { 33 | type: string; 34 | group: string; 35 | attributes: Attributes; 36 | messages: Message[]; 37 | meta: Meta; 38 | } 39 | 40 | // ----------------------------------------------------------------------------- 41 | export interface UI { 42 | action: string; 43 | method: string; 44 | messages?: Message[]; 45 | nodes: Node[]; 46 | "updated_at": string; 47 | } 48 | 49 | // ----------------------------------------------------------------------------- 50 | export interface KratosForm { 51 | instanceOf: "KratosForm"; 52 | id: string; 53 | type: string; 54 | forced?: boolean; 55 | ui: UI; 56 | "created_at"?: string; 57 | "expires_at": string; 58 | "issued_at": string; 59 | "updated_at"?: string; 60 | "request_url": string; 61 | } 62 | 63 | // ----------------------------------------------------------------------------- 64 | export interface KratosError { 65 | instanceOf: "KratosError"; 66 | error: { 67 | code: number; 68 | message: string; 69 | status: string; 70 | reason?: string; 71 | details?: { 72 | docs: string; 73 | hint: string; 74 | "redirect_to": string; 75 | "reject_reason": string; 76 | }; 77 | }; 78 | } 79 | 80 | // ----------------------------------------------------------------------------- 81 | export interface KratosLogout { 82 | instanceOf: "KratosLogout"; 83 | "logout_url": string; 84 | } 85 | 86 | // ----------------------------------------------------------------------------- 87 | export interface KratosIdentity { 88 | id: string; 89 | traits: { 90 | email: string; 91 | }; 92 | state: string; 93 | "created_at": string; 94 | "updated_at": string; 95 | } 96 | 97 | // ----------------------------------------------------------------------------- 98 | export interface KratosLoad { 99 | status?: number; 100 | redirect?: string; 101 | props?: { 102 | [key: string]: 103 | | string 104 | | KratosError 105 | | KratosForm 106 | | KratosIdentity 107 | | KratosLogout; 108 | }; 109 | } 110 | --------------------------------------------------------------------------------