Adaptadores de autenticación personalizados
Rebase incluye su propio sistema de autenticación — configúralo aquí. Esta página aborda el otro caso: un proveedor de identidad que ya utilizas o por el que ya pagas.
Adaptadores de autenticación personalizados
Sección titulada «Adaptadores de autenticación personalizados»Rebase permite el reemplazo completo del sistema de autenticación integrado mediante una arquitectura de autenticación conectable (pluggable). Esto desacopla la verificación de autenticación de la base de datos y de las capas REST/WebSocket, lo que permite una integración fluida con proveedores externos como Clerk, Auth0, Firebase Auth o servicios de identidad JWT personalizados.
El contrato AuthAdapter
Sección titulada «El contrato AuthAdapter»Puedes implementar la interfaz AuthAdapter directamente para obtener un control total. La interfaz, de forma resumida (la definición completa, con los hooks opcionales de administración de usuarios y transformAuthResponse, es AuthAdapter en @rebasepro/types):
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>;
/** * The identity an account would carry right now, with no credential. * Without it, a request that impersonates another user is refused. */ resolveUser?(uid: 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;}El payload de AuthenticatedUser
Sección titulada «El payload de AuthenticatedUser»Independientemente del proveedor de autenticación externo elegido, tu adaptador debe resolver las verificaciones de token exitosas en un objeto AuthenticatedUser uniforme. El RLS Scope Injector de Rebase asigna estos valores directamente a variables de sesión de PostgreSQL dentro de las transacciones:
export interface AuthenticatedUser { uid: string; // Maps to pg local 'app.uid' -> 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; // Informational: authorization reads `roles` (give the `admin` role) isAnonymous?: boolean; // A guest session. Maps to 'app.is_anonymous' -> rebase.is_anonymous() rawToken?: string; // The original token string (for downstream forwarding) claims?: Record<string, any>; // Custom claims/metadata (available in rebase.jwt()) scopes?: string[]; // Narrower than the user's roles (set for an API key)}Integración rápida mediante createCustomAuthAdapter
Sección titulada «Integración rápida mediante createCustomAuthAdapter»Para escenarios estándar (como validar JWTs de un servicio de terceros), puedes usar la utilidad createCustomAuthAdapter. Esta utilidad gestiona los valores predeterminados de las capacidades e implementa la validación de tokens de WebSocket de forma predeterminada envolviendo tu implementación de verifyRequest.
Ejemplo: Integración con Clerk
Sección titulada «Ejemplo: Integración con Clerk»Para conectar un backend de Rebase con Clerk, puedes verificar los tokens JWT de Clerk utilizando el JSON Web Key Set (JWKS) de 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, // ...});Ejemplo: Integración con Firebase Auth
Sección titulada «Ejemplo: Integración con Firebase Auth»Para verificar los tokens de Firebase Auth utilizando los certificados públicos de 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, // ...});La parte del cliente
Sección titulada «La parte del cliente»Antes de la versión 0.25, el getter autenticaba únicamente las solicitudes HTTP, y el socket en tiempo real permanecía anónimo.
El adaptador verifica el token del proveedor; el cliente se encarga de enviarlo. Proporciona al cliente de Rebase un getter para este token, una sola vez:
client.setAuthTokenGetter(() => clerk.session?.getToken() ?? Promise.resolve(null));Con Firebase Auth el getter es
() => auth.currentUser?.getIdToken() ?? Promise.resolve(null). El cliente
lo consulta para cada solicitud HTTP y cada vez que el socket en tiempo real
se autentica, de modo que las consultas en vivo (live queries) se ejecutan con el mismo usuario que las llamadas REST. En el
panel de administración, establécelo en el cliente que pasas a <Rebase client>. Consulta
Tu propio proveedor de autenticación.
Montaje de rutas de autenticación y acciones del panel
Sección titulada «Montaje de rutas de autenticación y acciones del panel»Si tu proveedor de autenticación personalizado requiere montar endpoints de redirección (como rutas de callback de OAuth o flujos de inicio de sesión de SAML), implementa el método createAuthRoutes en tu adaptador:
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; }};Si deseas permitir operaciones CRUD de usuarios directamente dentro del panel, implementa el helper userManagement dentro de las opciones del adaptador, el cual proporciona hooks para listUsers, getUserById, createUser, updateUser, deleteUser, getUserRoles y setUserRoles.
Siguientes pasos
Sección titulada «Siguientes pasos»- Autenticación — configuración del proveedor integrado
- Endpoints y tokens — las rutas que debe satisfacer un adaptador
- Reglas de seguridad (RLS) — para qué se utilizan los claims que devuelve un adaptador