diff --git a/classquiz/routers/users.py b/classquiz/routers/users.py index 03c5ce7..d5b3f99 100644 --- a/classquiz/routers/users.py +++ b/classquiz/routers/users.py @@ -5,6 +5,7 @@ from email_validator import validate_email, EmailNotValidError from classquiz.auth import * from fastapi.background import BackgroundTasks from classquiz.emails import send_mail +from classquiz.config import redis from fastapi.responses import JSONResponse from classquiz.db.models import * @@ -54,13 +55,15 @@ async def login_for_cookie_access_token(request: Request, response: Response, access_token = create_access_token( data={"sub": user.email}, expires_delta=access_token_expires ) - rememberme_token = create_access_token( + await redis.set(access_token, user.email, ex=settings.access_token_expire_minutes * 60) + + rememberme_jwt = create_access_token( data={"sub": user.email}, expires_delta=timedelta(days=360)) response.set_cookie(key="access_token", value=f"Bearer {access_token}", httponly=True, samesite='strict') response.set_cookie(key="expiry", value="", max_age=settings.access_token_expire_minutes * 60) response.set_cookie(key="rememberme", value="") - response.set_cookie(key="rememberme_token", value=rememberme_token, httponly=True, samesite='strict') + response.set_cookie(key="rememberme_token", value=rememberme_jwt, httponly=True, samesite='strict') return {"access_token": access_token, "token_type": "bearer"} @@ -88,3 +91,8 @@ async def logout(request: Request, response: Response): response.delete_cookie("expiry") response.delete_cookie("rememberme") response.delete_cookie("rememberme_token") + + +@router.get("/check") +async def check_token(user: User = Depends(get_current_user)): + return {"email": user.email} diff --git a/frontend/package.json b/frontend/package.json index 74a1071..6507d6b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,8 +25,10 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^3.2.1", "felte": "^1.0.1", + "ioredis": "^4.28.5", "js-cookie": "^3.0.1", "luxon": "^2.3.1", + "node-fetch": "^3.2.0", "postcss": "^8.4.5", "postcss-load-config": "^3.1.1", "prettier": "^2.5.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 19d86e5..65f3512 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -14,8 +14,10 @@ specifiers: eslint-config-prettier: ^8.3.0 eslint-plugin-svelte3: ^3.2.1 felte: ^1.0.1 + ioredis: ^4.28.5 js-cookie: ^3.0.1 luxon: ^2.3.1 + node-fetch: ^3.2.0 postcss: ^8.4.5 postcss-load-config: ^3.1.1 prettier: ^2.5.1 @@ -43,8 +45,10 @@ devDependencies: eslint-config-prettier: 8.4.0_eslint@7.32.0 eslint-plugin-svelte3: 3.4.1_eslint@7.32.0+svelte@3.46.4 felte: 1.0.1_svelte@3.46.4 + ioredis: 4.28.5 js-cookie: 3.0.1 luxon: 2.3.1 + node-fetch: 3.2.0 postcss: 8.4.7 postcss-load-config: 3.1.3 prettier: 2.5.1 @@ -614,6 +618,11 @@ packages: fsevents: 2.3.2 dev: true + /cluster-key-slot/1.1.0: + resolution: {integrity: sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==} + engines: {node: '>=0.10.0'} + dev: true + /color-convert/1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -670,6 +679,11 @@ packages: hasBin: true dev: true + /data-uri-to-buffer/4.0.0: + resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==} + engines: {node: '>= 12'} + dev: true + /debug/4.3.3: resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} engines: {node: '>=6.0'} @@ -690,6 +704,11 @@ packages: resolution: {integrity: sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=} dev: true + /denque/1.5.1: + resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==} + engines: {node: '>=0.10'} + dev: true + /detect-indent/6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -1186,6 +1205,14 @@ packages: svelte: 3.46.4 dev: true + /fetch-blob/3.1.4: + resolution: {integrity: sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==} + engines: {node: ^12.20 || >= 14.13} + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.2.0 + dev: true + /file-entry-cache/6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1212,6 +1239,13 @@ packages: resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==} dev: true + /formdata-polyfill/4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + dependencies: + fetch-blob: 3.1.4 + dev: true + /fraction.js/4.1.3: resolution: {integrity: sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==} dev: true @@ -1347,6 +1381,25 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /ioredis/4.28.5: + resolution: {integrity: sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==} + engines: {node: '>=6'} + dependencies: + cluster-key-slot: 1.1.0 + debug: 4.3.3 + denque: 1.5.1 + lodash.defaults: 4.2.0 + lodash.flatten: 4.4.0 + lodash.isarguments: 3.1.0 + p-map: 2.1.0 + redis-commands: 1.7.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /is-arrayish/0.2.1: resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} dev: true @@ -1449,6 +1502,18 @@ packages: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: true + /lodash.defaults/4.2.0: + resolution: {integrity: sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=} + dev: true + + /lodash.flatten/4.4.0: + resolution: {integrity: sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=} + dev: true + + /lodash.isarguments/3.1.0: + resolution: {integrity: sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=} + dev: true + /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -1537,6 +1602,20 @@ packages: resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} dev: true + /node-domexception/1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + dev: true + + /node-fetch/3.2.0: + resolution: {integrity: sha512-8xeimMwMItMw8hRrOl3C9/xzU49HV/yE6ORew/l+dxWimO5A4Ra8ld2rerlJvc/O7et5Z1zrWsPX43v1QBjCxw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + data-uri-to-buffer: 4.0.0 + fetch-blob: 3.1.4 + formdata-polyfill: 4.0.10 + dev: true + /node-releases/2.0.2: resolution: {integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==} dev: true @@ -1574,6 +1653,11 @@ packages: word-wrap: 1.2.3 dev: true + /p-map/2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + dev: true + /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1732,6 +1816,22 @@ packages: picomatch: 2.3.1 dev: true + /redis-commands/1.7.0: + resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==} + dev: true + + /redis-errors/1.2.0: + resolution: {integrity: sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=} + engines: {node: '>=4'} + dev: true + + /redis-parser/3.0.0: + resolution: {integrity: sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: true + /regenerator-runtime/0.13.9: resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} dev: true @@ -1897,6 +1997,10 @@ packages: resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} dev: true + /standard-as-callback/2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: true + /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2179,6 +2283,11 @@ packages: fsevents: 2.3.2 dev: true + /web-streams-polyfill/3.2.0: + resolution: {integrity: sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==} + engines: {node: '>= 8'} + dev: true + /which/2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts index 95885f2..6ac06b1 100644 --- a/frontend/src/app.d.ts +++ b/frontend/src/app.d.ts @@ -5,7 +5,13 @@ declare namespace App { // interface Locals {} // interface Platform {} - // interface Session {} + interface Session { + authenticated: boolean; + token: string | null; + email: string | null; + + } + // interface Stuff {} } diff --git a/frontend/src/hooks.ts b/frontend/src/hooks.ts new file mode 100644 index 0000000..4288638 --- /dev/null +++ b/frontend/src/hooks.ts @@ -0,0 +1,57 @@ +import * as cookie from 'cookie'; +import fetch from 'node-fetch'; +import Redis from 'ioredis'; + + +const redis = new Redis(process.env.REDIS_URL); + +/** @type {import('@sveltejs/kit').Handle} */ +export async function handle({ event, resolve }) { + const cookies = cookie.parse(event.request.headers.get('cookie') || ''); + const regex_token = /^Bearer (.*)$/gm.exec(cookies.access_token); + if (regex_token === null) { + event.locals.token = null; + } else { + event.locals.token = regex_token[1]; + } + event.locals.rememberme = cookies.rememberme_token; + + return await resolve(event); +} + +/** @type {import('@sveltejs/kit').GetSession} */ +export async function getSession(event) { + const redis_res = await redis.get(event.locals.token); + let user_email: string; + if (redis_res === null) { + const res = await fetch(`${process.env.API_URL}/api/v1/users/check`, { + headers: { + Cookie: `access_token=Bearer ${event.locals.token}` + } + }); + if (res.ok) { + const json = await res.json(); + // @ts-ignore + user_email = json.email; + } else { + user_email = null; + } + + } else { + user_email = redis_res; + } + if (user_email === null) { + return { + authenticated: false, + token: event.locals.token, + email: null + }; + } else { + return { + authenticated: true, + token: event.locals.token, + email: user_email + }; + } + +} \ No newline at end of file diff --git a/frontend/src/routes/account/login.svelte b/frontend/src/routes/account/login.svelte index e924d16..bad3229 100644 --- a/frontend/src/routes/account/login.svelte +++ b/frontend/src/routes/account/login.svelte @@ -1,15 +1,14 @@ - + \ No newline at end of file diff --git a/frontend/src/routes/play.svelte b/frontend/src/routes/play.svelte new file mode 100644 index 0000000..e5cd0fe --- /dev/null +++ b/frontend/src/routes/play.svelte @@ -0,0 +1,56 @@ + + + + +