Documentação
Comece a construirQuickstarts por frameworkReact

React

Uma sessão que sobrevive ao reload e rotas que redirecionam quando ela acaba.

Tudo aqui roda no navegador, com a chave publicável. O fluxo completo: um client, um formulário de entrada que lida com MFA, um hook que acompanha a sessão e uma rota protegida.

Para verificações de permissão que o usuário não possa burlar, combine com Node na sua própria API.

Instalação

npm install @nulvless/nulvless-js

Crie o client

Um módulo só, importado em todo lugar. A chave publicável pode ficar no bundle.

src/lib/nulvless.ts
import { createClient, Storage } from '@nulvless/nulvless-js';

export const nulvless = createClient(
  import.meta.env.VITE_NULVLESS_URL,
  import.meta.env.VITE_NULVLESS_KEY,
  { storage: Storage.Local, trackUser: true },
);

Storage.Local mantém as pessoas autenticadas entre visitas. trackUser faz a plataforma registrar o dispositivo e a localização em cada sessão.

Acompanhe a sessão

O SDK emite eventos quando a autenticação muda em qualquer ponto da aplicação, então um hook basta.

src/lib/use-session.ts
import { useEffect, useState } from 'react';
import type { Session } from '@nulvless/nulvless-js';
import { nulvless } from './nulvless';

export function useSession() {
  const [session, setSession] = useState<Session | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    nulvless.auth.getSession().then(({ data }) => {
      setSession(data);
      setLoading(false);
    });

    const offIn = nulvless.auth.on('signed-in', () => {
      nulvless.auth.getSession().then(({ data }) => setSession(data));
    });
    const offOut = nulvless.auth.on('signed-out', () => setSession(null));
    const offDead = nulvless.auth.on('unauthorized', () => setSession(null));

    return () => {
      offIn();
      offOut();
      offDead();
    };
  }, []);

  return { session, loading };
}

on devolve a própria função de cancelamento — retorne-as do effect e o React faz a limpeza ao desmontar.

Entrada

O formulário precisa lidar com dois desfechos: uma sessão, ou um desafio quando a pessoa tem MFA cadastrado.

src/SignIn.tsx
import { useState } from 'react';
import type { Challenge } from '@nulvless/nulvless-js';
import { nulvless } from './lib/nulvless';

export function SignIn() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [challenge, setChallenge] = useState<Challenge | null>(null);
  const [error, setError] = useState<string | null>(null);

  async function submit(event: React.FormEvent) {
    event.preventDefault();
    setError(null);

    const { data, error } = await nulvless.auth.signIn({ email, password });

    if (error) return setError('Confira o e-mail e a senha.');
    if (data.challenge) return setChallenge(data.challenge);
    // Autenticado — a sessão foi guardada e o evento disparou.
  }

  if (challenge) return <VerifyCode challenge={challenge} />;

  return (
    <form onSubmit={submit}>
      <input value={email} onChange={(e) => setEmail(e.target.value)} type="email" />
      <input value={password} onChange={(e) => setPassword(e.target.value)} type="password" />
      {error && <p role="alert">{error}</p>}
      <button type="submit">Entrar</button>
    </form>
  );
}

Colete o código

Um componente serve a todos os desafios — MFA, entrada por OTP, sudo — porque challenge.subject diz em qual fluxo você está.

src/VerifyCode.tsx
import { useState } from 'react';
import type { Challenge } from '@nulvless/nulvless-js';
import { nulvless } from './lib/nulvless';

export function VerifyCode({ challenge }: { challenge: Challenge }) {
  const [code, setCode] = useState('');
  const [error, setError] = useState<string | null>(null);

  async function submit(event: React.FormEvent) {
    event.preventDefault();

    const { error } = await nulvless.auth.verifyChallenge({
      token: challenge.token,
      code,
    });

    if (error) setError('Esse código não funcionou.');
  }

  return (
    <form onSubmit={submit}>
      <p>Enviamos um código para {challenge.identifier}</p>
      <input value={code} onChange={(e) => setCode(e.target.value)} inputMode="numeric" />
      {error && <p role="alert">{error}</p>}
      <button type="submit">Confirmar</button>
    </form>
  );
}

challenge.identifier já vem mascarado — u***@example.com — então pode ser exibido sem risco.

Proteja uma rota

src/Protected.tsx
import { useSession } from './lib/use-session';
import { SignIn } from './SignIn';

export function Protected({ children }: { children: React.ReactNode }) {
  const { session, loading } = useSession();

  if (loading) return null;
  if (!session) return <SignIn />;

  return children;
}

Saída

<button onClick={() => nulvless.auth.signOut()}>Sair</button>

Passe 'others' para encerrar todas as outras sessões e manter esta, ou 'global' para encerrar todas.

Login social

<button onClick={() => nulvless.auth.authorize('google', {
  redirect_uri: `${window.location.origin}/callback`,
})}>
  Continuar com Google
</button>

O SDK cuida do redirecionamento. Na rota de callback:

useEffect(() => {
  const code = new URLSearchParams(window.location.search).get('code');
  if (code) nulvless.auth.exchangeCodeForToken(code);
}, []);

Renderize os botões a partir de getMethods() em vez de fixar os provedores no código — assim, habilitar um no console não exige novo deploy.

Próximos

Nesta página