Documentação
Comece a construirQuickstarts por frameworkAngular

Angular

Signals para a sessão, um guard para rotas, um interceptor para a sua API.

Uma configuração do lado do navegador: um serviço injetável guarda o client, signals expõem a sessão aos templates e um interceptor coloca o token nas requisições para o seu próprio backend.

Instalação

npm install @nulvless/nulvless-js

Um serviço injetável

src/app/nulvless.service.ts
import { Injectable, signal } from '@angular/core';
import { createClient, Storage, type Session } from '@nulvless/nulvless-js';
import { environment } from '../environments/environment';

@Injectable({ providedIn: 'root' })
export class NulvlessService {
  readonly client = createClient(environment.nulvlessUrl, environment.nulvlessKey, {
    storage: Storage.Local,
    trackUser: true,
  });

  readonly session = signal<Session | null>(null);
  readonly loading = signal(true);

  constructor() {
    this.client.auth.getSession().then(({ data }) => {
      this.session.set(data);
      this.loading.set(false);
    });

    this.client.auth.on('signed-in', async () => {
      const { data } = await this.client.auth.getSession();
      this.session.set(data);
    });

    this.client.auth.on('signed-out', () => this.session.set(null));
    this.client.auth.on('unauthorized', () => this.session.set(null));
  }

  signIn(email: string, password: string) {
    return this.client.auth.signIn({ email, password });
  }

  signOut() {
    return this.client.auth.signOut();
  }
}

Expor a sessão como signal faz os templates re-renderizarem sozinhos quando a autenticação muda em qualquer lugar.

Entrada

src/app/sign-in.component.ts
import { Component, inject, signal } from '@angular/core';
import { Router } from '@angular/router';
import { NulvlessService } from './nulvless.service';

@Component({
  selector: 'app-sign-in',
  standalone: true,
  template: `
    <form (submit)="submit($event)">
      <input #email type="email" />
      <input #password type="password" />
      @if (error()) { <p role="alert">{{ error() }}</p> }
      <button type="submit">Entrar</button>
    </form>
  `,
})
export class SignInComponent {
  private nulvless = inject(NulvlessService);
  private router = inject(Router);
  error = signal<string | null>(null);

  async submit(event: Event) {
    event.preventDefault();
    const form = event.target as HTMLFormElement;

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

    if (error) return this.error.set('Confira o e-mail e a senha.');
    if (data.challenge) return this.router.navigate(['/verify']);

    this.router.navigate(['/']);
  }
}

Proteja uma rota

src/app/auth.guard.ts
import { inject } from '@angular/core';
import { Router, type CanActivateFn } from '@angular/router';
import { NulvlessService } from './nulvless.service';

export const authGuard: CanActivateFn = async (route) => {
  const nulvless = inject(NulvlessService);
  const router = inject(Router);

  const { data } = await nulvless.client.auth.getSession();
  return data ? true : router.createUrlTree(['/sign-in']);
};

Para exigir uma permissão, e não apenas uma sessão:

const { error } = await nulvless.client.auth.verify({
  resource: route.params['org'],
  roles: ['owner', 'admin'],
});

return error ? router.createUrlTree(['/forbidden']) : true;

Anexe o token à sua própria API

src/app/auth.interceptor.ts
import { inject } from '@angular/core';
import type { HttpInterceptorFn } from '@angular/common/http';
import { from, switchMap } from 'rxjs';
import { NulvlessService } from './nulvless.service';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const nulvless = inject(NulvlessService);

  return from(nulvless.client.auth.getAccessToken()).pipe(
    switchMap((token) =>
      next(token ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : req),
    ),
  );
};

Peça o token a cada requisição em vez de guardá-lo em cache — getAccessToken renova quando ele está perto de expirar.

Próximos

Nesta página