API Authentication

Purpose and Scope

Authentication in Dub has two related meanings that developers should keep separate. The public API contract tells integrators how to authenticate programmatic requests to workspace resources, while the web application source shows how Dub authenticates people using the dashboard, enterprise admin surfaces, magic links, OAuth providers, credentials, and SAML. Both are prerequisites for safe API usage, but they operate at different boundaries. Server integrations should authenticate with workspace-scoped API keys, whereas interactive users authenticate through the application session system. The source files on this page explain the second layer and show how protected route handlers depend on it. Sources: apps/web/lib/auth/options.ts, apps/web/lib/auth/index.ts

The official API authentication guidance defines two key families. API keys are for server-to-server REST API calls and should remain in backend code, environment variables, or a credential manager. Publishable keys are for client-side conversion tracking and are safe to include in frontend code. That distinction is especially important for Dub because the platform spans short links, analytics, conversion tracking, and affiliate workflows. A browser-side tracking event should not receive a secret workspace API key, and a backend job that creates links or reads analytics should not rely on a dashboard session cookie. The code here reinforces that boundary by centralizing user sessions and admin route protection separately from resource-specific API behavior. Sources: apps/web/lib/auth/options.ts

Relevant Source Files

  • apps/web/lib/auth/options.ts - Defines the central NextAuth options object, custom Prisma adapter behavior, email login-link sending, external provider setup, verification-token handling, and supporting imports for SAML, rate limiting, account locking, and product tracking.
  • apps/web/lib/auth/index.ts - Re-exports the auth module family, including admin helpers, hash-token utilities, options, session helpers, general utilities, and workspace auth helpers from one import boundary.
  • apps/web/app/(ee)/admin.dub.co/(auth)/layout.tsx - Provides the enterprise admin authentication layout, wrapping admin auth pages with the shared Dub background and centered container.
  • apps/web/app/(ee)/admin.dub.co/(auth)/login/page.tsx - Reuses the main application login page for the enterprise admin auth route rather than implementing a separate login form.
  • apps/web/app/(ee)/api/admin/partners/[partnerId]/generate-veriff-session/route.ts - Demonstrates a protected admin API route that requires owner access before generating or returning a partner identity-verification session.
  • apps/web/app/(ee)/api/auth/saml/authorize/route.ts - Implements the SAML authorization endpoint by delegating to Jackson, accepting GET query parameters or POST JSON, and returning either a redirect or an HTML authorization form.

Core Primitives

The main primitive is the shared authentication configuration exported from the options module. It is a NextAuth configuration that imports providers, the Prisma adapter, Prisma client types, email delivery, storage checks, rate limiting, SAML helpers, account-locking utilities, admin impersonation support, password validation, and post-login product hooks. That breadth is intentional: sign-in is not only a credential exchange. It can create a Dub-shaped user record, send or suppress a login email depending on environment, link an external identity provider account, enforce enterprise single sign-on rules, and preserve support workflows such as admin impersonation. Sources: apps/web/lib/auth/options.ts

The custom Prisma adapter is the most concrete source-level explanation of how Dub extends generic authentication. It wraps the standard Prisma adapter but overrides user creation, account linking, and verification-token consumption. New users receive an application-specific user identifier and a default notification preferences record. Account linking writes only the account fields that match the database model because some identity providers can return extra token properties. Verification-token consumption deletes the token, handles a not-found token as a null result, and records admin impersonation when the token is marked for that purpose. These behaviors mean authentication changes can affect schema compatibility, onboarding defaults, support tooling, and token safety at the same time. Sources: apps/web/lib/auth/options.ts

The auth barrel module is small but important because it defines how the rest of the application should reach authentication helpers. Instead of importing directly from implementation files, route handlers can import from the shared auth boundary. The barrel re-exports admin helpers, hash-token utilities, the options object, session helpers, general utilities, and workspace authentication helpers. That pattern matters for maintainability: an API route that needs authorization can depend on a stable module boundary, while the implementation behind that boundary can continue to evolve. When adding new protected routes, prefer this public auth import surface unless there is a specific reason to work inside the auth package itself. Sources: apps/web/lib/auth/index.ts

System-to-Code Mapping

ConcernSource-backed implementationDeveloper takeaway
Public API prerequisitesOfficial docs distinguish secret API keys from publishable keys; the source evidence here covers session and route authentication.Use backend API keys for REST integrations and publishable keys only for client-side tracking.
Shared application authapps/web/lib/auth/options.ts defines the central NextAuth configuration and adapter overrides.Interactive sign-in, provider linking, token consumption, and login emails are centralized.
Auth import boundaryapps/web/lib/auth/index.ts re-exports admin, hash-token, options, session, utils, and workspace modules.Protected routes should import shared helpers through the auth module boundary.
Enterprise admin loginapps/web/app/(ee)/admin.dub.co/(auth)/layout.tsx and apps/web/app/(ee)/admin.dub.co/(auth)/login/page.tsx compose the admin auth UI.Admin login reuses the main login page inside an admin-specific layout.
Admin API authorizationapps/web/app/(ee)/api/admin/partners/[partnerId]/generate-veriff-session/route.ts wraps its handler with admin authorization and an owner role requirement.Sensitive admin operations should declare role requirements before business logic runs.
SAML authorizationapps/web/app/(ee)/api/auth/saml/authorize/route.ts delegates to Jackson and returns a redirect or HTML authorization form.Enterprise SSO protocol handling stays in auth routes instead of resource handlers.

Execution Flow

A normal interactive sign-in begins at a provider entry point and eventually reaches the shared NextAuth options. For email login, the provider callback receives an identifier and login URL. In non-production environments, the callback prints the login link instead of sending mail, which keeps local development usable without a production email provider. In production, the callback sends a rendered Dub login-link email through the email package. If the user is new, the custom adapter creates the user record with Dub defaults. If the user signs in through an external identity provider, account linking filters provider data before writing it to the account table. Sources: apps/web/lib/auth/options.ts

Token consumption is another important part of the sign-in flow. The adapter deletes the verification token matching the identifier and token pair, then checks whether the consumed token was created for admin impersonation. If so, it marks that impersonation state after successful consumption. If Prisma reports that the token no longer exists, the adapter returns a null result instead of treating the missing token as an application crash. This gives the auth layer a predictable response for expired, reused, or already-consumed links. Contributors changing login-link behavior should preserve that distinction between an invalid token outcome and an unexpected persistence error. Sources: apps/web/lib/auth/options.ts

An enterprise SAML flow uses a separate authorization route. The handler obtains an OAuth controller from Jackson, normalizes request input from query parameters for GET requests or a JSON body for POST requests, and calls the controller authorization method. The controller response determines the HTTP result. A redirect URL produces a temporary redirect response, while an authorization form produces an HTML response with the appropriate content type. This route is best understood as a protocol adapter. It translates incoming SAML authorization traffic into the response shape expected by the identity provider and browser without mixing that protocol work into links, analytics, partners, or workspace resource handlers. Sources: apps/web/app/(ee)/api/auth/saml/authorize/route.ts

Admin-protected API routes add an authorization layer after authentication. The partner Veriff session route exports a POST handler wrapped by the admin helper and declares that the caller must have the owner role. Only after that boundary does the handler load the partner, check whether the partner exists, inspect identity-verification status, and decide whether to return an existing unexpired session or create a new one. This sequencing is a useful pattern for contributors: check access at the route boundary, then perform resource lookup, state validation, idempotency checks, external-service calls, and persistence updates inside the authorized handler. Sources: apps/web/app/(ee)/api/admin/partners/[partnerId]/generate-veriff-session/route.ts

API Components Reference

NameKindInputs visible in sourceOutputs or behavior visible in source
authOptionsNextAuth configurationProviders, custom adapter, environment flags, and imported auth helpersCentral session and sign-in behavior for the web app.
CustomPrismaAdapterAdapter wrapperPrismaClient and NextAuth adapter callsCreates Dub-shaped users, links allowed account fields, consumes verification tokens, and marks admin impersonation tokens.
EmailProvider sendVerificationRequestProvider callbackidentifier and urlLogs the login URL outside production or sends a Dub login-link email in production.
Auth index exportsModule barreladmin, hash-token, options, session, utils, and workspace modulesStable import surface for shared auth helpers.
AdminAuthLayoutReact layoutchildrenRenders the shared background and centered admin auth container.
Admin login page exportRoute pageno local props in the fileRe-exports the main application login page for the admin auth route.
POST generate Veriff sessionRoute handlerpartnerId route parameter and owner admin role requirementReturns an error, an existing session URL, or a newly created session URL.
SAML authorize GET and POSTRoute handlersquery parameters for GET or JSON body for POSTRedirects to an authorization URL or returns an HTML authorization form.

Implementation Details and Edge Cases

Several edge cases are deliberately handled close to authentication rather than scattered through feature code. Development login avoids sending real email by logging the magic link. Provider account linking guards against identity providers that return fields outside the account table shape. Verification-token deletion distinguishes a Prisma not-found condition from other database errors. Admin impersonation is marked only after the verification token has been consumed. These details reduce operational surprises: local development works without external mail setup, provider variance does not corrupt account writes, expired login links produce a controlled result, and support impersonation is tracked at the point where the session is established. Sources: apps/web/lib/auth/options.ts

The admin Veriff route illustrates that authorization and business-state validation are complementary, not interchangeable. Owner access is necessary for the route, but the handler still rejects unknown partners, avoids new sessions for already approved identities, blocks duplicate attempts when a verification is submitted or under review, and reuses an existing unexpired session URL. The route then creates a Veriff session only when the partner state allows it and stores the returned session identifier, URL, and expiration metadata. New protected API handlers should follow the same layered approach rather than assuming that a role check alone makes every state transition safe. Sources: apps/web/app/(ee)/api/admin/partners/[partnerId]/generate-veriff-session/route.ts

The SAML route has a different kind of edge case: multiple transport and response forms are valid. Browser redirects commonly arrive with query string parameters, while some integrations can send JSON in a POST body. The route supports both before delegating to Jackson. The response can also be either a redirect or an HTML form, so callers and tests should not assume that successful authorization always has the same response body shape. Keeping this behavior inside the auth route protects the rest of the application from SAML protocol details and makes enterprise SSO easier to reason about. Sources: apps/web/app/(ee)/api/auth/saml/authorize/route.ts

Guidance for API Consumers and Contributors

For API consumers, start by deciding where the code executes. Backend services that create short links, retrieve analytics, manage domains, or automate workspace resources should use a secret workspace API key and store it outside the client bundle. Frontend code that reports conversion activity should use a publishable key because that class of credential is designed for browser exposure. Do not copy a server API key into a web page, and do not build a server integration around a dashboard session. Sessions authenticate people operating Dub, while API keys authenticate programs acting on a workspace.

For contributors, use the existing boundaries when adding authentication-sensitive behavior. Import shared helpers through the auth barrel, inspect the central options module before changing sign-in behavior, and keep provider-specific or protocol-specific work near the auth routes. Admin APIs should declare role requirements before resource logic, then still validate resource existence, current state, duplicate-action behavior, and external-service side effects. If you are working on SAML, preserve the route’s ability to accept GET and POST input and return either redirect or HTML responses. Next, read the resource-specific API pages for the endpoints you are securing, especially links, analytics, track events, partners, commissions, and payouts.