nicelogin-nextjs
v0.1.1
Published
NiceLogin SDK for Next.js - Complete authentication solution with Server Components, Server Actions, and Edge Middleware
Maintainers
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-nextjsEstrutura 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 ambienteConfiguracao Passo a Passo
Passo 1: Variaveis de Ambiente
Crie .env.local na raiz do projeto:
NEXT_PUBLIC_NICELOGIN_API_KEY=nicelogin_sua_api_key_aquiPasso 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
