(ugly) fix login redirect doesnt work
Rust / Run tests (push) Successful in 1m45s
Details
Rust / Run tests (push) Successful in 1m45s
Details
This commit is contained in:
parent
0f317b5256
commit
0466b438e7
|
@ -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 {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: '/'
|
||||
});
|
||||
};
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
};
|
|
@ -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()}`);
|
||||
};
|
||||
|
|
|
@ -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 };
|
||||
};
|
|
@ -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>
|
|
@ -0,0 +1,5 @@
|
|||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ data: { uri } }) => {
|
||||
return { uri };
|
||||
};
|
|
@ -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');
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue