Adapter di autenticazione personalizzati
Rebase include un proprio sistema di autenticazione — configuralo qui. Questa pagina riguarda l’altro caso: un identity provider già attivo o per cui paghi già un abbonamento.
Adapter di autenticazione personalizzati
Sezione intitolata “Adapter di autenticazione personalizzati”Rebase consente la sostituzione completa del sistema di autenticazione integrato tramite un’architettura di autenticazione modulare (pluggable). In questo modo la verifica dell’autenticazione viene disaccoppiata dal database e dai layer REST/WebSocket, consentendo un’integrazione trasparente con provider esterni come Clerk, Auth0, Firebase Auth o servizi di identità JWT personalizzati.
Il contratto AuthAdapter
Sezione intitolata “Il contratto AuthAdapter”È possibile implementare direttamente l’interfaccia AuthAdapter per ottenere il controllo completo. La definizione dell’interfaccia è la seguente:
import { Hono } from "hono";import type { HonoEnv } from "@rebasepro/server";import { AuthenticatedUser, AuthAdapterCapabilities, UserManagementAdapter, UserCreationPrepareResult, UserCreationFinalizeResult } from "@rebasepro/types";
export interface AuthAdapter { /** Unique identifier for this auth adapter (e.g., "clerk", "custom") */ readonly id: string;
/** * Verifies an incoming HTTP request and returns the authenticated user payload. * Called by Hono authentication middleware on every REST endpoint. */ verifyRequest(request: Request): Promise<AuthenticatedUser | null>;
/** * Verifies a raw token string (e.g. for WebSocket connection handshake phase 1). * If omitted, a synthetic request is automatically constructed. */ verifyToken?(token: string): Promise<AuthenticatedUser | null>;
/** Optional user management operations (CRUD) for the panel */ userManagement?: UserManagementAdapter;
/** Optional: Mount adapter-specific custom public routes (e.g. callback paths) */ createAuthRoutes?(): Hono<any, any, any> | undefined;
/** Optional: Mount adapter-specific admin-only routes */ createAdminRoutes?(): Hono<any, any, any> | undefined;
/** Advertise supported capabilities (to customize what the panel shows) */ getCapabilities(): AuthAdapterCapabilities | Promise<AuthAdapterCapabilities>;
/** Lifecycle hooks called during backend start and graceful shutdown */ initialize?(): Promise<void>; destroy?(): Promise<void>;
/** Custom user lifecycle hooks (e.g., hash passwords before collection writes) */ prepareUserCreation?( values: Record<string, unknown>, collectionAuth?: unknown ): Promise<UserCreationPrepareResult>;
finalizeUserCreation?( entity: { id: string; values: Record<string, unknown> }, clearPassword?: string ): Promise<UserCreationFinalizeResult>;
/** Static service key to bypass checks for server-to-server calls */ serviceKey?: string;}Il payload AuthenticatedUser
Sezione intitolata “Il payload AuthenticatedUser”Indipendentemente dal provider di autenticazione esterno scelto, il tuo adapter deve risolvere le verifiche dei token andate a buon fine in un oggetto uniforme AuthenticatedUser. L’RLS Scope Injector di Rebase mappa direttamente questi valori nelle variabili di sessione PostgreSQL all’interno delle transazioni:
export interface AuthenticatedUser { uid: string; // Maps to pg local 'app.user_id' -> rebase.uid() email: string; // User email address displayName?: string | null; // Optional display name photoUrl?: string | null; // Optional avatar URL roles: string[]; // Maps to pg local 'app.user_roles' -> rebase.roles() isAdmin: boolean; // Grants global superuser privileges if true rawToken?: string; // The original token string (for downstream forwarding) claims?: Record<string, any>; // Custom claims/metadata (available in rebase.jwt())}Integrazione rapida tramite createCustomAuthAdapter
Sezione intitolata “Integrazione rapida tramite createCustomAuthAdapter”Per scenari standard (come la convalida di JWT provenienti da un servizio di terze parti), puoi utilizzare l’utility createCustomAuthAdapter. Questa utility gestisce i valori predefiniti delle funzionalità (capabilities) e implementa la convalida dei token WebSocket out-of-the-box eseguendo il wrapping della tua implementazione di verifyRequest.
Esempio: Integrazione con Clerk
Sezione intitolata “Esempio: Integrazione con Clerk”Per connettere un backend Rebase con Clerk, puoi verificare i token JWT di Clerk utilizzando il JSON Web Key Set (JWKS) di Clerk:
import { initializeRebaseBackend } from "@rebasepro/server";import { createCustomAuthAdapter } from "@rebasepro/server";import { createRemoteJWKSet, jwtVerify } from "jose";
// Clerk JWKS URLconst CLERK_JWKS_URL = "https://clerk.your-domain.com/.well-known/jwks.json";const JWKS = createRemoteJWKSet(new URL(CLERK_JWKS_URL));
const clerkAuthAdapter = createCustomAuthAdapter({ serviceKey: process.env.REBASE_SERVICE_KEY, verifyRequest: async (request) => { const authHeader = request.headers.get("Authorization"); const token = authHeader?.replace("Bearer ", ""); if (!token) return null;
try { // Verify Clerk JWT token against JWKS const { payload } = await jwtVerify(token, JWKS);
const metadata = payload.metadata as Record<string, unknown> | undefined; const roles = Array.isArray(metadata?.roles) ? metadata.roles as string[] : [];
return { uid: payload.sub!, email: (payload as Record<string, unknown>).email as string || "", displayName: (payload as Record<string, unknown>).name as string || null, roles: roles, isAdmin: roles.includes("admin"), claims: payload as Record<string, unknown> }; } catch (error) { console.error("Clerk token verification failed:", error); return null; // Fail-closed } }, capabilities: { hasBuiltInAuthRoutes: false, // Login is managed by Clerk UI emailPasswordLogin: false, registrationEnabled: false, passwordReset: false, profileUpdate: false, sessionManagement: false }});
const backend = await initializeRebaseBackend({ auth: clerkAuthAdapter, // ...});Esempio: Integrazione con Firebase Auth
Sezione intitolata “Esempio: Integrazione con Firebase Auth”Per verificare i token di Firebase Auth utilizzando i certificati pubblici di Firebase:
import { initializeRebaseBackend } from "@rebasepro/server";import { createCustomAuthAdapter } from "@rebasepro/server";import { createRemoteJWKSet, jwtVerify } from "jose";
const FIREBASE_JWKS_URL = "https://www.googleapis.com/robot/v1/metadata/jwk/securetoken@system.gserviceaccount.com";const JWKS = createRemoteJWKSet(new URL(FIREBASE_JWKS_URL));const FIREBASE_PROJECT_ID = "my-firebase-project-id";
const firebaseAuthAdapter = createCustomAuthAdapter({ serviceKey: process.env.REBASE_SERVICE_KEY, verifyRequest: async (request) => { const authHeader = request.headers.get("Authorization"); const token = authHeader?.replace("Bearer ", ""); if (!token) return null;
try { const { payload } = await jwtVerify(token, JWKS, { issuer: `https://securetoken.google.com/${FIREBASE_PROJECT_ID}`, audience: FIREBASE_PROJECT_ID });
const roles = Array.isArray((payload as Record<string, unknown>).roles) ? (payload as Record<string, unknown>).roles as string[] : [];
return { uid: payload.sub!, email: (payload as Record<string, unknown>).email as string || "", displayName: (payload as Record<string, unknown>).name as string || null, photoUrl: (payload as Record<string, unknown>).picture as string || null, roles: roles, isAdmin: roles.includes("admin"), claims: payload as Record<string, unknown> }; } catch (error) { console.error("Firebase token verification failed:", error); return null; } }});
const backend = await initializeRebaseBackend({ auth: firebaseAuthAdapter, // ...});Montaggio di rotte di autenticazione e azioni del pannello
Sezione intitolata “Montaggio di rotte di autenticazione e azioni del pannello”Se il tuo provider di autenticazione personalizzato richiede il montaggio di endpoint di reindirizzamento (come le route di callback OAuth o i loop di login SAML), implementa il metodo createAuthRoutes sul tuo adapter:
const myOauthAdapter: AuthAdapter = { id: "custom-oauth", verifyRequest: async (req) => ({ // validate the token, then return the caller uid: "…", email: "user@example.com", roles: [], isAdmin: false }), getCapabilities: () => ({ hasBuiltInAuthRoutes: true, emailPasswordLogin: false, registrationEnabled: false, passwordReset: false, adminPasswordReset: false, sessionManagement: false, profileUpdate: false, emailVerification: false, magicLink: false, anonymousLogin: false, enabledProviders: [] }), createAuthRoutes: () => { const app = new Hono<HonoEnv>();
// Mounted automatically under /api/auth/callback app.get("/callback", async (c) => { const code = c.req.query("code"); // Exchange code for provider tokens and set cookies/redirect return c.redirect("/dashboard"); });
return app; }};Se desideri consentire le operazioni CRUD sugli utenti direttamente all’interno del pannello, implementa l’helper userManagement all’interno delle opzioni dell’adapter, che fornisce hook per listUsers, createUser, updateUser e deleteUser.
Prossimi passi
Sezione intitolata “Prossimi passi”- Autenticazione — la configurazione del provider integrato
- Endpoint e token — le rotte che un adapter deve soddisfare
- Regole di sicurezza (RLS) — a cosa servono i claim restituiti da un adapter