Vue
Um composable compartilhado por todos os componentes e guards que perguntam por permissão.
Este é o formato que o próprio console da nulvless usa — Vue 3 com o mesmo SDK, rodando inteiramente no navegador. Os guards de rota decidem o que é renderizado; quem decide o que é permitido continua sendo a sua API.
Instalação
npm install @nulvless/nulvless-jsCrie o client
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,
language: () => navigator.language,
},
);language define em qual idioma a plataforma renderiza qualquer mensagem
disparada por este client — um e-mail de verificação, por exemplo.
Um composable de sessão
import { onUnmounted, ref } from 'vue';
import type { Session } from '@nulvless/nulvless-js';
import { nulvless } from '@/lib/nulvless';
const session = ref<Session | null>(null);
const loading = ref(true);
nulvless.auth.getSession().then(({ data }) => {
session.value = data;
loading.value = false;
});
export function useSession() {
const offIn = nulvless.auth.on('signed-in', async () => {
const { data } = await nulvless.auth.getSession();
session.value = data;
});
const offOut = nulvless.auth.on('signed-out', () => (session.value = null));
const offDead = nulvless.auth.on('unauthorized', () => (session.value = null));
onUnmounted(() => {
offIn();
offOut();
offDead();
});
return { session, loading };
}As refs vivem no escopo do módulo, então todos os componentes compartilham uma única sessão em vez de cada um buscar a sua.
Entrada
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { nulvless } from '@/lib/nulvless';
const router = useRouter();
const email = ref('');
const password = ref('');
const error = ref<string | null>(null);
async function signIn() {
error.value = null;
const { data, error: signInError } = await nulvless.auth.signIn({
email: email.value,
password: password.value,
});
if (signInError) {
error.value = 'Confira o e-mail e a senha.';
return;
}
if (data.challenge) {
router.push({ name: 'verify', state: { challenge: data.challenge } });
return;
}
router.push('/');
}
</script>
<template>
<form @submit.prevent="signIn">
<input v-model="email" type="email" />
<input v-model="password" type="password" />
<p v-if="error" role="alert">{{ error }}</p>
<button type="submit">Entrar</button>
</form>
</template>Cadastro
O cadastro se ramifica no desafio, porque se a confirmação é por link ou por código é uma configuração do ambiente.
const { data, error } = await nulvless.auth.signUp({
email: email.value,
password: password.value,
profile: { name: name.value },
});
if (error) return;
if (data.challenge) {
if (data.challenge.preferred === 'link') {
checkYourEmail.value = true;
return;
}
router.push({ name: 'verify', state: { challenge: data.challenge } });
return;
}
router.push('/');Proteja as rotas
import type { NavigationGuard } from 'vue-router';
import { nulvless } from '@/lib/nulvless';
export const requireAuth: NavigationGuard = async (to) => {
const { data } = await nulvless.auth.getSession();
if (!data) return { name: 'sign-in', query: { next: to.fullPath } };
return true;
};Para uma rota que também exige permissão, pergunte na mesma chamada:
export const requireOwner: NavigationGuard = async (to) => {
const { error } = await nulvless.auth.verify({
resource: String(to.params.org),
roles: ['owner', 'admin'],
});
return error ? { name: 'forbidden' } : true;
};