Login com supabase

lucasfrazao

Lucas Frazao

Posted on July 16, 2024

Login com supabase

Olá, me chamo Lucas Frazão. Trabalho como desenvolvedor de software (SWE) há 5 anos.

Nesse post nós iremos construir um fluxo simples de autenticação utilizando ReactJS (v.18.3) e Supabase. Durante a construção de aplicações pessoais, é comum precisarmos adicionar uma autenticação. Então, eu gostaria de te mostrar como você pode fazer isso utilizando supabase auth.

O que é supabase?

O supabase é uma alternativa open source ao Firebase que utiliza PostgreSQL. Isso significa que ele pode ser usado para diversos fins além de autenticação, como armazenar e organizar imagens e dados, construir aplicativos de chat em tempo real, gerenciar autenticação de usuário e muito mais. Você pode encontrar mais informações na documentação oficial: https://supabase.com/docs

Vamos comecar

Para manter as coisas simples eu escolhi o vitejs como ferramenta para montar o ambiente, npm como gerenciador de pacotes node e o typescript por gostar bastante da stack.

Disclaimer: Esse tutorial pode ser seguido utilizando tanto TypeScript quanto JavaScript.

Primeiro passo é construir nossa aplicação para conectar com o supabase, vamos lá.

Execute o comando:

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

Vou utilizar "auth-supabase" como nome do meu projeto mas voce pode escolher livremente outro nome.

npm create vite@latest
? Project name: › auth-supabase
Enter fullscreen mode Exit fullscreen mode

Selecione React como framework:

✔ Project name: … auth-supabase
? Select a framework: › - Use arrow-keys. Return to submit.
   Vanilla
   Vue
❯  React
   Preact
   Lit
   Svelte
   Solid
   Qwik
   Others
Enter fullscreen mode Exit fullscreen mode

Selecione TypeScript (ou JavaScript) como variante:

✔ Project name: … auth-supabase
✔ Select a framework: › React
? Select a variant: › - Use arrow-keys. Return to submit.
❯  TypeScript
   TypeScript + SWC
   JavaScript
   JavaScript + SWC
   Remix ↗
Enter fullscreen mode Exit fullscreen mode

Agora vamos navegar até a pasta do nosso projeto e limpar algumas coisas que o vite deixa pronto.
Execute o comando:

cd auth-supabase && code .
Enter fullscreen mode Exit fullscreen mode

Você deve ver algo assim:
vscode interaction

Agora, para simplificar a estrutura do projeto, vamos seguir algumas etapas. Começando com:
-> Delete a pasta src/assets.
-> Substitua todo o conteúdo do arquivo src/index.css pelo código abaixo:

*,
:after,
:before {
    box-sizing: border-box;

}

body {
    margin: 0;
    min-height: 100vh;
    scroll-behavior: smooth;
    text-rendering: optimizeSpeed;
    font-synthesis: none;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;

}

a, button {
    color: inherit;
    cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode

-> Substitua todo o conteúdo do arquivo src/App.tsx pelo código abaixo:

import { FormEvent } from "react";
import "./App.css";

export default function App() {
    function handleSubmit(event: FormEvent<HTMLFormElement>) {
        event.preventDefault();

        const data = new FormData(event.currentTarget);
        const email = data.get("email")?.toString();
        const password = data.get("password")?.toString();

        console.log({ email, password });
    }

    return (
        <main>
            <h1>Sign in with supabase</h1>
            <form onSubmit={handleSubmit}>

                <label htmlFor="email">E-mail</label>
                <input 
                    type="text" 
                    name="email" 
                    placeholder="email@email.com" 
                    required 
                />

                <label htmlFor="password">Password</label>
                <input
                    type="password"
                    name="password"
                    placeholder="********"
                    required
                /> 

                <button type="submit">Login</button>
            </form>
        </main>
    );
}
Enter fullscreen mode Exit fullscreen mode

Precisamos instalar as dependências, então execute:

npm install 
Enter fullscreen mode Exit fullscreen mode

Excelente! Se você não teve nenhum erro durante as etapas anteriories, nós podemos seguir com as configurações do supabase para nosso projeto.
Durante esse tutorial nosso foco será um fluxo tradicional de login, com username e senha mas, o supabase tem supor para login social. Se você se interessar por essa opção, pode conferir a documentação oficial que tem mais detalhes:
https://supabase.com/docs/guides/auth/social-login

Para instalar o supabase, execute o comando:

npm install @supabase/supabase-js
Enter fullscreen mode Exit fullscreen mode

Podemos verificar se tudo deu certo através do package.json
package.json image

Uma vez que completamos essa etapa, execute o comando abaixo para executar um servidor local da aplicação:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Então, você conseguir acessar a aplicação no browser através da urlÇ http://localhost:5173/ .
Você irá ver algo assim:
web server image

Vamos configurar nossa autenticação. Lembre se, é necessário criar uma conta no supabase para continuar com essas configurações.

-> Selecione "New organization"
option to create new organization

-> Clique em "Create organization" e preencha todos os campos
form to create a new organization

-> Para criar um novo projeto, é necessário preencha todos os campos abaixo. Na parte de database password, você pode clicar em "Generate a password" e depois em "copy" para gerar uma senha automática, certifique se de manter essa senha guardada.
form to create a new project

Você não precisar alterar nenhuma configuração no meun "security options", basta clicar em "Create new project" para continuar.
Agora o supabase está configurando nosso projeto.
image of status setting up

-> No menu lateral, localize a opção "Authentication" e clique nela.
supabase sidebar

-> No meu de opções "Add user" selecione a opção "Create New User" para adicionar um novo usuário.

Observação: Durante esse tutorial vou demonstrar uma criação "direta" de um novo usuário, mas geralmente é recomendado priorizar a segurança em aplicações reais. Pensando nisso, caso deseja aplicar em uma aplicação pública, considere selecionar a opção "send invitation", que irá permitir o usuário confirmar seu cadastro, verificando a conta e trocando sua senha. Além disso, explore metódos diferentes de autenticação que o supabase oferece para encontrar aquele que se encaixa melhor com o seu cenário.
menu opened to create new user

-> Preencha com email e senha. Então, selecione "Create user" e deixe marcado a opção "Auto Confirm User?"
form to create a new user

O resultado esperado:
database with current users

Agora nós temos um usuário para testar a conexão entre o supabase e a nossa aplicação. Vamos navegar até a página home e selecionar a opção "Connect".

-> Clique em "Connect", no lado direito da página.
home page from supabase

-> Copie todo o conteúdo da aba .env
code block with enviroment variables

-> Na nossa aplicação, crie um arquivo .env e adicione os valores copiados anteriormente:
.env file on vscode

No Vite.js, as variáveis de ambiente são prefixadas com VITE_ por padrão. Você pode encontrar mais detalhes sobre as variáveis de ambiente na documentação oficial do Vite:
https://vitejs.dev/guide/env-and-mode

Agora, vamos criar uma função utilitária para o supabase. Vamos seguir os passos abaixo.

  • Criar uma nova pasta: Dentro do diretório src crie uma pasta com o nome utils.
  • Crie um arquivo TypeScript: Dentro da basta utils adicione um arquivo chamado supabase.ts.
  • Adicionando código: Cole o código abaixo dentro do seu arquivo supabase.ts.
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseKey);
Enter fullscreen mode Exit fullscreen mode

function supabase

Agora finalizamos toda a configuração do supabase, vamos para integração.

Etapa final

initial handleSubmit

Nós já temos os valores do email e senha armazenados em variáveis na função handleSubmit(), mas só utilizamos no console.log, então vamos fazer algumas alterações. Vamos refatorar essa função tornado ela assíncrona e adiciona um bloco try-catch.

async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const data = new FormData(event.currentTarget);
    const email = data.get("email")?.toString();
    const password = data.get("password")?.toString();

    try {
        console.log({ email, password });
    } catch (error) {
        console.error(error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Alguns benefícios ao utilizar try-catch em funções de submit e chamadas para api:

  • Prevenir fluxos inesperados: Capturando possíveis erros na submissão ou chamada da api, você pode manipular fluxos para melhorar a experiência do usuário.
  • Customização de erros: O bloco try-catch permite você prover mensages de erro ou feedbacks ao usuários dependendo do erro durante a submissão ou chamada à api.

Excelentes melhorias! Construindo essa manipulação de erros, podemos adicionar mais uma validação báisca antes do try-catch para evitar que valores undefined sejam passadas para a chamada da api.

if (!email || !password) return;
Enter fullscreen mode Exit fullscreen mode

handle submit with handler error

Agora, vamos importar a função supabase do nosso supabase.ts para dentro do arquivo src/App.tsx. Assim, nós podemos utiliza la dentro da função handleSubmit().

import { supabase } from "./utils/supabase";

/* code ... **/
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const data = new FormData(event.currentTarget);
    const email = data.get("email")?.toString();
    const password = data.get("password")?.toString(); 

    if (!email || !password) return;

    try {
        const {} = await supabase.auth.signInWithPassword({ email, password });
    } catch (error) {
        console.error(error);
    }
}
Enter fullscreen mode Exit fullscreen mode

signInWithPassword example

É possível acessar duas propriedades através da função signInWithPassword(), error e data, nós iremos utilizar data para capturar o token na aplicação e error para retornar um erro para o usuário. Observer, nós alteramos o nome da variável data para response, pois já utilizamos uma variável chamada data dentro desse escopo.

const { error, data: response } = await supabase.auth.signInWithPassword({
    email,
    password,
});
Enter fullscreen mode Exit fullscreen mode

Agora que já incorporamos um bloco try-catch e temos acesso ao objeto de erro, vamos adicionar uma validação condicional. Essa condicional irá determinar se iremos manipular o erro através de um throw new Error ou armazenar o token em local storage.

async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const data = new FormData(event.currentTarget);
    const email = data.get("email")?.toString();
    const password = data.get("password")?.toString();

    if (!email || !password) return;

    try {
        const { error, data: response } = await supabase.auth.signInWithPassword({
            email,
            password,
        });

        if (error) {
            throw new Error(error.message);
        } else {
            const accessToken = response.session?.access_token;
            localStorage.setItem("access_token", accessToken);
        }
    } catch (error) {
        console.error("occured an error:", error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Para verificar se tivemos sucesso no login, podemos verificar o local storage do navegador. Seguindo os após:

  1. Abrir o developer tools:
    • Windows: Pressione Ctrl + Shift + J simultaneamente.
    • macOS: Pressione Command (⌘) + Option (⌥) + J simultaneamente.
  2. Navegue até a aba "Aplicação".
  3. No menu sidebar, à esquerda, selecione a seção "Local Storage".
  4. Veja se encontra uma key chamada "access_token". A presença dessa chave e seu valor indica que tivemos sucesso no login. Você deve ver algo assim: local storage after login

Bônus

Agora que nós estabelecemos um fluxo de login, vamos explorar como implementar a funcionalidade de deslogar.
O processo de deslogar é bem semelhante ao utilizado na função handleSubmit(). Basicamente, seguiremos o mesmo padrão e chamada. Observe o código de exemplo abaixo para mais detalhes da implementação.

async function handleSignOut() {
    try {
    const { error } = await supabase.auth.signOut();

    if (error) {
        throw new Error(error.message);
    }

    } catch (error) {
        console.error(error);
    }
}
Enter fullscreen mode Exit fullscreen mode

É importante adicionar um botão que executa a função signOut().

<button type="button" onClick={handleSignOut}>
    sign out
</button>
Enter fullscreen mode Exit fullscreen mode

code from form
form with sign out button

Após clicar no botão de signOut(), você pode verificar que o fluxo de deslogar funcionou verificando novamente o local storage no seu navegador. O supabase utiliza local storage para armazenada objetos de autenticação, conforme o print, um fluxo de deslogar com sucesso irá deletar esse objeto criado pelo supabase.
after and before sign out

Lembre se, a chava chamada access_token foi criada por nós anteriormente. Após deslogar, podemos remover essa chave para garantir que a sessão do usuário seja encerrada. Abaixo temos um exemplo de adicionar essa funcionalidade dentro do handleSubmit().

// Add this if you want to clear all localStorage
if (error) {
    throw new Error(error.message);
} else {
    localStorage.clear()
}

// Add this if you want to delete an especific key
if (error) {
    throw new Error(error.message);
} else {
    localStorage.removeItem('access_token')
}
Enter fullscreen mode Exit fullscreen mode

Conclusão

Nós construimmos um fluxo de autenticação do usuário utilizando supabase na nossa aplicação. Esse tutorial cobriu algumas etapas importantes como inicialização e configuração do supabase, sign in, sign out e gereciamento do local storage. Agora nós temos um conhecimento sólido sobre como funcionar uma autenticação com o supabase.
Sinta se livre para customizar esse fluxo e explorar mais funcionalidades do supabase, além de deixar seu comentário ou me enviar um feedback!

💖 💪 🙅 🚩
lucasfrazao
Lucas Frazao

Posted on July 16, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related