Skip to content

Commit 7ed9e4f

Browse files
committed
session passthrough for the wasp-launcher
This is required to give wasp-launcher the ability to do sign-ins. Until now it only supported logging in, sign in was not possible because it requires admin database access which the wasp-launcher cannot have due to security reasons, so from now on the website will handle sign ins and log ins for the launcher and pass the session to it once it's done. In a way similar to what the original wasp-launcher did before it has it's own embeded server.
1 parent 7870a37 commit 7ed9e4f

File tree

6 files changed

+204
-0
lines changed

6 files changed

+204
-0
lines changed

src/app.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ declare global {
99
// interface Error {}
1010
interface Locals {
1111
supabaseServer: SupabaseClient<Database>
12+
launcherSupabase: SupabaseClient<Database>
1213
safeGetSession: () => Promise<{ session: Session | null; user: User | null }>
1314
session: Session | null
1415
user: User | null

src/hooks.server.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ const supabase: Handle = async ({ event, resolve }) => {
3737
}
3838
)
3939

40+
event.locals.launcherSupabase = createServerClient<Database>(
41+
PUBLIC_SUPABASE_URL,
42+
PUBLIC_SUPABASE_ANON_KEY,
43+
{
44+
cookies: {
45+
getAll: () => event.cookies.getAll(),
46+
setAll: (cookiesToSet) => {
47+
cookiesToSet.forEach(({ name, value, options }) => {
48+
event.cookies.set(name, value, { ...options, path: "/" })
49+
})
50+
}
51+
}
52+
}
53+
)
54+
4055
event.locals.safeGetSession = async () => {
4156
let start = performance.now()
4257

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { formatError } from "$lib/utils"
2+
import { createStripeCustomer } from "$lib/server/stripe.server"
3+
4+
export const load = async ({ url: { searchParams }, locals: { launcherSupabase } }) => {
5+
console.log("💻 Launcher login")
6+
7+
const err = searchParams.get("error")
8+
9+
if (err) {
10+
let message = ""
11+
message += "<h3>Authentication Error</h3>"
12+
message += "<p>Error: " + decodeURI(err) + "</p>"
13+
14+
const description = searchParams.get("error_description")
15+
if (description) {
16+
message += "<p>Message: " + decodeURI(description) + "</p>"
17+
if (description.includes("email"))
18+
message += "<p>Make sure you have your email linked to discord!</p>"
19+
}
20+
21+
return {
22+
access_token: null,
23+
refresh_token: null,
24+
error: message
25+
}
26+
}
27+
28+
const code = searchParams.get("code")
29+
30+
if (code) {
31+
const {
32+
data: { user },
33+
error: err
34+
} = await launcherSupabase.auth.exchangeCodeForSession(code)
35+
36+
if (err) {
37+
return {
38+
access_token: null,
39+
refresh_token: null,
40+
error: formatError(err)
41+
}
42+
}
43+
44+
if (!user) {
45+
return {
46+
access_token: null,
47+
refresh_token: null,
48+
error: "Failed to get user!"
49+
}
50+
}
51+
52+
const {
53+
data: { session },
54+
error: errSession
55+
} = await launcherSupabase.auth.getSession()
56+
57+
if (errSession) {
58+
return {
59+
access_token: null,
60+
refresh_token: null,
61+
error: formatError(errSession)
62+
}
63+
}
64+
65+
if (!session) {
66+
return {
67+
access_token: null,
68+
refresh_token: null,
69+
error: "Failed to create session"
70+
}
71+
}
72+
73+
const { count } = await launcherSupabase
74+
.schema("profiles")
75+
.from("profiles")
76+
.select("*", { count: "exact", head: true })
77+
.eq("id", user.id)
78+
.single()
79+
80+
if (count) {
81+
return {
82+
access_token: session.access_token,
83+
refresh_token: session.refresh_token,
84+
error: null
85+
}
86+
}
87+
88+
if (user.email && user.app_metadata.provider == "discord") {
89+
const discord = user.user_metadata["provider_id"]
90+
const stripe = await createStripeCustomer(
91+
user.id,
92+
user.email,
93+
discord,
94+
user.user_metadata["custom_claims"]["global_name"]
95+
)
96+
if (!stripe) {
97+
return {
98+
access_token: null,
99+
refresh_token: null,
100+
error: "Failed to create stripe user for " + user.id
101+
}
102+
}
103+
104+
const { error: err } = await launcherSupabase.schema("profiles").from("profiles").insert({
105+
id: user.id,
106+
stripe,
107+
discord,
108+
username: "",
109+
avatar: "",
110+
role: null
111+
})
112+
113+
if (err) {
114+
return {
115+
access_token: null,
116+
refresh_token: null,
117+
error: "Failed to INSERT profile: " + formatError(err)
118+
}
119+
}
120+
}
121+
122+
return {
123+
access_token: session.access_token,
124+
refresh_token: session.refresh_token,
125+
error: null
126+
}
127+
}
128+
129+
return {
130+
access_token: null,
131+
refresh_token: null,
132+
error: "Failed to log you in."
133+
}
134+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script lang="ts">
2+
import { onMount } from "svelte"
3+
4+
const { data } = $props()
5+
const { access_token, refresh_token, error } = $derived(data)
6+
let loading = $state(true)
7+
8+
onMount(async () => {
9+
if (access_token && refresh_token) {
10+
await fetch("http://localhost:5217/", {
11+
method: "POST",
12+
headers: { "Content-Type": "application/json" },
13+
body: JSON.stringify({ access_token, refresh_token })
14+
}).catch(() => "Failed to fetch with session")
15+
} else if (data.error) {
16+
await fetch("http://localhost:5217/", {
17+
method: "POST",
18+
headers: { "Content-Type": "application/json" },
19+
body: JSON.stringify({ error })
20+
}).catch(() => "Failed to fetch with error")
21+
}
22+
23+
loading = false
24+
})
25+
</script>
26+
27+
<main class="my-32 h-screen text-center">
28+
{#if loading}
29+
<h1 class="text-xl font-bold">Loading...</h1>
30+
{:else if access_token && refresh_token}
31+
<h1 class="my-12 text-xl font-bold">You successfully logged in with WaspLauncher</h1>
32+
<h2 class="font-semibold">You may now close this page.</h2>
33+
{:else}
34+
<h1 class="text-xl font-bold">Failed to login!</h1>
35+
{/if}
36+
</main>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { formatError } from "$lib/utils"
2+
import { error, redirect } from "@sveltejs/kit"
3+
4+
export const GET = async ({ url: { origin }, locals: { launcherSupabase } }) => {
5+
console.log("💻 Starting launcher login!")
6+
const { data, error: err } = await launcherSupabase.auth.signInWithOAuth({
7+
provider: "discord",
8+
options: {
9+
redirectTo: origin + "/auth/launcher/",
10+
scopes: "identify email guilds guilds.members.read"
11+
}
12+
})
13+
14+
if (err) error(400, formatError(err))
15+
16+
redirect(303, data.url)
17+
}

svelte.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const config = {
3434
"connect-src": [
3535
"self",
3636
"ws://localhost:*",
37+
"http://localhost:*",
3738
"https://db.waspscripts.dev",
3839
"ws://db.waspscripts.dev",
3940
"wss://db.waspscripts.dev"

0 commit comments

Comments
 (0)