JSPM

nicelogin-nextjs

0.1.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 7
  • Score
    100M100P100Q26147F
  • License MIT

NiceLogin SDK for Next.js - Complete authentication solution with Server Components, Server Actions, and Edge Middleware

Package Exports

  • nicelogin-nextjs
  • nicelogin-nextjs/client
  • nicelogin-nextjs/middleware
  • nicelogin-nextjs/server

Readme

nicelogin-nextjs

SDK completo de autenticacao para Next.js 13+ (App Router). Combina verificacao JWT RS256 local com JWKS, Server Components, Server Actions e Edge Middleware.

Instalacao

npm install nicelogin-nextjs

Estrutura de Arquivos Necessaria

Apos instalar, crie os seguintes arquivos na sua aplicacao Next.js:

app/
├── layout.tsx                      # Adicionar NiceLoginProvider
├── api/
│   └── auth/
│       ├── session/route.ts        # GET - retorna sessao atual
│       ├── login/route.ts          # POST - login
│       ├── logout/route.ts         # POST - logout
│       ├── register/route.ts       # POST - registro
│       └── oauth/
│           └── [provider]/route.ts # GET - redirect OAuth
├── login/page.tsx                  # Pagina de login
├── register/page.tsx               # Pagina de registro
└── dashboard/page.tsx              # Pagina protegida (exemplo)
middleware.ts                       # Protecao de rotas
.env.local                          # Variaveis de ambiente

Configuracao Passo a Passo

Passo 1: Variaveis de Ambiente

Crie .env.local na raiz do projeto:

NEXT_PUBLIC_NICELOGIN_API_KEY=nicelogin_sua_api_key_aqui

Passo 2: Configuracao do Servidor

Crie lib/auth.ts para inicializar o SDK no servidor:

// lib/auth.ts
import { initializeConfig } from 'nicelogin-nextjs';

// Inicializa configuracao (chamado automaticamente nas API routes)
export function initAuth() {
  initializeConfig({
    apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY!,
  });
}

Passo 3: Provider no Layout

// app/layout.tsx
import { NiceLoginProvider } from 'nicelogin-nextjs/client';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="pt-BR">
      <body>
        <NiceLoginProvider>
          {children}
        </NiceLoginProvider>
      </body>
    </html>
  );
}

Passo 4: Middleware de Protecao

// middleware.ts (na raiz do projeto, mesmo nivel que app/)
import { withAuth } from 'nicelogin-nextjs/middleware';

export default withAuth({
  publicRoutes: ['/', '/login', '/register', '/api/auth/(.*)'],
  loginUrl: '/login',
});

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Passo 5: API Routes de Autenticacao

Crie todas as rotas de API:

// app/api/auth/session/route.ts
import { NextResponse } from 'next/server';
import { getSession, initializeConfig } from 'nicelogin-nextjs';

export async function GET() {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });
  const session = await getSession();
  return NextResponse.json({ session });
}
// app/api/auth/login/route.ts
import { NextResponse } from 'next/server';
import { signIn, initializeConfig } from 'nicelogin-nextjs';

export async function POST(request: Request) {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });
  const formData = await request.formData();
  const result = await signIn(formData);
  return NextResponse.json(result);
}
// app/api/auth/logout/route.ts
import { NextResponse } from 'next/server';
import { signOut, initializeConfig } from 'nicelogin-nextjs';

export async function POST() {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });
  const result = await signOut();
  return NextResponse.json(result);
}
// app/api/auth/register/route.ts
import { NextResponse } from 'next/server';
import { signUp, initializeConfig } from 'nicelogin-nextjs';

export async function POST(request: Request) {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });
  const formData = await request.formData();
  const result = await signUp(formData);
  return NextResponse.json(result);
}
// app/api/auth/oauth/[provider]/route.ts
import { NextResponse } from 'next/server';
import { getOAuthUrl, initializeConfig } from 'nicelogin-nextjs';

export async function GET(
  request: Request,
  { params }: { params: { provider: string } }
) {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });

  const provider = params.provider as 'google' | 'github' | 'discord' | 'apple';
  const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth/${provider}/callback`;

  const { authorization_url } = await getOAuthUrl(
    provider,
    process.env.NEXT_PUBLIC_NICELOGIN_API_KEY!,
    redirectUri
  );

  return NextResponse.redirect(authorization_url);
}

Uso

Client Components (Hooks)

'use client';

import { useNiceLogin } from 'nicelogin-nextjs/client';

export function UserProfile() {
  const { user, isAuthenticated, isLoading, logout } = useNiceLogin();

  if (isLoading) return <div>Carregando...</div>;
  if (!isAuthenticated) return <div>Nao autenticado</div>;

  return (
    <div>
      <p>Email: {user?.email}</p>
      <button onClick={() => logout()}>Sair</button>
    </div>
  );
}

Server Components

// app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { getSession, initializeConfig } from 'nicelogin-nextjs';

export default async function DashboardPage() {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });

  const session = await getSession();
  if (!session) {
    redirect('/login');
  }

  return (
    <div>
      <h1>Bem-vindo, {session.user.email}!</h1>
      <p>ID: {session.user.id}</p>
    </div>
  );
}

Componentes Pre-Prontos

'use client';

import { LoginForm, RegisterForm, SignOutButton, AuthGuard } from 'nicelogin-nextjs/client';

// Formulario de login
<LoginForm 
  onSuccess={() => window.location.href = '/dashboard'} 
  onError={(err) => console.error(err)}
/>

// Formulario de registro
<RegisterForm 
  redirectTo="/dashboard"
  requirePasswordConfirmation={true}
/>

// Botao de logout
<SignOutButton redirectTo="/login">
  Sair da conta
</SignOutButton>

// Protecao de conteudo
<AuthGuard 
  fallback={<p>Faca login para ver este conteudo</p>}
  loadingComponent={<p>Carregando...</p>}
>
  <SecretContent />
</AuthGuard>

OAuth (Login Social)

'use client';

import { useNiceLogin } from 'nicelogin-nextjs/client';

export function SocialLogin() {
  const { loginWithOAuth } = useNiceLogin();

  return (
    <div>
      <button onClick={() => loginWithOAuth('google')}>
        Entrar com Google
      </button>
      <button onClick={() => loginWithOAuth('github')}>
        Entrar com GitHub
      </button>
      <button onClick={() => loginWithOAuth('discord')}>
        Entrar com Discord
      </button>
      <button onClick={() => loginWithOAuth('apple')}>
        Entrar com Apple
      </button>
    </div>
  );
}

API Reference

Hooks

Hook Descricao
useNiceLogin() Hook principal com user, login, logout, register
useSession() Hook para estado da sessao com polling

Server Functions

Funcao Descricao
getSession() Obtem sessao atual (ou null)
getAuthenticatedUser() Obtem usuario (throws se nao autenticado)
requireSession(redirectTo) Obtem sessao ou redireciona
verifyToken(token) Verifica e decodifica JWT
isTokenValid(token) Verifica se token e valido (boolean)

Server Actions

Action Descricao
signIn(formData) Login com email/password
signOut() Logout (limpa cookies)
signUp(formData) Registro + auto-login
requestPasswordReset(formData) Solicita reset de senha

Middleware

Funcao Descricao
withAuth(options) HOF para criar middleware de auth
createAuthMiddleware(options) Cria middleware diretamente

Componentes

Componente Descricao
NiceLoginProvider Provider para envolver a app
LoginForm Formulario de login
RegisterForm Formulario de registro
SignOutButton Botao de logout
AuthGuard Componente de protecao

Seguranca

  • Tokens armazenados em cookies HttpOnly
  • Verificacao RS256 local com JWKS (sem chamadas extras a API)
  • Cache de JWKS por 24h
  • Protecao CSRF integrada
  • Compativel com Edge Runtime

Diferencas vs nicelogin-react

Aspecto nicelogin-react nicelogin-nextjs
Storage localStorage Cookies HttpOnly
Verificacao Apenas decode RS256 completo
Server-side Nao Server Components + Actions
Middleware Nao Edge runtime
OAuth Nao 4 provedores
SSR Nao Completo

Exemplos Completos de Paginas

Pagina de Login

// app/login/page.tsx
'use client';

import { useState } from 'react';
import { useNiceLogin } from 'nicelogin-nextjs/client';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

export default function LoginPage() {
  const router = useRouter();
  const { login, loginWithOAuth, isLoading } = useNiceLogin();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');

    try {
      await login(email, password);
      router.push('/dashboard');
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Erro ao fazer login');
    }
  };

  return (
    <div style={{ maxWidth: 400, margin: '100px auto', padding: 20 }}>
      <h1>Login</h1>

      {error && <p style={{ color: 'red' }}>{error}</p>}

      <form onSubmit={handleSubmit}>
        <div style={{ marginBottom: 15 }}>
          <label>Email</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
            style={{ width: '100%', padding: 8 }}
          />
        </div>

        <div style={{ marginBottom: 15 }}>
          <label>Senha</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
            style={{ width: '100%', padding: 8 }}
          />
        </div>

        <button type="submit" disabled={isLoading} style={{ width: '100%', padding: 10 }}>
          {isLoading ? 'Entrando...' : 'Entrar'}
        </button>
      </form>

      <hr style={{ margin: '20px 0' }} />

      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        <button onClick={() => loginWithOAuth('google')}>Entrar com Google</button>
        <button onClick={() => loginWithOAuth('github')}>Entrar com GitHub</button>
      </div>

      <p style={{ marginTop: 20 }}>
        Nao tem conta? <Link href="/register">Criar conta</Link>
      </p>
    </div>
  );
}

Pagina de Registro

// app/register/page.tsx
'use client';

import { useState } from 'react';
import { useNiceLogin } from 'nicelogin-nextjs/client';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

export default function RegisterPage() {
  const router = useRouter();
  const { register, isLoading } = useNiceLogin();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');

    if (password !== confirmPassword) {
      setError('As senhas nao coincidem');
      return;
    }

    try {
      await register(email, password);
      router.push('/dashboard');
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Erro ao criar conta');
    }
  };

  return (
    <div style={{ maxWidth: 400, margin: '100px auto', padding: 20 }}>
      <h1>Criar Conta</h1>

      {error && <p style={{ color: 'red' }}>{error}</p>}

      <form onSubmit={handleSubmit}>
        <div style={{ marginBottom: 15 }}>
          <label>Email</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
            style={{ width: '100%', padding: 8 }}
          />
        </div>

        <div style={{ marginBottom: 15 }}>
          <label>Senha</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
            minLength={8}
            style={{ width: '100%', padding: 8 }}
          />
        </div>

        <div style={{ marginBottom: 15 }}>
          <label>Confirmar Senha</label>
          <input
            type="password"
            value={confirmPassword}
            onChange={(e) => setConfirmPassword(e.target.value)}
            required
            style={{ width: '100%', padding: 8 }}
          />
        </div>

        <button type="submit" disabled={isLoading} style={{ width: '100%', padding: 10 }}>
          {isLoading ? 'Criando...' : 'Criar Conta'}
        </button>
      </form>

      <p style={{ marginTop: 20 }}>
        Ja tem conta? <Link href="/login">Fazer login</Link>
      </p>
    </div>
  );
}

Pagina Dashboard (Protegida)

// app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { getSession, initializeConfig } from 'nicelogin-nextjs';
import LogoutButton from './LogoutButton';

export default async function DashboardPage() {
  initializeConfig({ apiKey: process.env.NEXT_PUBLIC_NICELOGIN_API_KEY! });

  const session = await getSession();
  if (!session) {
    redirect('/login');
  }

  return (
    <div style={{ maxWidth: 600, margin: '50px auto', padding: 20 }}>
      <h1>Dashboard</h1>
      <p>Bem-vindo, {session.user.email}!</p>
      <p>User ID: {session.user.id}</p>
      <p>Company ID: {session.user.companyId}</p>
      <LogoutButton />
    </div>
  );
}
// app/dashboard/LogoutButton.tsx
'use client';

import { useNiceLogin } from 'nicelogin-nextjs/client';
import { useRouter } from 'next/navigation';

export default function LogoutButton() {
  const router = useRouter();
  const { logout } = useNiceLogin();

  const handleLogout = async () => {
    await logout();
    router.push('/login');
  };

  return (
    <button onClick={handleLogout} style={{ marginTop: 20, padding: 10 }}>
      Sair
    </button>
  );
}

Licenca

MIT