feat: link fediverse account to existing user
This commit is contained in:
		
							parent
							
								
									03209e4028
								
							
						
					
					
						commit
						57e1ec09c0
					
				
					 17 changed files with 335 additions and 95 deletions
				
			
		|  | @ -47,7 +47,8 @@ | |||
| 		"successful-link-fedi": "Your account has successfully been linked to the following fediverse account:", | ||||
| 		"successful-link-profile-hint": "You now can close this page, or go back to your profile:", | ||||
| 		"successful-link-profile-link": "Go to your profile", | ||||
| 		"remote-discord-account-label": "Your Discord account" | ||||
| 		"remote-discord-account-label": "Your Discord account", | ||||
| 		"log-in-with-fediverse-instance-placeholder": "Your instance (i.e. mastodon.social)" | ||||
| 	}, | ||||
| 	"error": { | ||||
| 		"bad-request-header": "Something was wrong with your input", | ||||
|  |  | |||
|  | @ -1,35 +1,62 @@ | |||
| import { apiRequest } from "$api"; | ||||
| import ApiError, { ErrorCode } from "$api/error"; | ||||
| import type { CallbackResponse } from "$api/models/auth.js"; | ||||
| import type { AddAccountResponse, CallbackResponse } from "$api/models/auth.js"; | ||||
| import { setToken } from "$lib"; | ||||
| import createRegisterAction from "$lib/actions/register.js"; | ||||
| import { redirect } from "@sveltejs/kit"; | ||||
| import log from "$lib/log"; | ||||
| import { isRedirect, redirect } from "@sveltejs/kit"; | ||||
| 
 | ||||
| export const load = async ({ parent, params, url, fetch, cookies }) => { | ||||
| 	const { meUser } = await parent(); | ||||
| 	if (meUser) redirect(303, `/@${meUser.username}`); | ||||
| 
 | ||||
| 	const code = url.searchParams.get("code") as string | null; | ||||
| 	const state = url.searchParams.get("state") as string | null; | ||||
| 	if (!code || !state) throw new ApiError(undefined, ErrorCode.BadRequest).obj; | ||||
| 
 | ||||
| 	const resp = await apiRequest<CallbackResponse>("POST", "/auth/fediverse/callback", { | ||||
| 		body: { code, state, instance: params.instance }, | ||||
| 		isInternal: true, | ||||
| 		fetch, | ||||
| 	}); | ||||
| 	const { meUser } = await parent(); | ||||
| 	if (meUser) { | ||||
| 		try { | ||||
| 			const resp = await apiRequest<AddAccountResponse>( | ||||
| 				"POST", | ||||
| 				"/auth/fediverse/add-account/callback", | ||||
| 				{ | ||||
| 					isInternal: true, | ||||
| 					body: { code, state, instance: params.instance }, | ||||
| 					fetch, | ||||
| 					cookies, | ||||
| 				}, | ||||
| 			); | ||||
| 
 | ||||
| 	if (resp.has_account) { | ||||
| 		setToken(cookies, resp.token!); | ||||
| 		redirect(303, `/@${resp.user!.username}`); | ||||
| 			return { hasAccount: true, isLinkRequest: true, newAuthMethod: resp }; | ||||
| 		} catch (e) { | ||||
| 			if (e instanceof ApiError) return { isLinkRequest: true, error: e.obj }; | ||||
| 			log.error("error linking new fediverse account to user %s:", meUser.id, e); | ||||
| 			throw e; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return { | ||||
| 		hasAccount: false, | ||||
| 		instance: params.instance, | ||||
| 		ticket: resp.ticket!, | ||||
| 		remoteUser: resp.remote_username!, | ||||
| 	}; | ||||
| 	try { | ||||
| 		const resp = await apiRequest<CallbackResponse>("POST", "/auth/fediverse/callback", { | ||||
| 			body: { code, state, instance: params.instance }, | ||||
| 			isInternal: true, | ||||
| 			fetch, | ||||
| 		}); | ||||
| 
 | ||||
| 		if (resp.has_account) { | ||||
| 			setToken(cookies, resp.token!); | ||||
| 			redirect(303, `/@${resp.user!.username}`); | ||||
| 		} | ||||
| 
 | ||||
| 		return { | ||||
| 			hasAccount: false, | ||||
| 			isLinkRequest: false, | ||||
| 			ticket: resp.ticket!, | ||||
| 			remoteUser: resp.remote_username!, | ||||
| 		}; | ||||
| 	} catch (e) { | ||||
| 		if (isRedirect(e)) throw e; | ||||
| 		if (e instanceof ApiError) return { isLinkRequest: false, error: e.obj }; | ||||
| 		log.error("error while requesting fediverse callback:", e); | ||||
| 		throw e; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export const actions = { | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| <script lang="ts"> | ||||
| 	import type { ActionData, PageData } from "./$types"; | ||||
| 	import { t } from "$lib/i18n"; | ||||
| 	import Error from "$components/Error.svelte"; | ||||
| 	import OauthRegistrationForm from "$components/settings/OauthRegistrationForm.svelte"; | ||||
| 	import NewAuthMethod from "$components/settings/NewAuthMethod.svelte"; | ||||
| 
 | ||||
| 	type Props = { data: PageData; form: ActionData }; | ||||
| 	let { data, form }: Props = $props(); | ||||
|  | @ -12,11 +14,18 @@ | |||
| </svelte:head> | ||||
| 
 | ||||
| <div class="container"> | ||||
| 	<OauthRegistrationForm | ||||
| 		title={$t("auth.register-with-mastodon")} | ||||
| 		remoteLabel={$t("auth.remote-fediverse-account-label")} | ||||
| 		remoteUser={data.remoteUser} | ||||
| 		ticket={data.ticket} | ||||
| 		error={form?.error} | ||||
| 	/> | ||||
| 	{#if data.error} | ||||
| 		<h1>{$t("auth.register-with-mastodon")}</h1> | ||||
| 		<Error error={data.error} /> | ||||
| 	{:else if data.isLinkRequest} | ||||
| 		<NewAuthMethod method={data.newAuthMethod!} user={data.meUser!} /> | ||||
| 	{:else} | ||||
| 		<OauthRegistrationForm | ||||
| 			title={$t("auth.register-with-mastodon")} | ||||
| 			remoteLabel={$t("auth.remote-fediverse-account-label")} | ||||
| 			remoteUser={data.remoteUser!} | ||||
| 			ticket={data.ticket!} | ||||
| 			error={form?.error} | ||||
| 		/> | ||||
| 	{/if} | ||||
| </div> | ||||
|  |  | |||
|  | @ -72,7 +72,11 @@ | |||
| 				<h4 class="mt-4">{$t("auth.log-in-with-the-fediverse")}</h4> | ||||
| 				<form method="POST" action="?/fedi" use:enhance> | ||||
| 					<InputGroup> | ||||
| 						<Input name="instance" type="text" placeholder="Your instance (i.e. mastodon.social)" /> | ||||
| 						<Input | ||||
| 							name="instance" | ||||
| 							type="text" | ||||
| 							placeholder={$t("auth.log-in-with-fediverse-instance-placeholder")} | ||||
| 						/> | ||||
| 						<Button type="submit" color="secondary">{$t("auth.log-in-button")}</Button> | ||||
| 					</InputGroup> | ||||
| 					<p> | ||||
|  |  | |||
|  | @ -0,0 +1,37 @@ | |||
| import { apiRequest } from "$api"; | ||||
| import { redirect } from "@sveltejs/kit"; | ||||
| 
 | ||||
| export const actions = { | ||||
| 	add: async ({ request, fetch, cookies }) => { | ||||
| 		const body = await request.formData(); | ||||
| 		const instance = body.get("instance") as string; | ||||
| 
 | ||||
| 		const { url } = await apiRequest<{ url: string }>( | ||||
| 			"GET", | ||||
| 			`/auth/fediverse/add-account?instance=${encodeURIComponent(instance)}`, | ||||
| 			{ | ||||
| 				isInternal: true, | ||||
| 				fetch, | ||||
| 				cookies, | ||||
| 			}, | ||||
| 		); | ||||
| 
 | ||||
| 		redirect(303, url); | ||||
| 	}, | ||||
| 	forceRefresh: async ({ request, fetch, cookies }) => { | ||||
| 		const body = await request.formData(); | ||||
| 		const instance = body.get("instance") as string; | ||||
| 
 | ||||
| 		const { url } = await apiRequest<{ url: string }>( | ||||
| 			"GET", | ||||
| 			`/auth/fediverse/add-account?instance=${encodeURIComponent(instance)}&forceRefresh=true`, | ||||
| 			{ | ||||
| 				isInternal: true, | ||||
| 				fetch, | ||||
| 				cookies, | ||||
| 			}, | ||||
| 		); | ||||
| 
 | ||||
| 		redirect(303, url); | ||||
| 	}, | ||||
| }; | ||||
|  | @ -0,0 +1,23 @@ | |||
| <script lang="ts"> | ||||
| 	import { t } from "$lib/i18n"; | ||||
| 	import { Button, Input, InputGroup } from "@sveltestrap/sveltestrap"; | ||||
| </script> | ||||
| 
 | ||||
| <h3>Link a new Fediverse account</h3> | ||||
| 
 | ||||
| <form method="POST" action="?/add"> | ||||
| 	<InputGroup> | ||||
| 		<Input | ||||
| 			name="instance" | ||||
| 			type="text" | ||||
| 			placeholder={$t("auth.log-in-with-fediverse-instance-placeholder")} | ||||
| 		/> | ||||
| 		<Button type="submit" color="secondary">{$t("auth.log-in-button")}</Button> | ||||
| 	</InputGroup> | ||||
| 	<p> | ||||
| 		{$t("auth.log-in-with-fediverse-error-blurb")} | ||||
| 		<Button formaction="?/forceRefresh" type="submit" color="link"> | ||||
| 			{$t("auth.log-in-with-fediverse-force-refresh-button")} | ||||
| 		</Button> | ||||
| 	</p> | ||||
| </form> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue