diff --git a/src/lib/server/oauth.ts b/src/lib/server/oauth.ts index 7d424aa..622c15d 100644 --- a/src/lib/server/oauth.ts +++ b/src/lib/server/oauth.ts @@ -1,4 +1,12 @@ -import { Google, Discord, GitHub, generateState, generateCodeVerifier } from 'arctic' +import { + Google, + Discord, + GitHub, + generateState, + generateCodeVerifier, + type GitHubTokens, + type DiscordTokens +} from 'arctic' import { error, redirect, type Actions, type RequestHandler, type ServerLoad } from '@sveltejs/kit' import { z } from 'zod' import { db, publicUser } from './db' @@ -6,8 +14,9 @@ import { env } from '$env/dynamic/private' import { dev } from '$app/environment' import { alphabet, generateRandomString } from 'oslo/crypto' import { decodeJwt } from 'jose' -import { superForm, type SuperForm } from 'sveltekit-superforms' +import { superValidate, type SuperForm } from 'sveltekit-superforms' import { zod, type ValidationAdapter } from 'sveltekit-superforms/adapters' +import { cookieController, createSessionForUser } from './auth' const { DISCORD_CLIENT_ID, @@ -158,24 +167,50 @@ export function oauth_callback(): ServerLoad< ) { error(400, 'Invalid request') } - let tokens - let id - let name + let id: string + let name: string if (provider instanceof Google) { - tokens = await provider.validateAuthorizationCode(code, storedCodeVerifier) + const tokens = await provider.validateAuthorizationCode(code, storedCodeVerifier) console.log(tokens.idToken) const { sub, name: Uname } = decodeJwt(tokens.idToken) - id = sub - name = Uname + id = sub! + name = Uname as string } else { - tokens = await provider.validateAuthorizationCode(code) + const tokens: GitHubTokens | DiscordTokens = await provider.validateAuthorizationCode(code) + if (providerID === 'discord') { + const response = await fetch('https://discord.com/api/users/@me', { + headers: { + Authorization: `Bearer ${tokens.accessToken}` + } + }) + if (!response.ok) error(500, 'Unable to contact provider') + const { id: userId, username } = await response.json() + id = userId + name = username + } else if (providerID === 'github') { + const response = await fetch('https://api.github.com/user', { + headers: { + Authorization: `Bearer ${tokens.accessToken}` + } + }) + if (!response.ok) error(500, 'Unable to contact provider') + const { id: userId, login } = await response.json() + id = userId + name = login + } } - const formToken = generateRandomString(12, alphabet('0-9', 'a-z')) - const form = superForm( - zod(z.object({ token: z.string() }), { - defaults: { token: formToken } + const existingUser = (await db.user.getOneBySecondaryIndex(`oauth_${providerID}_id`, id))?.flat() + if(existingUser) { + const session = await createSessionForUser(existingUser.id) + const sessionCookie = cookieController.createCookie(session.unwrap().id) + cookies.set(sessionCookie.name, sessionCookie.value, { + path: '.', + ...sessionCookie.attributes }) - ) + redirect(302, '/app') + } + const formToken = generateRandomString(12, alphabet('0-9', 'a-z')) + const form = await superValidate({ token: formToken }, zod(z.object({ token: z.string() }))) if (locals.user) { // the user is already logged in, ask them if they want to link the account to their existing account, or log out and try again await db.saved_oauth_data.set(formToken, { @@ -207,5 +242,12 @@ export function oauth_callback(): ServerLoad< } export function oauth_callback_actions(): Actions<{ provider: string }> { - return {} + return { + link: ({ request, locals, }) => { + // get existing account and link it + }, + create: ({ request }) => { + // create new account with + } + } } diff --git a/src/routes/oauth/[provider]/callback/+page.svelte b/src/routes/oauth/[provider]/callback/+page.svelte index 9eb807b..0ae594f 100644 --- a/src/routes/oauth/[provider]/callback/+page.svelte +++ b/src/routes/oauth/[provider]/callback/+page.svelte @@ -1,8 +1,18 @@ -{#if data.type === 'create'} - do you want to create a new account using {data.name} from {data.prov}? - -{:else if data.type === 'link'}{/if} +
+ {#if data.type === 'create'} + do you want to create a new account using {data.name} from {data.prov}? + no + {:else if data.type === 'link'} + do you want to link {data.name} from {data.prov} to your existing + account {data.user.displayName}? + no + Sign me out and try again + {/if} +