remove cookie auth from backend, add initial types to frontend
This commit is contained in:
parent
c6eaf49779
commit
aeb044d1a0
12 changed files with 101 additions and 21 deletions
|
@ -19,11 +19,7 @@ app = cors(
|
|||
)
|
||||
QuartSchema(app)
|
||||
|
||||
for bp in (
|
||||
blueprints.users_blueprint,
|
||||
blueprints.members_blueprint,
|
||||
blueprints.meta_blueprint,
|
||||
):
|
||||
for bp in blueprints.__all__:
|
||||
app.register_blueprint(bp)
|
||||
|
||||
|
||||
|
@ -50,11 +46,10 @@ async def handle_500(_):
|
|||
|
||||
@app.before_request
|
||||
async def get_user_from_token():
|
||||
"""Get the current user from a token given in the `Authorization` header or the `pronounscc-token` cookie.
|
||||
"""Get the current user from a token given in the `Authorization` header.
|
||||
If no token is set, does nothing; if an invalid token is set, raises an error."""
|
||||
token = request.headers.get("Authorization", None) or request.cookies.get(
|
||||
"pronounscc-token", None
|
||||
)
|
||||
|
||||
token = request.headers.get("Authorization", None)
|
||||
if not token:
|
||||
return
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from .v2.members import bp as members_blueprint
|
||||
from .v2.meta import bp as meta_blueprint
|
||||
from .v2.users import bp as users_blueprint
|
||||
from .v2.auth.discord import bp as discord_auth_blueprint
|
||||
|
||||
__all__ = [users_blueprint, members_blueprint, meta_blueprint]
|
||||
__all__ = [users_blueprint, members_blueprint, meta_blueprint, discord_auth_blueprint]
|
||||
|
|
|
@ -1,10 +1,29 @@
|
|||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from quart import Blueprint
|
||||
from quart_schema import validate_response
|
||||
|
||||
from foxnouns.settings import BASE_DOMAIN
|
||||
from foxnouns.models.user import SelfUserModel
|
||||
|
||||
|
||||
bp = Blueprint("auth_v2", __name__)
|
||||
|
||||
|
||||
class URLsResponse(BaseModel):
|
||||
discord: str | None = Field(default=None)
|
||||
google: str | None = Field(default=None)
|
||||
tumblr: str | None = Field(default=None)
|
||||
|
||||
|
||||
@bp.post("/api/v2/auth/urls", host=BASE_DOMAIN)
|
||||
@validate_response(URLsResponse, 200)
|
||||
async def urls():
|
||||
# TODO: build authorization URLs + callback URLs, store state in Redis
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class OAuthCallbackRequest(BaseModel):
|
||||
callback_domain: str
|
||||
code: str
|
||||
|
|
|
@ -5,7 +5,7 @@ from foxnouns.settings import BASE_DOMAIN
|
|||
|
||||
from . import BaseCallbackResponse, OAuthCallbackRequest
|
||||
|
||||
bp = Blueprint("discord", __name__)
|
||||
bp = Blueprint("discord_v2", __name__)
|
||||
|
||||
|
||||
@bp.post("/api/v2/auth/discord/callback", host=BASE_DOMAIN)
|
||||
|
|
5
foxnouns/db/redis.py
Normal file
5
foxnouns/db/redis.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from redis import asyncio as aioredis
|
||||
|
||||
from foxnouns.settings import REDIS_URL
|
||||
|
||||
redis = aioredis.from_url(REDIS_URL)
|
|
@ -15,7 +15,7 @@ with env.prefixed("DATABASE_"):
|
|||
}
|
||||
|
||||
# The Redis database used for Celery and ephemeral storage.
|
||||
REDIS_URL: str = env("REDIS_URL", "redis://localhost")
|
||||
REDIS_URL = env("REDIS_URL", "redis://localhost")
|
||||
|
||||
with env.prefixed("MINIO_"):
|
||||
MINIO = {
|
||||
|
@ -31,6 +31,8 @@ with env.prefixed("MINIO_"):
|
|||
BASE_DOMAIN = env("BASE_DOMAIN")
|
||||
# The base domain for short URLs.
|
||||
SHORT_DOMAIN = env("SHORT_DOMAIN", "prns.localhost")
|
||||
# The base URL used for the frontend. This will usually be the same as BASE_DOMAIN prefixed with https://.
|
||||
FRONTEND_BASE = env("FRONTEND_DOMAIN", f"https://{BASE_DOMAIN}")
|
||||
|
||||
# Secret key for signing tokens, generate with (for example) `openssl rand -base64 32`
|
||||
SECRET_KEY = env("SECRET_KEY")
|
||||
|
|
32
frontend/src/lib/entities.ts
Normal file
32
frontend/src/lib/entities.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
export type User = {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string | null;
|
||||
bio: string | null;
|
||||
avatar: string | null;
|
||||
|
||||
names: FieldEntry[];
|
||||
pronouns: PronounEntry[];
|
||||
fields: ProfileField[];
|
||||
};
|
||||
|
||||
export type FieldEntry = {
|
||||
value: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ProfileField = {
|
||||
name: string;
|
||||
entries: FieldEntry[];
|
||||
};
|
||||
|
||||
export type PronounEntry = {
|
||||
value: string;
|
||||
status: string;
|
||||
display: string | null;
|
||||
};
|
||||
|
||||
export type Meta = {
|
||||
users: number;
|
||||
members: number;
|
||||
};
|
|
@ -1,5 +1,8 @@
|
|||
import type { Cookies, ServerLoadEvent } from "@sveltejs/kit";
|
||||
|
||||
export type FetchOptions = {
|
||||
fetchFn?: typeof fetch;
|
||||
token?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data?: any;
|
||||
version?: number;
|
||||
|
@ -22,13 +25,17 @@ export default async function request<T>(
|
|||
path: string,
|
||||
opts: FetchOptions = {},
|
||||
): Promise<T> {
|
||||
const { data, version, extraHeaders } = opts;
|
||||
const { token, data, version, extraHeaders } = opts;
|
||||
const fetchFn = opts.fetchFn ?? fetch;
|
||||
|
||||
const resp = await fetchFn(`/api/v${version ?? 2}${path}`, {
|
||||
method,
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
headers: { ...extraHeaders, "Content-Type": "application/json" },
|
||||
headers: {
|
||||
...extraHeaders,
|
||||
...(token ? { Authorization: token } : {}),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status < 200 || resp.status >= 400) throw await resp.json();
|
||||
|
@ -50,14 +57,27 @@ export async function fastRequest(
|
|||
path: string,
|
||||
opts: FetchOptions = {},
|
||||
): Promise<void> {
|
||||
const { data, version, extraHeaders } = opts;
|
||||
const { token, data, version, extraHeaders } = opts;
|
||||
const fetchFn = opts.fetchFn ?? fetch;
|
||||
|
||||
const resp = await fetchFn(`/api/v2${version ?? 2}${path}`, {
|
||||
method,
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
headers: { ...extraHeaders, "Content-Type": "application/json" },
|
||||
headers: {
|
||||
...extraHeaders,
|
||||
...(token ? { Authorization: token } : {}),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status < 200 || resp.status >= 400) throw await resp.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a token from a request cookie.
|
||||
* Accepts both a cookie object ({ cookies }) or a request object (req).
|
||||
* @param s A Cookies or ServerLoadEvent object
|
||||
* @returns A token, or `undefined` if no token is set.
|
||||
*/
|
||||
export const getToken = (s: Cookies | ServerLoadEvent) =>
|
||||
"cookies" in s ? s.cookies.get("pronounscc-token") : s.get("pronounscc-token");
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import request from "$lib/request";
|
||||
import request, { getToken } from "$lib/request";
|
||||
import type { User, Meta } from "$lib/entities";
|
||||
|
||||
export async function load({ fetch, cookies }) {
|
||||
const meta = await request("GET", "/meta", { fetchFn: fetch });
|
||||
const meta = await request<Meta>("GET", "/meta", { fetchFn: fetch });
|
||||
|
||||
let user;
|
||||
if (cookies.get("pronounscc-token")) {
|
||||
user = await request("GET", "/users/@me", { fetchFn: fetch });
|
||||
user = await request<User>("GET", "/users/@me", { fetchFn: fetch, token: getToken(cookies) });
|
||||
}
|
||||
|
||||
return { meta, user };
|
||||
return { meta, user, token: getToken(cookies) };
|
||||
}
|
||||
|
|
|
@ -8,4 +8,8 @@
|
|||
|
||||
{JSON.stringify(data.meta)}
|
||||
|
||||
{#if data.user}
|
||||
{JSON.stringify(data.user)}
|
||||
{/if}
|
||||
|
||||
<slot />
|
||||
|
|
2
poetry.lock
generated
2
poetry.lock
generated
|
@ -1513,4 +1513,4 @@ h11 = ">=0.9.0,<1"
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "0f97996ff854041ec5bc98541863488c29ae65f44652721af67f3e06875c6bbb"
|
||||
content-hash = "3013b1d9175f34895133e708e35dea7d3401a07b5869c857e30d0aac41ce7add"
|
||||
|
|
|
@ -25,6 +25,7 @@ alembic = "^1.13.1"
|
|||
quart-cors = "^0.7.0"
|
||||
minio = "^7.2.5"
|
||||
pyvips = "^2.2.2"
|
||||
redis = "^5.0.3"
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
|
|
Loading…
Reference in a new issue