I am currently using Supabase to build out the Meeting Dolphin Extension. One of the user flows I wanted to create was one where the user:
-
Installs addon
-
Website popups
-
User signs in via OAuth in Website
-
User session is passed onto the extension
chrome.runtime.onInstalled(
chrome.tabs.create({
url: `AUTH_URL`
});
Follow the svelkite helpers setup. For Google OAuth
<button
on:click={async () => {
const response = await $page.data.supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/redirect`,
queryParams: { access_type: 'offline', prompt: 'consent' },
scopes:
'https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events'
}
});
}}>Continue with Google Calendar</Button
>
The main difficultly was the passing of the tokens - step 4. This is done using sendMessage + onMessageExternal.
In "manifest.json"
"externally_connectable": {
"matches": [
"*://meetingdolphin.com/*",
"http://127.0.0.1:5173/*"
]
},
In "background.js", we set the following local storage to prepare for receiving message from our website.
const options = {
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
storage: {
async getItem(key: string): Promise<string | null> {
// @ts-ignore
const storage = await chrome.storage.local.get(key);
return storage?.[key];
},
async setItem(key: string, value: string): Promise<void> {
// @ts-ignore
await chrome.storage.local.set({
[key]: JSON.parse(value)
});
},
async removeItem(key: string): Promise<void> {
// @ts-ignore
await chrome.storage.local.remove(key);
}
}
}
}
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY,
options
);
chrome.runtime.onMessageExternal.addListener(async ({ message, session }) => {
console.log("External message ", message)
if (message === "SIGNED_IN") {
const { data, error } = await supabase.auth.setSession({
access_token: session['access_token'],
refresh_token: session['refresh_token'],
});
chrome.storage.local.set({ session: session }, () => { })
}
else if (message === "SIGNED_OUT") {
const response = await supabase.auth.signOut()
}
}
)
In website, "+layout.svelte", we send a message and the current session to the extension (based on id) when there is an AuthChange
<script lang="ts">
import './styles.css';
import { invalidate } from '$app/navigation';
import { onMount } from 'svelte';
import type { LayoutData } from './$types';
import { publishAuthMessage } from '$lib/assets/js/auth';
export let data: LayoutData;
$: ({ supabase, session } = data);
onMount(() => {
const {
data: { subscription }
} = supabase.auth.onAuthStateChange((event, _session) => {
console.log(_session);
if (event === 'SIGNED_IN') {
publishAuthMessage('SIGNED_IN', _session);
}
if (event === 'SIGNED_OUT') {
publishAuthMessage('SIGNED_OUT');
}
if (_session?.expires_at !== session?.expires_at) {
invalidate('supabase:auth');
}
});
return () => subscription.unsubscribe();
});
</script>
<slot />
with "auth.ts"
export const publishAuthMessage = async (message: string, session?) => {
console.log('publishAuthMessage', message, session);
return await chrome.runtime.sendMessage(PUBLIC_EXTENSION_ID, {
message,
session
});
}
thanks for sharing this. I've been using something similar but calling
in my background script will actually invalidate the token on my website so users will have to log in again. Is that not an issue you had?