From 3e80d8636869dd9b5c68f1184f321dac0b7f14d6 Mon Sep 17 00:00:00 2001 From: steel <mael.acier@ensiie.fr> Date: Mon, 5 Feb 2024 16:03:09 +0100 Subject: [PATCH] 2.0: Lucia --- package.json | 6 +- pnpm-lock.yaml | 579 ++++++++++++++++++++++++++++- src/auth.ts | 10 + src/hooks.server.ts | 3 + src/lib/cookies.ts | 58 --- src/lib/default_lucia.ts | 65 ++++ src/lib/handlers/cookie.ts | 32 -- src/lib/handlers/index.ts | 35 -- src/lib/handlers/login.ts | 30 -- src/lib/handlers/loginCallback.ts | 53 --- src/lib/handlers/logout.ts | 19 - src/lib/handlers/logoutCallback.ts | 15 - src/lib/helpers.ts | 48 +++ src/lib/index.ts | 250 +++++++++++-- src/lib/lucia.ts | 65 ++++ src/lib/paths.ts | 5 - src/lib/public.ts | 5 - src/lib/types.ts | 70 ++-- src/lib/utils/jwt_cookie.ts | 77 ---- src/routes/+layout.server.ts | 5 + src/routes/+page.server.ts | 10 + src/routes/+page.svelte | 9 + src/routes/login/+page.server.ts | 8 + src/routes/login/+page.svelte | 6 + static/favicon.png | Bin 0 -> 1571 bytes 25 files changed, 1041 insertions(+), 422 deletions(-) create mode 100644 src/auth.ts create mode 100644 src/hooks.server.ts delete mode 100644 src/lib/cookies.ts create mode 100644 src/lib/default_lucia.ts delete mode 100644 src/lib/handlers/cookie.ts delete mode 100644 src/lib/handlers/index.ts delete mode 100644 src/lib/handlers/login.ts delete mode 100644 src/lib/handlers/loginCallback.ts delete mode 100644 src/lib/handlers/logout.ts delete mode 100644 src/lib/handlers/logoutCallback.ts create mode 100644 src/lib/helpers.ts create mode 100644 src/lib/lucia.ts delete mode 100644 src/lib/paths.ts delete mode 100644 src/lib/public.ts delete mode 100644 src/lib/utils/jwt_cookie.ts create mode 100644 src/routes/+layout.server.ts create mode 100644 src/routes/+page.server.ts create mode 100644 src/routes/+page.svelte create mode 100644 src/routes/login/+page.server.ts create mode 100644 src/routes/login/+page.svelte create mode 100644 static/favicon.png diff --git a/package.json b/package.json index 97742d7..216e58b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arise/aidc-sveltekit", - "version": "0.1.1", + "version": "0.2.0", "type": "module", "scripts": { "dev": "vite dev", @@ -39,6 +39,7 @@ "@sveltejs/kit": "^2.0.0", "@sveltejs/package": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@types/better-sqlite3": "^7.6.9", "@types/cookie": "^0.6.0", "@types/jsonwebtoken": "^9.0.5", "@typescript-eslint/eslint-plugin": "^6.14.0", @@ -57,9 +58,12 @@ "vitest": "^1.0.0" }, "dependencies": { + "@lucia-auth/adapter-sqlite": "^3.0.0", "assert": "^2.1.0", + "better-sqlite3": "^9.4.0", "cookie": "^0.6.0", "jsonwebtoken": "^9.0.2", + "lucia": "^3.0.1", "openid-client": "^5.6.1", "readable-http-codes": "^1.1.1", "zod": "^3.22.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc91ec1..d5e388c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,15 +5,24 @@ settings: excludeLinksFromLockfile: false dependencies: + '@lucia-auth/adapter-sqlite': + specifier: ^3.0.0 + version: 3.0.0(better-sqlite3@9.4.0)(lucia@3.0.1) assert: specifier: ^2.1.0 version: 2.1.0 + better-sqlite3: + specifier: ^9.4.0 + version: 9.4.0 cookie: specifier: ^0.6.0 version: 0.6.0 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 + lucia: + specifier: ^3.0.1 + version: 3.0.1 openid-client: specifier: ^5.6.1 version: 5.6.1 @@ -34,6 +43,9 @@ devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^3.0.0 version: 3.0.1(svelte@4.2.8)(vite@5.0.9) + '@types/better-sqlite3': + specifier: ^7.6.9 + version: 7.6.9 '@types/cookie': specifier: ^0.6.0 version: 0.6.0 @@ -98,6 +110,22 @@ packages: '@jridgewell/trace-mapping': 0.3.20 dev: true + /@emnapi/core@0.45.0: + resolution: {integrity: sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + + /@emnapi/runtime@0.45.0: + resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + /@esbuild/android-arm64@0.19.9: resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==} engines: {node: '>=12'} @@ -390,6 +418,326 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@lucia-auth/adapter-sqlite@3.0.0(better-sqlite3@9.4.0)(lucia@3.0.1): + resolution: {integrity: sha512-7XMfMMNziFOoqIq2u9W5Z+wo7G7lzNQfUh7wDlkIqKxJMSLtP6GKH+QpK+21X4ZY9dIDmiuiwqZmS3Q3CgpATw==} + peerDependencies: + '@libsql/client': ^0.3.0 + better-sqlite3: 8.x - 9.x + lucia: 3.x + peerDependenciesMeta: + '@libsql/client': + optional: true + better-sqlite3: + optional: true + dependencies: + better-sqlite3: 9.4.0 + lucia: 3.0.1 + dev: false + + /@napi-rs/wasm-runtime@0.1.1: + resolution: {integrity: sha512-ATj9ua659JgrkICjJscaeZdmPr44cb/KFjNWuD0N6pux0SpzaM7+iOuuK11mAnQM2N9q0DT4REu6NkL8ZEhopw==} + requiresBuild: true + dependencies: + '@emnapi/core': 0.45.0 + '@emnapi/runtime': 0.45.0 + '@tybys/wasm-util': 0.8.1 + dev: false + optional: true + + /@node-rs/argon2-android-arm-eabi@1.7.2: + resolution: {integrity: sha512-WhW84XOzdR4AOGc4BJvIg5lCRVBL0pXp/PPCe8QCyWw493p7VdNCdYpr2xdtjS/0zImmY85HNB/6zpzjLRTT/A==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-android-arm64@1.7.2: + resolution: {integrity: sha512-CdtayHSMIyDuVhSYFirwA757c4foQuyTjpysgFJLHweP9C7uDiBf9WBYij+UyabpaCadJ0wPyK6Vakinvlk4/g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-darwin-arm64@1.7.2: + resolution: {integrity: sha512-hUOhtgYHTEyzX5sgMZVdXunONOus2HWpWydF5D/RYJ1mZ76FXRnFpQE40DqbzisdPIraKdn40m7JqkPP7wqdyg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-darwin-x64@1.7.2: + resolution: {integrity: sha512-lfs5HX+t542yUfcv6Aa/NeGD1nUCwyQNgnPEGcik71Ow6V13hkR1bHgmT1u3CHN4fBts0gW+DQEDsq1xlVgkvw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-freebsd-x64@1.7.2: + resolution: {integrity: sha512-ROoF+4VaCBJUjddrTN1hjuqSl89ppRcjVXJscSPJjWzTlbzFmGGovJvIzUBmCr/Oq3yM1zKHj6MP9oRD5cB+/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-linux-arm-gnueabihf@1.7.2: + resolution: {integrity: sha512-CBSB8KPI8LS74Bcz3dYaa2/khULutz4vSDvFWUERlSLX+mPdDhoZi6UPuUPPF9e01w8AbiK1YCqlLUTm3tIMfw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-linux-arm64-gnu@1.7.2: + resolution: {integrity: sha512-6LBTug6ZiWFakP3X3Nqs7ZTM03gmcSWX4YvEn20HhhQE5NDrsrw3zNqGj0cJiNzKKIMSDDuj7uGy+ITEfNo4CA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-linux-arm64-musl@1.7.2: + resolution: {integrity: sha512-KjhQ+ZPne29t9VRVeIif7JdKwQba+tM6CBNYBoJB1iON0CUKeqSQtZcHuTj9gkf2SNRG5bsU4ABcfxd0OKsKHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-linux-x64-gnu@1.7.2: + resolution: {integrity: sha512-BQvp+iLtKqomHz4q5t1aKoni9osgvUDU5sZtHAlFm5dRTlGHnympcQVATRE5GHyH9C6MIM9W7P1kqEeCLGPolQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-linux-x64-musl@1.7.2: + resolution: {integrity: sha512-yXJudpBZQ98g+lWaHn9EzZ5KsAyqRdlpub/K+5NP7gHehb8wzBRIFAejIHAG0fvzQEEc86VOnV2koWIVZxWAvw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-wasm32-wasi@1.7.2: + resolution: {integrity: sha512-diXlVjJZY2GIV8ZDwUqXPhacXsFR0klGSv5D9f+XidwWXK4udtzDhkM/7N/Mb7h1HAWaxZ6IN9spYFjvWH1wqg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@napi-rs/wasm-runtime': 0.1.1 + dev: false + optional: true + + /@node-rs/argon2-win32-arm64-msvc@1.7.2: + resolution: {integrity: sha512-dhIBrY04P9nbmwzBpgERQDmmSu4YBZyeEE32t4TikMz5rQ07iaVC+JpGmtCBZoDIsLDHGC8cikENd3YEqpqIcA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-win32-ia32-msvc@1.7.2: + resolution: {integrity: sha512-o1tfqr8gyALCzuxBoQfvhxkeYMaw/0H8Gmt7klTYyEIBvEFu7SD5qytXO9Px7t5420nZL/Wy5cflg3IB1s57Pg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2-win32-x64-msvc@1.7.2: + resolution: {integrity: sha512-v0h53XUc7hNgWiWi0qcMcHvj9/kwuItI9NwLK4C+gtzT3UB0cedhfIL8HFMKThMXasy41ZdbpCF2Bi0kJoLNEg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/argon2@1.7.2: + resolution: {integrity: sha512-+H6pc3M1vIX9YnG59YW7prHhhpv19P8YyxlXHnnFzTimf2q+kKDF7mGWbhvN9STqIY+P70Patn0Q6qb6Ib5/4g==} + engines: {node: '>= 10'} + optionalDependencies: + '@node-rs/argon2-android-arm-eabi': 1.7.2 + '@node-rs/argon2-android-arm64': 1.7.2 + '@node-rs/argon2-darwin-arm64': 1.7.2 + '@node-rs/argon2-darwin-x64': 1.7.2 + '@node-rs/argon2-freebsd-x64': 1.7.2 + '@node-rs/argon2-linux-arm-gnueabihf': 1.7.2 + '@node-rs/argon2-linux-arm64-gnu': 1.7.2 + '@node-rs/argon2-linux-arm64-musl': 1.7.2 + '@node-rs/argon2-linux-x64-gnu': 1.7.2 + '@node-rs/argon2-linux-x64-musl': 1.7.2 + '@node-rs/argon2-wasm32-wasi': 1.7.2 + '@node-rs/argon2-win32-arm64-msvc': 1.7.2 + '@node-rs/argon2-win32-ia32-msvc': 1.7.2 + '@node-rs/argon2-win32-x64-msvc': 1.7.2 + dev: false + + /@node-rs/bcrypt-android-arm-eabi@1.9.2: + resolution: {integrity: sha512-er/Q2khwpan9pczvTTqY/DJE4UU65u31xd0NkZlHUTKyB7djRhWfzoGexGx2GN+k831/RR3U8kKE/8QUHeO3hQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-android-arm64@1.9.2: + resolution: {integrity: sha512-OUYatOEG5vbLbF73q2TC8UqrDO81zUQxnaFD/OAB1hcm6J+ur0zJ8E53c35/DIqkTp7JarPMraC4rouJ2ugN4w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-darwin-arm64@1.9.2: + resolution: {integrity: sha512-svJKsGbzMAxOB5oluOYneN4YkKUy26WSMgm3KOIhgoX30IeMilj+2jFN/5qrI0oDZ0Iczb3XyL5DuZFtEkdP8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-darwin-x64@1.9.2: + resolution: {integrity: sha512-9OrySjBi/rWix8NZWD/TrNbNcwMY0pAiMHdL09aJnJ07uPih83GGh1pq4UHCYFCMy7iTX8swOmDlGBUImkOZbg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-freebsd-x64@1.9.2: + resolution: {integrity: sha512-/djXV71RO6g5L1mI2pVvmp3x3pH7G4uKI3ODG1JBIXoz334oOcCMh40sB0uq0ljP8WEadker01p4T1rJE98fpg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-linux-arm-gnueabihf@1.9.2: + resolution: {integrity: sha512-F7wP950OTAooxEleUN4I2hqryGZK7hi1cSgRF13Wvbc597RFux35KiSxIXUA3mNt2DE7lV2PeceEtCOScaThWQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-linux-arm64-gnu@1.9.2: + resolution: {integrity: sha512-MehG+yQ0TgKMgKR1rO4hdvHkVsTM91Cof8qI9EJlS5+7+QSwfFA5O0zGwCkISD7bsyauJ5uJgcByGjpEobAHOg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-linux-arm64-musl@1.9.2: + resolution: {integrity: sha512-PRZTAJjOwKEGsIhmBvfNh81So+wGl4QyCFAt23j+KwBujLStjC0N3YaqtTlWVKG9tcriPtmMYiAQtXWIyIgg/w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-linux-x64-gnu@1.9.2: + resolution: {integrity: sha512-5WfGO+O1m7nJ55WZ8XDq+ItA98Z4O7sNWsR+1nIj9YGT+Tx5zkQ2RBhpK6oCWZMluuZ0eKQ0FDmyP6K+2NDRIA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-linux-x64-musl@1.9.2: + resolution: {integrity: sha512-VjCn0388p6PMCVUYHgYmHZrKNc7WwNJRr2WLJsHbQRGDOKbpNL6YolCjQxUchcSPDhzwrq1cIdy4j0fpoXEsdw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-wasm32-wasi@1.9.2: + resolution: {integrity: sha512-P06aHfMzm9makwU+nM7WA65yQnS1xuqJ8l/6I/LvXjnl+lfB3DtJ2B0CSLtjnUGpUgcHbWl5gEbNnTPxSAirjQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@napi-rs/wasm-runtime': 0.1.1 + dev: false + optional: true + + /@node-rs/bcrypt-win32-arm64-msvc@1.9.2: + resolution: {integrity: sha512-Iyo/Q5/eNw27VRd3mLBgh1b9b5fnT3QHTVwxv3Siv/MRAIfJXH/cTOe18qSwYQzNh0ZioW4yemFPYCWSZi7szA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-win32-ia32-msvc@1.9.2: + resolution: {integrity: sha512-6LHWMaPylyyHoS5863YpxAACVB8DWCxro5W6pQ4h8WKSgHpJp8Um9jphTdN0A2w45HZjUnfcFuiFFC+TbftjCw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt-win32-x64-msvc@1.9.2: + resolution: {integrity: sha512-vZ9T1MOaYkLO9FTyl28YX0SYJneiYTKNFgM8PUv8nas8xrD+7OzokA0fEtlNp6413T7IKSD/iG9qi8nTWsiyGg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/bcrypt@1.9.2: + resolution: {integrity: sha512-FKUo9iCSIti+ldwoOlY1ztyIFhZxEgT7jZ/UCt/9bg1rLmNdbQQD2JKIMImDCqmTWuLPY4ZF4Q5MyOMIfDCd8Q==} + engines: {node: '>= 10'} + optionalDependencies: + '@node-rs/bcrypt-android-arm-eabi': 1.9.2 + '@node-rs/bcrypt-android-arm64': 1.9.2 + '@node-rs/bcrypt-darwin-arm64': 1.9.2 + '@node-rs/bcrypt-darwin-x64': 1.9.2 + '@node-rs/bcrypt-freebsd-x64': 1.9.2 + '@node-rs/bcrypt-linux-arm-gnueabihf': 1.9.2 + '@node-rs/bcrypt-linux-arm64-gnu': 1.9.2 + '@node-rs/bcrypt-linux-arm64-musl': 1.9.2 + '@node-rs/bcrypt-linux-x64-gnu': 1.9.2 + '@node-rs/bcrypt-linux-x64-musl': 1.9.2 + '@node-rs/bcrypt-wasm32-wasi': 1.9.2 + '@node-rs/bcrypt-win32-arm64-msvc': 1.9.2 + '@node-rs/bcrypt-win32-ia32-msvc': 1.9.2 + '@node-rs/bcrypt-win32-x64-msvc': 1.9.2 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -602,6 +950,20 @@ packages: - supports-color dev: true + /@tybys/wasm-util@0.8.1: + resolution: {integrity: sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + + /@types/better-sqlite3@7.6.9: + resolution: {integrity: sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ==} + dependencies: + '@types/node': 20.10.4 + dev: true + /@types/cookie@0.6.0: resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} dev: true @@ -904,11 +1266,37 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + + /better-sqlite3@9.4.0: + resolution: {integrity: sha512-5kynxekMxSjCMiFyUBLHggFcJkCmiZi6fUkiGz/B5GZOvdRWQJD0klqCx5/Y+bm2AKP7I/DHbSFx26AxjruWNg==} + requiresBuild: true + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.1 + dev: false + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: true + /bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + dependencies: + file-uri-to-path: 1.0.0 + dev: false + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -937,6 +1325,13 @@ packages: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} dev: false + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -997,6 +1392,10 @@ packages: fsevents: 2.3.3 dev: true + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: false + /code-red@1.0.4: resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} dependencies: @@ -1061,6 +1460,13 @@ packages: ms: 2.1.2 dev: true + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + /dedent-js@1.0.1: resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} dev: true @@ -1072,6 +1478,11 @@ packages: type-detect: 4.0.8 dev: true + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -1109,6 +1520,11 @@ packages: engines: {node: '>=8'} dev: true + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + dev: false + /devalue@4.3.2: resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} dev: true @@ -1138,6 +1554,12 @@ packages: safe-buffer: 5.2.1 dev: false + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + /es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} dev: true @@ -1342,6 +1764,11 @@ packages: strip-final-newline: 3.0.0 dev: true + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: false + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -1378,6 +1805,10 @@ packages: flat-cache: 3.2.0 dev: true + /file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + dev: false + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -1412,6 +1843,10 @@ packages: is-callable: 1.2.7 dev: false + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -1446,6 +1881,10 @@ packages: engines: {node: '>=16'} dev: true + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: false + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1563,6 +2002,10 @@ packages: engines: {node: '>=16.17.0'} dev: true + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false + /ignore-walk@5.0.1: resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -1598,6 +2041,10 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -1832,6 +2279,12 @@ packages: dependencies: yallist: 4.0.0 + /lucia@3.0.1: + resolution: {integrity: sha512-srwUkTCGgr6N4mFpaKZVZy5kwiRZdsrbIDv9Wrjar+xyw1MjojYQQ7oRbegjRWOZ3yI8xOOclK3sz/rga2J7/w==} + dependencies: + oslo: 1.0.1 + dev: false + /magic-string@0.27.0: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} @@ -1872,6 +2325,11 @@ packages: engines: {node: '>=12'} dev: true + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -1892,7 +2350,10 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true + + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} @@ -1929,6 +2390,10 @@ packages: hasBin: true dev: true + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + dev: false + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -1940,6 +2405,13 @@ packages: tslib: 2.6.2 dev: true + /node-abi@3.54.0: + resolution: {integrity: sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: false + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2012,7 +2484,6 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} @@ -2042,6 +2513,13 @@ packages: type-check: 0.4.0 dev: true + /oslo@1.0.1: + resolution: {integrity: sha512-esfzZry+HfGgK/GCYkg7BRlLd3RH5aHa08wgLJPYjENXybi0BvXxGk0LbUj+lXfz2TkjPDHe4rB/o6JxRLHxBg==} + dependencies: + '@node-rs/argon2': 1.7.2 + '@node-rs/bcrypt': 1.9.2 + dev: false + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2187,6 +2665,25 @@ packages: source-map-js: 1.0.2 dev: true + /prebuild-install@7.1.1: + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.54.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2227,6 +2724,13 @@ packages: sade: 1.8.1 dev: true + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2236,6 +2740,16 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true @@ -2244,6 +2758,15 @@ packages: resolution: {integrity: sha512-M+u7iokHWtqEXmeeLuPztJrWnS6kMvqDWEg/Ho7i1hqVv64ROJLqf0U0PHhwQJo9Q7lynj25sbDG8hljdLEfHg==} dev: false + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2364,6 +2887,18 @@ packages: engines: {node: '>=14'} dev: true + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: false + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + /sirv@2.0.3: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} @@ -2401,6 +2936,12 @@ packages: resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==} dev: true + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2420,6 +2961,11 @@ packages: min-indent: 1.0.1 dev: true + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2570,6 +3116,26 @@ packages: periscopic: 3.1.0 dev: true + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -2618,7 +3184,12 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - dev: true + + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} @@ -2659,7 +3230,6 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true /util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} @@ -2824,7 +3394,6 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..2a345be --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,10 @@ +import { AIDC_CLIENT_ID, AIDC_CLIENT_SECRET } from "$env/static/private"; +import { AriseIdConnect } from "$lib/index.js"; +import { defaultLucia } from "$lib/default_lucia.js"; + +export const aidc = await AriseIdConnect.init({ + client_id: AIDC_CLIENT_ID, + client_secret: AIDC_CLIENT_SECRET, + scope: "openid offline profile", + luciaWrapper: defaultLucia, +}); diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..6104748 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,3 @@ +import { aidc } from "./auth.js"; + +export const handle = aidc.handler(); diff --git a/src/lib/cookies.ts b/src/lib/cookies.ts deleted file mode 100644 index 5a30671..0000000 --- a/src/lib/cookies.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { JWTCookieManager } from "$lib/utils/jwt_cookie.js"; -import type { Cookies } from "@sveltejs/kit"; -import type { TokenSet } from "openid-client"; -import { z } from "zod"; -import type { InternalConfig, Config } from "$lib/types.js"; - -const ONE_HOUR = 60 * 60; -const ONE_DAY = 24 * ONE_HOUR; -const ONE_MONTH = 30 * ONE_DAY; - -export const authSchema = z.object({ - codeVerifier: z.string(), - state: z.string(), - nonce: z.string(), -}); - -export type AuthCookie = JWTCookieManager<typeof authSchema>; - -export function authCookieBuilder({ jwt_secret }: Config): AuthCookie { - return new JWTCookieManager(authSchema, jwt_secret, { - serialize: { path: "/", maxAge: ONE_HOUR }, - name: "tmp_arise_auth_secrets", - }); -} - -export const tokenSetSchema = z.object({ - access_token: z.string().optional(), - token_type: z.string().optional(), - id_token: z.string().optional(), - refresh_token: z.string().optional(), - scope: z.string().optional(), - expires_at: z.number().optional(), - session_state: z.string().optional(), -}); - -export type TokenSetCookie = JWTCookieManager<typeof tokenSetSchema>; - -export function tokenSetCookieBuilder({ jwt_secret }: Config): TokenSetCookie { - return new JWTCookieManager(tokenSetSchema, jwt_secret, { - name: "arise_token_set", - serialize: { - maxAge: ONE_MONTH, - }, - }); -} - -export async function setTokenSetCookie( - tokenSet: TokenSet, - cookies: Cookies, - config: InternalConfig, -) { - const userinfo = await config.client.userinfo(tokenSet); - - config.cookies.tokenSet.send(cookies, { - payload: tokenSet, - jwt: { subject: userinfo.sub, expiresIn: ONE_MONTH }, - }); -} diff --git a/src/lib/default_lucia.ts b/src/lib/default_lucia.ts new file mode 100644 index 0000000..1ffaf41 --- /dev/null +++ b/src/lib/default_lucia.ts @@ -0,0 +1,65 @@ +import { BetterSqlite3Adapter } from "@lucia-auth/adapter-sqlite"; +import sqlite from "better-sqlite3"; +import type { Database as SqLiteConnection } from "better-sqlite3"; +import { LuciaWrapper } from "./lucia.js"; + +class DefaultLucia extends LuciaWrapper<DatabaseUser> { + db: SqLiteConnection; + + constructor() { + let db = sqlite(":memory:"); + super( + new BetterSqlite3Adapter(db, { + user: "user", + session: "session", + }), + ); + this.db = db; + } + + initDatabase() { + this.db.exec(`CREATE TABLE IF NOT EXISTS user ( + id TEXT NOT NULL PRIMARY KEY, + subject TEXT NOT NULL UNIQUE + )`); + + this.db.exec(`CREATE TABLE IF NOT EXISTS session ( + id TEXT NOT NULL PRIMARY KEY, + expires_at INTEGER NOT NULL, + user_id TEXT NOT NULL, + id_token TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES user(id) + )`); + } + + getUser(subject: string) { + return this.db + .prepare("SELECT * FROM user WHERE subject = ?") + .get(subject) as DatabaseUser | undefined; + } + + createUser(subject: string, userId: string) { + this.db + .prepare("INSERT INTO user (id, subject) VALUES (?, ?)") + .run(userId, subject); + } +} + +export const defaultLucia = new DefaultLucia(); + +declare module "lucia" { + interface Register { + Lucia: typeof defaultLucia.lucia; + DatabaseUserAttributes: Omit<DatabaseUser, "id">; + DatabaseSessionAttributes: Omit<DatabaseSession, "id">; + } +} + +export interface DatabaseUser { + id: string; + subject: string; +} +export interface DatabaseSession { + id: string; + id_token: string; +} diff --git a/src/lib/handlers/cookie.ts b/src/lib/handlers/cookie.ts deleted file mode 100644 index c30e239..0000000 --- a/src/lib/handlers/cookie.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { TokenSet } from "openid-client"; -import { setTokenSetCookie } from "$lib/cookies.js"; -import type { Handle } from "@sveltejs/kit"; -import type { InternalConfig } from "$lib/types.js"; - -export default function (config: InternalConfig): Handle { - return async function ({ event, resolve }) { - const jwtTokenSet = config.cookies.tokenSet.receive(event.cookies); - - if (jwtTokenSet !== null) { - let tokenSet = new TokenSet(jwtTokenSet); - - try { - if (tokenSet.expired()) { - tokenSet = await config.client.refresh(tokenSet); - - await setTokenSetCookie(tokenSet, event.cookies, config); - } - - event.locals.arise.loggedIn = { - user: { id: tokenSet.claims().sub }, - tokenSet, - }; - } catch (error) { - console.error(error); - config.cookies.tokenSet.delete(event.cookies); - } - } - - return resolve(event); - }; -} diff --git a/src/lib/handlers/index.ts b/src/lib/handlers/index.ts deleted file mode 100644 index 2a93eca..0000000 --- a/src/lib/handlers/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { sequence } from "@sveltejs/kit/hooks"; -import cookieHandler from "./cookie.js"; -import loginHandler from "./login.js"; -import logoutHandler from "./logout.js"; -import loginCallbackHandler from "./loginCallback.js"; -import logoutCallbackHandler from "./logoutCallback.js"; -import type { Handle } from "@sveltejs/kit"; -import type { InternalConfig } from "$lib/types.js"; - -const init: Handle = ({ event, resolve }) => { - event.locals.arise = {}; - return resolve(event); -}; - -function route(pathname: string, handler: Handle): Handle { - return ({ event, resolve }) => { - if (event.url.pathname === pathname) { - return handler({ event, resolve }); - } - - return resolve(event); - }; -} - -export default function (config: InternalConfig): Handle { - const { paths } = config; - return sequence( - init, - cookieHandler(config), - route(paths.login, loginHandler(config)), - route(paths.logout, logoutHandler(config)), - route(paths.callback, loginCallbackHandler(config)), - route(paths.logoutCallback, logoutCallbackHandler(config)), - ); -} diff --git a/src/lib/handlers/login.ts b/src/lib/handlers/login.ts deleted file mode 100644 index f2e906a..0000000 --- a/src/lib/handlers/login.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import { generators } from "openid-client"; -import type { Handle } from "@sveltejs/kit"; -import type { InternalConfig } from "$lib/types.js"; -import { SEE_OTHER } from "readable-http-codes"; - -export default function (config: InternalConfig): Handle { - return async function ({ event }) { - const codeVerifier = generators.codeVerifier(); - const state = generators.state(); - const nonce = generators.nonce(); - - config.cookies.auth.send(event.cookies, { - payload: { codeVerifier, state, nonce }, - }); - - const redirectURI = new URL(config.paths.callback, event.url).toString(); - const authorizationUrl = config.client.authorizationUrl({ - response_type: "code", - scope: config.scope, - code_challenge: generators.codeChallenge(codeVerifier), - code_challenge_method: "S256", - redirect_uri: redirectURI, - state, - nonce, - }); - - redirect(SEE_OTHER, authorizationUrl); - }; -} diff --git a/src/lib/handlers/loginCallback.ts b/src/lib/handlers/loginCallback.ts deleted file mode 100644 index a7db2a0..0000000 --- a/src/lib/handlers/loginCallback.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { type Handle, redirect } from "@sveltejs/kit"; -import { setTokenSetCookie } from "$lib/cookies.js"; -import { errors } from "openid-client"; -import type { InternalConfig } from "$lib/types.js"; -import { base } from "$app/paths"; -import { SEE_OTHER } from "readable-http-codes"; - -export default function (config: InternalConfig): Handle { - return async function ({ event, resolve }) { - const params = config.client.callbackParams(event.url.toString()); - - const cookie = config.cookies.auth.receive(event.cookies); - - if (cookie === null) { - redirect(SEE_OTHER, config.paths.login); - } - - try { - const redirectURI = new URL(config.paths.callback, event.url).toString(); - const tokenSet = await config.client.callback(redirectURI, params, { - code_verifier: cookie.codeVerifier, - state: cookie.state, - nonce: cookie.nonce, - }); - - await setTokenSetCookie(tokenSet, event.cookies, config); - } catch (error) { - if (error instanceof errors.OPError) { - if (error.error === "access_denied") { - if (config.on?.accessDenied) { - return config.on.accessDenied(event, error); - } - - redirect(SEE_OTHER, `${base}/`); - } - - event.locals.arise.error = error; - return resolve(event); - } else if (error instanceof errors.RPError) { - event.locals.arise.error = error; - return resolve(event); - } - - throw error; - } - - if (config.on?.loggedIn) { - return config.on.loggedIn(event); - } - - redirect(SEE_OTHER, `${base}/`); - }; -} diff --git a/src/lib/handlers/logout.ts b/src/lib/handlers/logout.ts deleted file mode 100644 index 6a18280..0000000 --- a/src/lib/handlers/logout.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { Handle } from "@sveltejs/kit"; -import type { InternalConfig } from "$lib/types.js"; -import { SEE_OTHER } from "readable-http-codes"; - -export default function (config: InternalConfig): Handle { - return async function ({ event }) { - const postLogoutRedirectUTI = new URL( - config.paths.logoutCallback, - event.url, - ).toString(); - const endSessionUrl = config.client.endSessionUrl({ - post_logout_redirect_uri: postLogoutRedirectUTI, - id_token_hint: event.locals.arise.loggedIn?.tokenSet, - }); - - redirect(SEE_OTHER, endSessionUrl); - }; -} diff --git a/src/lib/handlers/logoutCallback.ts b/src/lib/handlers/logoutCallback.ts deleted file mode 100644 index f8b4e05..0000000 --- a/src/lib/handlers/logoutCallback.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { type Handle, redirect } from "@sveltejs/kit"; -import type { InternalConfig } from "$lib/types.js"; -import { SEE_OTHER } from "readable-http-codes"; - -export default function (config: InternalConfig): Handle { - return async function ({ event }) { - config.cookies.tokenSet.delete(event.cookies); - - if (config.on?.loggedOut) { - return config.on.loggedOut(event); - } - - redirect(SEE_OTHER, "/"); - }; -} diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts new file mode 100644 index 0000000..868a3a2 --- /dev/null +++ b/src/lib/helpers.ts @@ -0,0 +1,48 @@ +import { base } from "$app/paths"; +import type { Handle, RequestEvent } from "@sveltejs/kit"; +import type { Cookie as LuciaCookie } from "lucia"; + +export function setLuciaCookie(event: RequestEvent, cookie: LuciaCookie) { + event.cookies.set(cookie.name, cookie.value, { + path: ".", + ...cookie.attributes, + }); +} + +export function setTempCookie( + event: RequestEvent, + name: string, + value: string, +) { + event.cookies.set(name, value, { + path: "/", + secure: import.meta.env.PROD, + httpOnly: true, + maxAge: 60 * 10, + sameSite: "lax", + }); +} + +export function route(pathname: string, handler: Handle): Handle { + return ({ event, resolve }) => { + if (event.url.pathname === pathname) { + return handler({ event, resolve }); + } + + return resolve(event); + }; +} + +export function method(method: string, handler: Handle): Handle { + return ({ event, resolve }) => { + if (event.request.method === method) { + return handler({ event, resolve }); + } + + return resolve(event); + }; +} + +export function addBasePath(path: string): string { + return `${base}${path}`; +} diff --git a/src/lib/index.ts b/src/lib/index.ts index 8ea634a..e991be7 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,46 +1,216 @@ -import { Issuer } from "openid-client"; -import { base } from "$app/paths"; -import handler from "./handlers/index.js"; -import type { Config, InternalConfig } from "./types.js"; -import * as paths from "./paths.js"; -import { authCookieBuilder, tokenSetCookieBuilder } from "./cookies.js"; - -export { handleBuilder as ariseIdConnectBuilder } from "./index.js"; -export * from "./public.js"; - -function addBase(path: string): string { - return `${base}${path}`; -} +import { redirect, type Handle, error, type RequestEvent } from "@sveltejs/kit"; +import { sequence } from "@sveltejs/kit/hooks"; +import { Lucia, generateId } from "lucia"; +import type { Client, TokenSet } from "openid-client"; +import { Issuer, errors, generators } from "openid-client"; +import { SEE_OTHER } from "readable-http-codes"; +import { + method, + route, + setLuciaCookie, + setTempCookie, + addBasePath, +} from "./helpers.js"; +import type { Config, CookieNames, Paths } from "./types.js"; +import type { DatabaseUser } from "./lucia.js"; + +export class AriseIdConnect<DbUser extends DatabaseUser> { + readonly client: Client; + readonly paths: Paths; + protected cookieNames: CookieNames; + + constructor( + readonly config: Config<DbUser>, + issuer: Issuer, + ) { + this.client = new issuer.Client({ + ...config, + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code", "id_token"], + }); + + this.cookieNames = { + oauthState: config.cookieNames?.oauthState ?? "aidc_state", + oauthCodeVerifier: + config.cookieNames?.oauthCodeVerifier ?? "aidc_code_verifier", + }; + + this.paths = { + home: addBasePath(config.paths?.home ?? "/"), + callback: addBasePath(config.paths?.callback ?? "/auth/callback"), + logoutCallback: addBasePath(config.paths?.logoutCallback ?? "/"), + login: addBasePath(config.paths?.login ?? "/auth/login"), + logout: addBasePath(config.paths?.logout ?? "/auth/logout"), + }; + } + + static async init<DbUser extends DatabaseUser>( + config: Config<DbUser>, + ): Promise<AriseIdConnect<DbUser>> { + const issuer = await Issuer.discover( + config.issuer || + "https://oidc.iiens.net/.well-known/openid-configuration", + ); + const aidc = new AriseIdConnect(config, issuer); + + await aidc.config.luciaWrapper.initDatabase(); + return aidc; + } + + protected async getTokens(event: RequestEvent): Promise<TokenSet> { + const state = event.cookies.get(this.cookieNames.oauthState) ?? null; + const codeVerifier = + event.cookies.get(this.cookieNames.oauthCodeVerifier) ?? null; + const params = this.client.callbackParams(event.url.toString()); + + if (!codeVerifier || !state) { + redirect(SEE_OTHER, this.paths.login); + } + + const redirectURI = new URL(this.paths.callback, event.url).toString(); + try { + const tokenSet = await this.client.callback(redirectURI, params, { + code_verifier: codeVerifier, + state: state, + }); + + return tokenSet; + } catch (err) { + if (err instanceof errors.OPError) { + if (err.error === "access_denied") { + if (this.config.on?.accessDenied) { + return this.config.on.accessDenied(event, err); + } + + redirect(SEE_OTHER, this.paths.home); + } + + error(500, err); + } else if (err instanceof errors.RPError) { + error(500, err); + } + + throw err; + } + } + + protected loginHandler: Handle = ({ event }) => { + const codeVerifier = generators.codeVerifier(); + const state = generators.state(); + // const nonce = generators.nonce(); + + const redirectURI = new URL(this.paths.callback, event.url).toString(); + const authorizationUrl = this.client.authorizationUrl({ + response_type: "code", + scope: this.config.scope, + code_challenge: generators.codeChallenge(codeVerifier), + code_challenge_method: "S256", + redirect_uri: redirectURI, + state, + // nonce, + }); + + setTempCookie(event, this.cookieNames.oauthState, state); + setTempCookie(event, this.cookieNames.oauthCodeVerifier, codeVerifier); + + redirect(SEE_OTHER, authorizationUrl); + }; + + protected logoutHandler: Handle = async ({ event }) => { + if (!event.locals.session) { + redirect(SEE_OTHER, this.paths.home); + } + + const { lucia } = this.config.luciaWrapper; + + const { session } = await lucia.validateSession(event.locals.session.id); -export async function handleBuilder(config: Config) { - const issuer = await Issuer.discover( - config.issuer || "https://oidc.iiens.net/.well-known/openid-configuration", - ); - - const client = new issuer.Client({ - ...config, - grant_types: ["authorization_code", "refresh_token"], - response_types: ["code", "id_token"], - }); - - const flowPaths: InternalConfig["paths"] = { - callback: addBase(config.paths?.callback ?? paths.CALLBACK), - logoutCallback: addBase( - config.paths?.logoutCallback ?? paths.LOGOUT_CALLBACK, - ), - login: addBase(config.paths?.login ?? paths.LOGIN), - logout: addBase(config.paths?.logout ?? paths.LOGOUT), + const postLogoutRedirectURI = new URL(this.paths.logoutCallback, event.url); + const endSessionUrl = this.client.endSessionUrl({ + post_logout_redirect_uri: postLogoutRedirectURI.toString(), + id_token_hint: session?.id_token, + }); + + if (this.config.on?.logout) { + await this.config.on.logout(event); + } + + await lucia.invalidateSession(event.locals.session.id); + + setLuciaCookie(event, lucia.createBlankSessionCookie()); + + redirect(SEE_OTHER, endSessionUrl); + }; + + protected callbackHandler: Handle = async ({ event }) => { + const tokenSet = await this.getTokens(event); + + if (!tokenSet.id_token) { + throw new Error("No id_token in tokenSet"); + } + + const subject = tokenSet.claims().sub; + + const { luciaWrapper } = this.config; + const { lucia } = luciaWrapper; + + const existingUser = await luciaWrapper.getUser(subject); + + const userId = existingUser ? existingUser.id : generateId(15); + + if (!existingUser) { + await luciaWrapper.createUser(subject, userId); + } + + const session = await lucia.createSession(userId, { + id_token: tokenSet.id_token, + }); + setLuciaCookie(event, lucia.createSessionCookie(session.id)); + + if (this.config.on?.login) { + return this.config.on.login(event, tokenSet.claims()); + } + + redirect(SEE_OTHER, this.paths.home); }; - const cookies: InternalConfig["cookies"] = { - auth: authCookieBuilder(config), - tokenSet: tokenSetCookieBuilder(config), + protected sessionHandler: Handle = async ({ event, resolve }) => { + const { lucia } = this.config.luciaWrapper; + + const sessionId = event.cookies.get(lucia.sessionCookieName); + if (!sessionId) { + event.locals.user = null; + event.locals.session = null; + return resolve(event); + } + + const { session, user } = await lucia.validateSession(sessionId); + + if (session && session.fresh) { + setLuciaCookie(event, lucia.createSessionCookie(session.id)); + } + if (!session) { + setLuciaCookie(event, lucia.createBlankSessionCookie()); + } + + event.locals.user = user; + event.locals.session = session; + return resolve(event); + }; + + protected miscHandler: Handle = ({ event, resolve }) => { + event.locals.authPaths = this.paths; + + return resolve(event); }; - return handler({ - ...config, - paths: flowPaths, - client, - cookies, - }); + handler(): Handle { + return sequence( + this.miscHandler.bind(this), + this.sessionHandler.bind(this), + route(this.paths.login, this.loginHandler.bind(this)), + route(this.paths.logout, method("POST", this.logoutHandler.bind(this))), + route(this.paths.callback, this.callbackHandler.bind(this)), + ); + } } diff --git a/src/lib/lucia.ts b/src/lib/lucia.ts new file mode 100644 index 0000000..a52426c --- /dev/null +++ b/src/lib/lucia.ts @@ -0,0 +1,65 @@ +import { dev } from "$app/environment"; +import type { MaybePromise } from "@sveltejs/kit"; +import type { + Adapter, + RegisteredDatabaseSessionAttributes, + RegisteredDatabaseUserAttributes, + SessionCookieOptions, + TimeSpan, +} from "lucia"; +import { Lucia } from "lucia"; + +export interface DatabaseUser { + id: string; + subject: string; +} +export interface DatabaseSession { + id: string; + id_token: string; +} + +export abstract class LuciaWrapper< + DbUser extends DatabaseUser, + _SessionAttributes extends { id_token: string } = { id_token: string }, + _UserAttributes extends { subject: string } = { subject: string }, +> { + lucia: Lucia<_SessionAttributes, _UserAttributes>; + + constructor( + adapter: Adapter, + options?: { + sessionExpiresIn?: TimeSpan; + sessionCookie?: SessionCookieOptions; + getSessionAttributes?: ( + databaseSessionAttributes: RegisteredDatabaseSessionAttributes, + ) => _SessionAttributes; + getUserAttributes?: ( + databaseUserAttributes: RegisteredDatabaseUserAttributes, + ) => _UserAttributes; + }, + ) { + this.lucia = new Lucia(adapter, { + sessionCookie: { + attributes: { + secure: !dev, + }, + name: "aidc_session", + }, + getUserAttributes(attributes) { + return { + subject: attributes.subject, + }; + }, + getSessionAttributes(attributes) { + return { + id_token: attributes.id_token, + }; + }, + ...options, + }); + } + + abstract initDatabase(): MaybePromise<void>; + abstract getUser(subject: string): MaybePromise<DbUser | undefined>; + abstract createUser(subject: string, userId: string): MaybePromise<void>; +} diff --git a/src/lib/paths.ts b/src/lib/paths.ts deleted file mode 100644 index 1b28a50..0000000 --- a/src/lib/paths.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const BASE = "/auth"; -export const CALLBACK = `${BASE}/callback`; -export const LOGOUT_CALLBACK = `${BASE}/callback/logout`; -export const LOGIN = `${BASE}/login`; -export const LOGOUT = `${BASE}/logout`; diff --git a/src/lib/public.ts b/src/lib/public.ts deleted file mode 100644 index b9f22af..0000000 --- a/src/lib/public.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * as openid_client from "openid-client"; -export * as cookie from "cookie"; -export * as jsonwebtoken from "jsonwebtoken"; -export type { AuthData as AriseData, Config } from "./types.js"; -export * as authPaths from "./paths.js"; diff --git a/src/lib/types.ts b/src/lib/types.ts index 0995ebf..73ffbb8 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,60 +1,36 @@ import type { MaybePromise, RequestEvent } from "@sveltejs/kit"; -import type { - BaseClient, - ClientMetadata, - errors, - TokenSet, -} from "openid-client"; -import type { AuthCookie, TokenSetCookie } from "./cookies.js"; +import type { ClientMetadata, UserinfoResponse, errors } from "openid-client"; +import type { DatabaseUser, LuciaWrapper } from "./lucia.js"; -export type AuthData = { - loggedIn?: { - user: { - id: string; - }; - tokenSet: TokenSet; +export interface Config<DbUser extends DatabaseUser> extends ClientMetadata { + client_secret: string; + scope: string; + issuer?: string; + on?: { + accessDenied?: (event: RequestEvent, error: errors.OPError) => never; + login?: (event: RequestEvent, userInfo: UserinfoResponse) => never; + logout?: (event: RequestEvent) => MaybePromise<void>; }; - error?: errors.OPError | errors.RPError; -}; - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace App { - interface Locals { - arise: AuthData; - } - } + luciaWrapper: LuciaWrapper<DbUser>; + cookieNames?: Partial<CookieNames>; + paths?: Partial<Paths>; } -type Paths = { +export type Paths = { + home: string; callback: string; logoutCallback: string; login: string; logout: string; }; -export interface Config extends ClientMetadata { - client_secret: string; - scope: string; - /** JWT Secret */ - jwt_secret: string; - paths?: Partial<Paths>; - issuer?: string; - on?: { - accessDenied?: ( - event: RequestEvent, - error: errors.OPError, - ) => MaybePromise<Response>; - loggedIn?: (event: RequestEvent) => MaybePromise<Response>; - loggedOut?: (event: RequestEvent) => MaybePromise<Response>; - }; -} +export type CookieNames = { + oauthState: string; + oauthCodeVerifier: string; +}; -export interface InternalConfig extends Config { - client: BaseClient; - paths: Paths; - cookies: { - auth: AuthCookie; - tokenSet: TokenSetCookie; - }; +export interface Locals { + user: import("lucia").User | null; + session: import("lucia").Session | null; + authPaths: Paths; } diff --git a/src/lib/utils/jwt_cookie.ts b/src/lib/utils/jwt_cookie.ts deleted file mode 100644 index 99dca0c..0000000 --- a/src/lib/utils/jwt_cookie.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { Cookies } from "@sveltejs/kit"; -import jwt from "jsonwebtoken"; -import type { z, ZodObject, ZodSchema } from "zod"; -import type { CookieSerializeOptions } from "cookie"; -import { dev } from "$app/environment"; - -type Empty = Record<string, never>; - -export type JWT<T extends ZodSchema = ZodObject<Empty>> = jwt.JwtPayload & - z.output<T>; - -export interface SendOptions<T> { - payload: T; - jwt?: jwt.SignOptions; -} - -export interface CookieOptions { - serialize?: CookieSerializeOptions; - name?: string; -} - -export class JWTCookieManager<T extends ZodSchema = ZodObject<Empty>> { - constructor( - private schema: T, - private secret: string, - private cookieOptions?: CookieOptions, - ) {} - - protected cookieName(): string { - return this.cookieOptions?.name ?? "flash"; - } - protected cookieSerializeOptions(): CookieSerializeOptions & { - path: string; - } { - return { - path: "/", - httpOnly: true, - secure: !dev, - sameSite: !dev, - ...this.cookieOptions?.serialize, - }; - } - - send(cookies: Cookies, options: SendOptions<z.output<T>>) { - const payload = this.schema.safeParse(options.payload); - - if (!payload.success) { - throw payload.error; - } - - const cookie = jwt.sign(payload.data, this.secret, options.jwt); - cookies.set(this.cookieName(), cookie, this.cookieSerializeOptions()); - } - - receive(cookies: Cookies): JWT<T> | null { - const token = cookies.get(this.cookieName()); - - if (!token) return null; - - try { - jwt.verify(token, this.secret); - } catch { - return null; - } - - const payload = jwt.decode(token, { json: true }); - if (payload) { - const { success } = this.schema.safeParse(payload); - return success ? (payload as JWT<T>) : null; - } - return null; - } - - delete(cookies: Cookies) { - cookies.delete(this.cookieName(), this.cookieSerializeOptions()); - } -} diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts new file mode 100644 index 0000000..0e7d544 --- /dev/null +++ b/src/routes/+layout.server.ts @@ -0,0 +1,5 @@ +export function load({ locals }) { + return { + authPaths: locals.authPaths, + }; +} diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts new file mode 100644 index 0000000..751ee18 --- /dev/null +++ b/src/routes/+page.server.ts @@ -0,0 +1,10 @@ +import { redirect } from "@sveltejs/kit"; + +export async function load(event) { + if (!event.locals.user) { + return redirect(302, "/login"); + } + return { + user: event.locals.user, + }; +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..95f9e13 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,9 @@ +<script lang="ts"> + export let data; +</script> + +<h1>Hi, {data.user.subject}!</h1> +<p>Your user ID is {data.user.id}.</p> +<form method="post" action={data.authPaths.logout}> + <button>Sign out</button> +</form> diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts new file mode 100644 index 0000000..c465c92 --- /dev/null +++ b/src/routes/login/+page.server.ts @@ -0,0 +1,8 @@ +import { redirect } from "@sveltejs/kit"; + +export async function load(event) { + if (event.locals.user) { + return redirect(302, "/"); + } + return {}; +} diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte new file mode 100644 index 0000000..ecf448c --- /dev/null +++ b/src/routes/login/+page.svelte @@ -0,0 +1,6 @@ +<script lang="ts"> + export let data; +</script> + +<h1>Sign in</h1> +<a href={data.authPaths.login}>Sign in with AriseID Connect</a> diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097 GIT binary patch literal 1571 zcmV+;2Hg3HP)<h;3K|Lk000e1NJLTq004jh004jp1ONa4X*a1r00001b5ch_0Itp) z=>Px)-AP12RCwC$UE6KzI1p6{F2N<Fgp}YCTtZ542`(WexCEDw|A=A)1A-t30wEX> z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zg<YDbY-gN-C@$NXr>j#$x=!~7LGqHW?IO8+*oE1MyDp!G=L<gz=E*n%PA*mnuaD-% zU>0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt7<V*0Nvi!iKPY@n`1~$se=LwHTOE7`*@+};uU_gT$1{Nib z9&~5~nc4J(EWez%_f0Ty7T;wB{7s*oNO=9p8(o}N*_V>86oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFN<oPlV?qzG@=jl`t@|E8zV8 zJ4}It4e%+dFPWw16u{sAX42jlFu{_e!AKZE75iV2&Q7>gpIod~R{>@#@5x9zJK<vg zI0+@80NMa{gtNoRhsR-w%piFI3d-5xrN)R}H!WtK=Gp%dC5(a`Q0V4_vS-mj<((Z~ zbV*PucDY#zFVglY>EHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsL<j(YStU%^TNkjEqg808>y-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+<zzg4N+!Mudx;=&$zSp?cjRmyF-+404b-P66DA17$<$H}= z7$P4)QXt>)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!<qA9l$G~6DRxiCy7Vo zOl^oK!S3XbayWz&p33NSw$lxR1fm)O*rCE0^ZNmk0wnax!!?%g599+O37!666~F(y z5fq?5T*Fm{^%YR^PiM%z#%x`fAC+;C&`X5JXN_40SieIEXoaUU=*}=c0OC7@a*rFE z3xr3yyFB~zRl(lNY&B@$Fia%81M!xeIuTYNz!Dz6eBKONjL<@dJdT$H?ShKl6$p=F ze!fdgzelJI&n`Hk9f}>BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`<ao$!3dCkTn3@aF0ny6VrToO6qA z-~&2=w&2nT&o5u>**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH<v&YL5FIcieFm_zQ2O VHuiSbmk|H}002ovPDHLkV1f%e^4kCa literal 0 HcmV?d00001 -- GitLab