Skip to content

Latest commit

 

History

History
155 lines (116 loc) · 5.26 KB

File metadata and controls

155 lines (116 loc) · 5.26 KB

🏡 Home

Create Supabase project Create Remix application Query Supabase data with Remix Loaders Generate TypeScript types from Supabase CLI Implement Supabase Auth using GitHub Restrict access with RLS policies Automatically set session cookie with Supabase Auth Helpers Call active Loaders on Supabase Auth state change Mutate Supabase data with Remix Actions Subscribe to database changes with Supabase Realtime Deploy Remix app to Vercel

Implement Supabase Auth using GitHub

📹 Video

Supabase supports a collection of auth strategies - Email and password, passwordless and OAuth. In this lesson, we look at implementing OAuth with GitHub.

In order to do this, we create a new GitHub OAuth app, and configure Supabase to use its Client ID and Secret. Additionally, we create a <Login /> component that uses the Supabase client to sign users in and out.

This identifies a problem that we don't have access to the environment variables required to create a Supabase client outside of loaders or actions. Therefore, we pipe through our SUPABASE_URL and SUPABASE_ANON_KEY from the Loader function, and create a singleton Supabase client, to use across our components.

Lastly, we look at sharing this single instance of Supabase through the Outlet Context and declare types to ensure we have TypeScript helping us out throughout the application.

Code Snippets

Expose environment variables from the server

export const loader = async ({}: LoaderArgs) => {
  const env = {
    SUPABASE_URL: process.env.SUPABASE_URL!,
    SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!,
  };

  return json({ env });
};

Access env in component

const { env } = useLoaderData<typeof loader>();

Create a singleton Supabase client

const [supabase] = useState(() =>
  createClient<Database>(env.SUPABASE_URL, env.SUPABASE_ANON_KEY)
);

Share global variables with Outlet Context

<Outlet context={{ supabase }} />

Consuming Outlet Context

const { supabase } = useOutletContext<SupabaseOutletContext>();

Logging in with GitHub

await supabase.auth.signInWithOAuth({
  provider: "github",
});

Logging out

await supabase.auth.signOut();

Entire root component

import { json, LoaderArgs, MetaFunction } from "@remix-run/node";
import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useLoaderData,
} from "@remix-run/react";
import { createClient, SupabaseClient } from "@supabase/supabase-js";
import { useState } from "react";

import type { Database } from "db_types";

type TypedSupabaseClient = SupabaseClient<Database>;

export type SupabaseOutletContext = {
  supabase: TypedSupabaseClient;
};

export const meta: MetaFunction = () => ({
  charset: "utf-8",
  title: "New Remix App",
  viewport: "width=device-width,initial-scale=1",
});

export const loader = async ({}: LoaderArgs) => {
  const env = {
    SUPABASE_URL: process.env.SUPABASE_URL!,
    SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!,
  };

  return json({ env });
};

export default function App() {
  const { env } = useLoaderData<typeof loader>();

  const [supabase] = useState(() =>
    createClient<Database>(env.SUPABASE_URL, env.SUPABASE_ANON_KEY)
  );
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet context={{ supabase }} />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

Resources


👉 Next lesson


Enjoying the course? Follow Jon Meyers on Twitter and subscribe to the YouTube channel.