L'API Italianonprofit utilizza PASETO (Platform-Agnostic Security Tokens) per l'autenticazione degli utenti. PASETO è un'alternativa moderna ai JWT con migliori garanzie di sicurezza.
Il sistema implementa un meccanismo di autenticazione con due tipi di token:
Questa separazione migliora la sicurezza limitando la validità dei token di accesso e riducendo i rischi in caso di compromissione.
I tempi di scadenza dei token possono essere configurati tramite variabili d'ambiente:
ACCESS_TOKEN_EXPIRY: Durata dell'access token in secondi (default: 3600 = 1 ora)REFRESH_TOKEN_EXPIRY: Durata del refresh token in secondi (default: 604800 = 7 giorni)Esempio per impostare un access token valido per 30 minuti:
ACCESS_TOKEN_EXPIRY=1800
Ottieni una coppia di token (access + refresh) tramite login:
POST /api/v1/auth/login
Body della richiesta:
{
"email": "utente@example.com",
"password": "password123"
}
Risposta:
{
"status": "success",
"data": {
"accessToken": "v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ...",
"refreshToken": "v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ...",
"expiresIn": 3600,
"user": {
"id": 123,
"email": "utente@example.com",
"name": "Nome Utente",
"role": "user"
}
}
}
Verifica la validità di un token:
POST /api/v1/auth/verify
Body della richiesta:
{
"token": "v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ..."
}
Risposta:
{
"status": "success",
"data": {
"valid": true,
"payload": {
"sub": "123",
"name": "Nome Utente",
"email": "utente@example.com",
"role": "user",
"type": "access",
"iat": 1516239022,
"exp": 1516242622
}
}
}
Rinnova un access token usando il refresh token:
POST /api/v1/auth/refresh
Body della richiesta:
{
"token": "v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ..."
}
Nota: Il token deve essere un refresh token, non un access token.
Risposta:
{
"status": "success",
"data": {
"accessToken": "v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ...",
"expiresIn": 3600
}
}
Per accedere alle route protette, includi l'access token nell'header Authorization:
Authorization: Bearer v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ...
curl -X GET https://nitro.italianonprofit.it/api/v1/users/me \
-H "Authorization: Bearer v4.public.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ..."
Il payload dell'utente autenticato è disponibile nell'oggetto event.context.user in tutti gli handler delle route protette. Questo è impostato automaticamente dal middleware di autenticazione globale.
{
sub: string; // ID utente
name: string; // Nome utente
email: string; // Email utente
role: string; // Ruolo (user, admin)
type: "access" | "refresh"; // Tipo di token
iat: number; // Issued at (timestamp)
exp: number; // Expiration (timestamp)
}
export default defineEventHandler((event) => {
// Accedi direttamente ai dati dell'utente
const user = event.context.user;
return {
message: `Benvenuto, ${user.name}!`,
userId: user.sub,
role: user.role,
};
});
I token PASETO contengono un campo type che ne indica l'utilizzo:
type: "access" - Token utilizzabile per autenticare le richiestetype: "refresh" - Token utilizzabile solo per ottenere nuovi access tokenIl sistema verifica automaticamente che:
1. Login → Ottieni accessToken + refreshToken
2. Usa accessToken per richieste API
3. Quando accessToken scade → Usa refreshToken per ottenere nuovo accessToken
4. Continua con nuovo accessToken
5. Quando refreshToken scade → Riloggare
class AuthManager {
private accessToken: string | null = null;
private refreshToken: string | null = null;
async login(email: string, password: string) {
const response = await fetch("/api/v1/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const data = await response.json();
this.accessToken = data.data.accessToken;
this.refreshToken = data.data.refreshToken;
// Salva in localStorage o secure storage
localStorage.setItem("accessToken", this.accessToken);
localStorage.setItem("refreshToken", this.refreshToken);
}
async refreshAccessToken() {
if (!this.refreshToken) {
throw new Error("No refresh token available");
}
const response = await fetch("/api/v1/auth/refresh", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: this.refreshToken }),
});
const data = await response.json();
this.accessToken = data.data.accessToken;
localStorage.setItem("accessToken", this.accessToken);
return this.accessToken;
}
async makeAuthenticatedRequest(url: string, options: RequestInit = {}) {
if (!this.accessToken) {
throw new Error("Not authenticated");
}
const response = await fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${this.accessToken}`,
},
});
// Se token scaduto, prova a rinnovare
if (response.status === 401) {
await this.refreshAccessToken();
// Riprova la richiesta
return fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${this.accessToken}`,
},
});
}
return response;
}
}
Per invalidare i token, semplicemente rimuovili dal client. I token non possono essere revocati lato server fino alla loro scadenza naturale.
logout() {
this.accessToken = null;
this.refreshToken = null;
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
}