@flinkk/shared-auth
v1.0.11
Published
NextAuth configuration, session helpers, and tenant-resolution logic shared by every Flinkk app. This is what decides "which tenant does this login belong to" and stays that way whether the deployment is multi-tenant, single-org standalone, or a restricte
Readme
@flinkk/shared-auth
NextAuth configuration, session helpers, and tenant-resolution logic shared by every Flinkk app. This is what decides "which tenant does this login belong to" and stays that way whether the deployment is multi-tenant, single-org standalone, or a restricted multi-org standalone.
Exports
| Subpath | What it is |
| --- | --- |
| @flinkk/shared-auth/options | authOptions — the shared NextAuth config (providers, JWT/session callbacks, CustomSession type). |
| @flinkk/shared-auth/server-session | getServerSession() — a wrapper around NextAuth's own getServerSession with retry logic for intermittent session-lookup failures. |
| @flinkk/shared-auth/tenant-resolver | getTenantResolver() — returns a TenantResolver (MultiTenantResolver or StandaloneTenantResolver, chosen by DEPLOYMENT_MODE) that resolves which tenant/membership applies for a user, and whether a candidate tenant is one this deployment's license actually permits. |
| @flinkk/shared-auth/token | getToken({ req }) — reads/verifies the NextAuth JWT from a request (used by API routes and middleware). |
| @flinkk/shared-auth/tenant-encryption | Encrypts/decrypts tenant-scoped secrets (e.g. OAuth tokens) at rest. |
| @flinkk/shared-auth/standalone-tenant-config | getStandaloneTenantId() — the one fixed tenant id for DEPLOYMENT_MODE=standalone deployments. |
| @flinkk/shared-auth/utils/field-permissions-utils | getFieldPermissionsFromSession(entityType) / getFieldPermissions(entityType, org) — resolves per-tenant custom-field view/edit permissions. |
| @flinkk/shared-auth/utils/hash-password | Password hashing/verification (bcrypt). |
| @flinkk/shared-auth/services/createUser | Shared user-creation flow (used by signup and invite-acceptance). |
| @flinkk/shared-auth/components/session-wrapper | <SessionWrapper> — client-side SessionProvider wrapper apps use in their root layout. |
| @flinkk/shared-auth/report-edge-identity | Identity reporting for edge-runtime requests (middleware). |
Tenant resolution
TenantResolver is the single point of access for "which tenant applies here" — every module that needs one goes through this instead of resolving it independently:
export interface TenantResolver {
resolveForUser(userId: string): Promise<ResolvedMembership | null>;
resolveForTenant(userId: string, candidateTenantId: string): Promise<ResolvedMembership | null>;
allowsTenantSwitching(): boolean;
isTenantAllowed(candidateTenantId: string): Promise<boolean>;
}MultiTenantResolver is used unless DEPLOYMENT_MODE=standalone. Under a saas-standalone license it still restricts resolveForUser/resolveForTenant/isTenantAllowed to the license's allowed-tenant list even though membership alone would otherwise be enough — a user can have an active membership in a tenant this deployment isn't licensed to serve at all. StandaloneTenantResolver ignores whatever tenant is asked for and always resolves to the license's one fixed tenant.
All of this is wired into authOptions's jwt callback in options.ts, so it runs automatically on every session lookup — callers don't need to call the resolver directly in normal request handling.
Usage
// options.ts (already wired) - shown for reference
import { getTenantResolver } from "@flinkk/shared-auth/tenant-resolver";
const resolver = getTenantResolver();
const membership = await resolver.resolveForUser(userId);import { getServerSession } from "@flinkk/shared-auth/server-session";
const { tenantId, userId } = await getServerSession();Environment variables
NEXTAUTH_SECRET,NEXTAUTH_URL— standard NextAuth config.DEPLOYMENT_MODE—"standalone"selectsStandaloneTenantResolver; anything else (including unset) usesMultiTenantResolver.
License
ISC License - Internal Flinkk library