(ugly) fix login redirect doesnt work
Rust / Run tests (push) Successful in 1m45s Details

This commit is contained in:
DataHearth 2023-08-16 13:10:52 +02:00
parent 0f317b5256
commit 0466b438e7
No known key found for this signature in database
GPG Key ID: E88FD356ACC5F3C4
11 changed files with 139 additions and 176 deletions

View File

@ -5,15 +5,13 @@ declare global {
// interface Error {}
interface Locals {
session: boolean;
}
interface PageData {
user?: {
id: string;
username: string;
avatar: string;
};
}
// interface PageData {}
// interface Platform {}
}
}

View File

@ -1,50 +1,50 @@
import refresh from '$lib/refreshToken';
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
const protectedRoutes = ['/'];
const auth: Handle = async ({ resolve, event }) => {
console.log(`handle auth for route: ${event.url.pathname}`);
const handleAuth: Handle = async ({ resolve, event }) => {
const refreshToken = event.cookies.get('refresh-token');
let accessToken = event.cookies.get('access-token');
const accessToken = event.cookies.get('access-token');
console.log(`refresh token: ${refreshToken}`);
console.log(`access token: ${accessToken}`);
if (refreshToken && !accessToken) await refresh(event);
if (!accessToken && refreshToken) {
const rsp = await event.fetch('/auth/discord', {
method: 'PUT',
return await resolve(event);
};
const handleUserSession: Handle = async ({ resolve, event }) => {
if (event.cookies.get('access-token') && event.cookies.get('refresh-token')) {
const rsp = await event.fetch('https://discord.com/api/v10/users/@me', {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ refreshToken })
Authorization: `Bearer ${event.cookies.get('access-token')}`
}
});
if (!rsp.ok) throw redirect(302, '/login');
if (!rsp.ok) console.error(`failed to get user session: ${rsp.status} ${await rsp.text()}`);
accessToken = event.cookies.get('access-token');
const { id, username, avatar } = await rsp.json();
event.locals.user = {
id,
username,
avatar: `https://cdn.discordapp.com/avatars/${id}/${avatar}.png`
};
}
// * grab the access token again, in case it was just refreshed
event.locals.session = !!(event.cookies.get('access-token') && refreshToken);
return await resolve(event);
};
const guard: Handle = async ({ resolve, event }) => {
if (protectedRoutes.includes(event.url.pathname) && !event.locals.session) {
console.warn(`authentication failed for: ${event.url.pathname}`);
const handleGuard: Handle = async ({ resolve, event }) => {
if (!event.locals.user && protectedRoutes.includes(event.url.pathname))
throw redirect(302, '/login');
} else if (
(event.url.pathname === '/login' || event.url.pathname.includes('/auth')) &&
event.locals.session
) {
console.log('already authenticated. redirecting to home page');
else if (
(event.locals.user && event.url.pathname === '/login') ||
(event.locals.user && event.url.pathname.includes('/auth'))
)
throw redirect(302, '/');
}
return await resolve(event);
};
export const handle: Handle = sequence(auth, guard);
export const handle: Handle = sequence(handleAuth, handleUserSession, handleGuard);

View File

@ -0,0 +1,34 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import { redirect, type RequestEvent } from '@sveltejs/kit';
import type { OAuth2Response } from './types';
export default async (event: RequestEvent) => {
const refreshToken = event.cookies.get('refresh-token');
if (!refreshToken) throw redirect(302, '/login');
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: refreshToken
})
});
if (!rsp.ok) throw redirect(302, '/login');
const { access_token, expires_in }: OAuth2Response = await rsp.json();
event.cookies.set('access-token', access_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
});
};

View File

@ -1,28 +1,5 @@
import { toast } from '$lib/toast';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals: { session }, cookies }) => {
if (session) {
const rsp = await fetch('https://discord.com/api/v10/users/@me', {
headers: {
Authorization: `Bearer ${cookies.get('access-token')}`
}
});
if (!rsp.ok) {
console.log("failed to fetch user's information");
toast({ type: 'error', message: "failed to fetch user's information" });
return {};
}
const { id, username, avatar } = await rsp.json();
return {
id,
username,
avatar: `https://cdn.discordapp.com/avatars/${id}/${avatar}.png`
};
}
return {};
export const load: LayoutServerLoad = async ({ locals: { user } }) => {
return { user };
};

View File

@ -8,7 +8,7 @@
export let data: PageData;
$: ({ username, avatar, id } = data);
$: ({ user } = data);
setContextClient(
new Client({
@ -27,13 +27,13 @@
</div>
<div class="flex-1 justify-end space-x-5 mr-5">
{#if id && avatar && username}
{#if user}
<div class="tooltip tooltip-bottom tooltip-info" data-tip="Copy ID">
<button
class="btn btn-ghost p-0 normal-case"
on:click={() => navigator.clipboard.writeText(id)}
on:click={() => navigator.clipboard.writeText(user.id)}
>
{username}
{user.username}
</button>
</div>
<div class="tooltip tooltip-bottom tooltip-info" data-tip="Logout">
@ -43,7 +43,7 @@
</div>
<div class="avatar">
<div class="w-8 rounded-full">
<img src={avatar} alt="user's avatar" />
<img src={user.avatar} alt="user's avatar" />
</div>
</div>
{/if}

View File

@ -1,62 +0,0 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import type { OAuth2Response } from '$lib/types';
import { redirect } from '@sveltejs/kit';
import { serialize } from 'cookie';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async (event) => {
const code = event.url.searchParams.get('code');
if (!code) {
console.error(`failed to get code in callback url: ${event.url}`);
throw redirect(302, '/login');
}
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'authorization_code',
redirect_uri: `${publicEnv.PUBLIC_ORIGIN}/auth/callback/discord`,
code
})
});
if (!rsp.ok) throw redirect(302, '/login');
const { access_token, refresh_token, expires_in }: OAuth2Response = await rsp.json();
const headers = new Headers();
headers.set('Location', '/');
headers.append(
'Set-Cookie',
serialize('access-token', access_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
})
);
headers.append(
'Set-Cookie',
serialize('refresh-token', refresh_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: 60 * 60 * 24 * 7,
expires: new Date(Date.now() + 60 * 60 * 24 * 7),
httpOnly: true,
sameSite: true,
path: '/'
})
);
return new Response(null, {
status: 302,
headers
});
};

View File

@ -1,61 +1,14 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import type { OAuth2Response } from '$lib/types';
import { serialize } from 'cookie';
import type { RequestHandler } from './$types';
import { redirect, type RequestHandler } from '@sveltejs/kit';
export const GET: RequestHandler = () => {
export const GET: RequestHandler = async () => {
const params = new URLSearchParams({
client_id: env.CLIENT_ID,
redirect_uri: `${publicEnv.PUBLIC_ORIGIN}/auth/callback/discord`,
redirect_uri: `${publicEnv.PUBLIC_ORIGIN}/auth/discord/callback`,
response_type: 'code',
scope: 'identify'
});
return new Response(null, {
headers: {
Location: `https://discord.com/api/oauth2/authorize?${params.toString()}`
},
status: 302
});
};
export const PUT: RequestHandler = async ({ request, fetch }) => {
const { refreshToken } = await request.json();
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: refreshToken
})
});
if (!rsp.ok) return new Response(null, { status: 401 });
const { access_token, expires_in }: OAuth2Response = await rsp.json();
const headers = new Headers();
headers.set('Location', '/');
headers.append(
'Set-Cookie',
serialize('access-token', access_token, {
domain: publicEnv.PUBLIC_ORIGIN,
maxAge: expires_in,
expires: new Date(Date.now() + expires_in),
httpOnly: true,
sameSite: true,
path: '/'
})
);
return new Response(null, {
status: 302,
headers
});
throw redirect(302, `https://discord.com/api/oauth2/authorize?${params.toString()}`);
};

View File

@ -0,0 +1,47 @@
import { env } from '$env/dynamic/private';
import { env as publicEnv } from '$env/dynamic/public';
import type { OAuth2Response } from '$lib/types';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ url, cookies }) => {
const code = url.searchParams.get('code');
if (!code) {
console.error(`failed to get code in callback url: ${url}`);
return { ok: false, uri: '/login', status: 302 };
}
const rsp = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: env.CLIENT_ID,
client_secret: env.CLIENT_SECRET,
grant_type: 'authorization_code',
redirect_uri: `${publicEnv.PUBLIC_ORIGIN}/auth/discord/callback`,
code
})
});
if (!rsp.ok) return { ok: false, uri: '/login', status: 302 };
const { access_token, refresh_token, expires_in }: OAuth2Response = await rsp.json();
cookies.set('access-token', access_token, {
maxAge: expires_in,
httpOnly: true,
sameSite: true,
path: '/',
secure: process.env.NODE_ENV === 'production'
});
cookies.set('refresh-token', refresh_token, {
maxAge: expires_in * 2,
httpOnly: true,
sameSite: true,
path: '/',
secure: process.env.NODE_ENV === 'production'
});
return { ok: true, uri: '/', status: 302 };
};

View File

@ -0,0 +1,11 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import type { PageData } from './$types';
export let data: PageData;
onMount(() => {
goto(data.uri, { invalidateAll: true, replaceState: false });
});
</script>

View File

@ -0,0 +1,5 @@
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ data: { uri } }) => {
return { uri };
};

View File

@ -1,9 +1,9 @@
import { redirect } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async (event) => {
event.cookies.delete('access-token');
event.cookies.delete('refresh-token');
export const GET: RequestHandler = async ({ cookies }) => {
cookies.set('access-token', '', { maxAge: -1 });
cookies.set('refresh-token', '', { maxAge: -1 });
throw redirect(302, '/login');
};