✨ Cleand authentication-code up
This commit is contained in:
@@ -12,6 +12,9 @@ from fastapi.background import BackgroundTasks
|
||||
from fastapi.responses import JSONResponse, RedirectResponse, PlainTextResponse
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
import html
|
||||
|
||||
from jose import jwt, JWTError
|
||||
|
||||
from classquiz import oauth
|
||||
from classquiz.helpers.avatar import gzipped_user_avatar
|
||||
import base64
|
||||
@@ -266,3 +269,46 @@ async def get_other_avatar(respo: Response, user_id: uuid.UUID):
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
respo.headers.append("Content-Type", "image/svg+xml")
|
||||
return gzip.decompress(base64.b64decode(user.avatar))
|
||||
|
||||
|
||||
class InternalAuthData(BaseModel):
|
||||
rememberme: str
|
||||
jwt: str | None
|
||||
|
||||
|
||||
@router.post("/auth/internal")
|
||||
async def internal_auth(data: InternalAuthData, resp: Response):
|
||||
try:
|
||||
data.jwt = data.jwt.replace("Bearer ", "")
|
||||
except AttributeError:
|
||||
pass
|
||||
if data.jwt is not None:
|
||||
try:
|
||||
payload = jwt.decode(data.jwt, settings.secret_key, algorithms=["HS256"])
|
||||
email: str = payload.get("sub")
|
||||
if email is None:
|
||||
resp.status_code = 401
|
||||
return resp
|
||||
except JWTError:
|
||||
resp.status_code = 401
|
||||
return resp
|
||||
else:
|
||||
return await rememberme_check(data.rememberme, resp)
|
||||
|
||||
|
||||
class GetEmailFromJWT(BaseModel):
|
||||
jwt: str
|
||||
|
||||
|
||||
@router.post("/auth/internal/email")
|
||||
async def get_email_from_jwt(data: GetEmailFromJWT):
|
||||
try:
|
||||
data.jwt = data.jwt.replace("Bearer ", "")
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
payload = jwt.decode(data.jwt, settings.secret_key, algorithms=["HS256"])
|
||||
return payload.get("sub")
|
||||
except JWTError as e:
|
||||
print(e)
|
||||
raise HTTPException(status_code=401)
|
||||
|
||||
+1
-4
@@ -25,11 +25,8 @@ RUN corepack enable && corepack prepare pnpm@7.5.0 --activate && pnpm i
|
||||
# copy the generated modules and all other files to the container
|
||||
COPY . .
|
||||
|
||||
RUN apt update && \
|
||||
apt install -y redis-server
|
||||
|
||||
# build the application
|
||||
RUN nohup bash -c "redis-server &" && sleep 4 && pnpm run build
|
||||
RUN pnpm run build
|
||||
|
||||
### Serve Step
|
||||
# pull the Node.js Docker image
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
"felte": "^1.2.2",
|
||||
"highlight.js": "^11.5.1",
|
||||
"i18next-browser-languagedetector": "^6.1.4",
|
||||
"ioredis": "^5.1.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"luxon": "^2.4.0",
|
||||
"mapbox-gl": "^2.9.1",
|
||||
|
||||
Generated
-84
@@ -36,7 +36,6 @@ specifiers:
|
||||
highlight.js: ^11.5.1
|
||||
i18next: ^21.8.13
|
||||
i18next-browser-languagedetector: ^6.1.4
|
||||
ioredis: ^5.1.0
|
||||
js-cookie: ^3.0.1
|
||||
luxon: ^2.4.0
|
||||
mapbox-gl: ^2.9.1
|
||||
@@ -101,7 +100,6 @@ devDependencies:
|
||||
felte: 1.2.2_svelte@3.49.0
|
||||
highlight.js: 11.6.0
|
||||
i18next-browser-languagedetector: 6.1.4
|
||||
ioredis: 5.2.1
|
||||
js-cookie: 3.0.1
|
||||
luxon: 2.5.0
|
||||
mapbox-gl: 2.9.2
|
||||
@@ -251,13 +249,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@ioredis/commands/1.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@jridgewell/resolve-uri/3.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -1439,14 +1430,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/cluster-key-slot/1.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==
|
||||
}
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/color-convert/2.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -1724,14 +1707,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/denque/2.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
|
||||
}
|
||||
engines: { node: '>=0.10' }
|
||||
dev: true
|
||||
|
||||
/detect-indent/6.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2797,26 +2772,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/ioredis/5.2.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+rgPUIawiT+L3am9FbUcwjI7R7KtLDUSJNYWF82NZmlUXCZELws7iJkVDuECQoNUHqZTf/EGn3uw9IA+PlBiyg==
|
||||
}
|
||||
engines: { node: '>=12.22.0' }
|
||||
dependencies:
|
||||
'@ioredis/commands': 1.2.0
|
||||
cluster-key-slot: 1.1.0
|
||||
debug: 4.3.4
|
||||
denque: 2.1.0
|
||||
lodash.defaults: 4.2.0
|
||||
lodash.isarguments: 3.1.0
|
||||
redis-errors: 1.2.0
|
||||
redis-parser: 3.0.0
|
||||
standard-as-callback: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/is-binary-path/2.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2979,20 +2934,6 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/lodash.defaults/4.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/lodash.isarguments/3.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/lodash.isplainobject/4.0.6:
|
||||
resolution:
|
||||
{
|
||||
@@ -4088,24 +4029,6 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/redis-errors/1.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==
|
||||
}
|
||||
engines: { node: '>=4' }
|
||||
dev: true
|
||||
|
||||
/redis-parser/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==
|
||||
}
|
||||
engines: { node: '>=4' }
|
||||
dependencies:
|
||||
redis-errors: 1.2.0
|
||||
dev: true
|
||||
|
||||
/regenerator-runtime/0.13.9:
|
||||
resolution:
|
||||
{
|
||||
@@ -4418,13 +4341,6 @@ packages:
|
||||
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
|
||||
dev: true
|
||||
|
||||
/standard-as-callback/2.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/string-width/4.2.3:
|
||||
resolution:
|
||||
{
|
||||
|
||||
Vendored
+18
@@ -3,3 +3,21 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
declare namespace App {
|
||||
interface Locals {
|
||||
email: string | null;
|
||||
}
|
||||
|
||||
// interface Platform {}
|
||||
interface Session {
|
||||
authenticated: boolean;
|
||||
email: string | null;
|
||||
}
|
||||
|
||||
// interface Stuff {}
|
||||
}
|
||||
|
||||
+41
-44
@@ -5,56 +5,53 @@
|
||||
*/
|
||||
|
||||
import * as cookie from 'cookie';
|
||||
import Redis from 'ioredis';
|
||||
|
||||
const redis = new Redis(process.env.REDIS_URL);
|
||||
import type { Handle, GetSession } from '@sveltejs/kit';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Handle} */
|
||||
export async function handle({ event, resolve }) {
|
||||
export const handle: Handle = async ({ 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`, {
|
||||
const jwt = /^Bearer (.*)$/gm.exec(cookies.access_token);
|
||||
const rememberme_token = cookies.rememberme_token;
|
||||
if (rememberme_token) {
|
||||
const res = await fetch(`${process.env.API_URL}/api/v1/users/auth/internal`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Cookie: `access_token=Bearer ${event.locals.token}` // skipcq: JS-0378
|
||||
}
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
rememberme: rememberme_token,
|
||||
jwt: jwt === null ? undefined : jwt[0]
|
||||
})
|
||||
});
|
||||
if (res.ok) {
|
||||
const json = await res.json();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore // skipcq: JS-0295
|
||||
user_email = json.email;
|
||||
let new_jwt;
|
||||
if (jwt) {
|
||||
new_jwt = jwt[0];
|
||||
} else {
|
||||
user_email = null;
|
||||
new_jwt = cookie.parse(res.headers.get('set-cookie')).access_token;
|
||||
}
|
||||
event.locals.email = await (
|
||||
await fetch(`${process.env.API_URL}/api/v1/users/auth/internal/email`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jwt: new_jwt
|
||||
})
|
||||
})
|
||||
).text();
|
||||
const resp = await resolve(event);
|
||||
resp.headers.set('Set-Cookie', res.headers.get('set-cookie'));
|
||||
return resp;
|
||||
} else {
|
||||
user_email = redis_res;
|
||||
event.locals.email = null;
|
||||
return resolve(event);
|
||||
}
|
||||
if (user_email === null) {
|
||||
return {
|
||||
authenticated: false,
|
||||
token: event.locals.token,
|
||||
email: null
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
authenticated: true,
|
||||
token: event.locals.token,
|
||||
email: user_email
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getSession: GetSession = async (event) => {
|
||||
return {
|
||||
email: event.locals.email,
|
||||
authenticated: Boolean(event.locals.email)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<footer class="text-center text-white border-black w-screen">
|
||||
<div class="grid grid-cols-12 w-full pt-4 bg-gray-700 pb-2">
|
||||
<p class="col-start-2 col-end-12 text-center w-full">
|
||||
<p class="col-start-3 col-end-11 text-center w-full">
|
||||
Made with ❤️ by
|
||||
<a
|
||||
href="https://mawoka.eu?utm_source=ClassQuiz&utm_medium=footer"
|
||||
@@ -36,14 +36,16 @@
|
||||
>More details are here.</a
|
||||
>
|
||||
</p>
|
||||
<div class="flex flex-col items-center min-h-full h-full md:block hidden">
|
||||
<div
|
||||
class="flex flex-col items-center min-h-full h-full md:block hidden col-start-11 col-end-12"
|
||||
>
|
||||
<div class="my-auto mr-16">
|
||||
<LanguageToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:hidden block">
|
||||
<div class="self-end flex justify-center min-h-full h-full bg-gray-700 md:pb-2">
|
||||
<div class="self-end flex justify-center min-h-full h-full bg-gray-700 pb-2 md:pb-0">
|
||||
<div class="m-auto md:invisible visible">
|
||||
<LanguageToggle />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user