Circle V2 API Docs
    Preparing search index...

    Module @repo/auth

    @repo/auth

    @repo/auth is the server-side authentication layer. It provides getUserFromToken to resolve a JWT into a User object via @repo/db repos, an abstract BaseAuthClient with a concrete SupabaseAuthClient implementation, shared auth types, and auth-specific error classes. This package is framework-agnostic -- see @repo/auth-next for Next.js integration.

    %%{init:{"theme":"dark"}}%% graph TD auth["@repo/auth"] --> db["@repo/db"] auth --> safe["@repo/safe"] auth --> logger["@repo/logger"] auth --> errors["@repo/errors"] auth -.-> typescript_config["@repo/typescript-config"] auth -.-> vitest_config["@repo/vitest-config"]
    %%{init:{"theme":"default"}}%% graph TD auth["@repo/auth"] --> db["@repo/db"] auth --> safe["@repo/safe"] auth --> logger["@repo/logger"] auth --> errors["@repo/errors"] auth -.-> typescript_config["@repo/typescript-config"] auth -.-> vitest_config["@repo/vitest-config"]
    graph TD
      auth["@repo/auth"] --> db["@repo/db"]
      auth --> safe["@repo/safe"]
      auth --> logger["@repo/logger"]
      auth --> errors["@repo/errors"]
      auth -.-> typescript_config["@repo/typescript-config"]
      auth -.-> vitest_config["@repo/vitest-config"]
    Import Resolves to Description
    @repo/auth src/index.ts getUserFromToken
    @repo/auth/types src/types.ts User, OAuthTokens, AuthToken, RefreshToken, SupabaseToken
    @repo/auth/errors src/errors.ts UnhandledAuthError, UnhandledSupabaseAuthError, UnauthorizedError
    @repo/auth/clients/base src/clients/base.auth-client.ts BaseAuthClient abstract class
    @repo/auth/clients/supabase src/clients/supabase.auth-client.ts SupabaseAuthClient implementation
    %%{init:{"theme":"dark"}}%% sequenceDiagram participant Caller participant Auth as getUserFromToken participant JWT as decodeJwt participant DB as "@repo/db repos"

    Caller->>Auth: getUserFromToken(token) Auth->>JWT: decodeJwt(token) JWT-->>Auth: { sub: UserId, exp } par Parallel fetches Auth->>DB: profilesRepo.getByAuthUserId(sub) Auth->>DB: userEntitiesRepo.getByUserId(sub) end DB-->>Auth: profile, userEntity Auth->>DB: entitiesRepo.getById(userEntity.entity_id) DB-->>Auth: entity Auth-->>Caller: User object

    %%{init:{"theme":"default"}}%% sequenceDiagram participant Caller participant Auth as getUserFromToken participant JWT as decodeJwt participant DB as "@repo/db repos"

    Caller->>Auth: getUserFromToken(token) Auth->>JWT: decodeJwt(token) JWT-->>Auth: { sub: UserId, exp } par Parallel fetches Auth->>DB: profilesRepo.getByAuthUserId(sub) Auth->>DB: userEntitiesRepo.getByUserId(sub) end DB-->>Auth: profile, userEntity Auth->>DB: entitiesRepo.getById(userEntity.entity_id) DB-->>Auth: entity Auth-->>Caller: User object

    sequenceDiagram
    participant Caller
    participant Auth as getUserFromToken
    participant JWT as decodeJwt
    participant DB as "@repo/db repos"

    Caller->>Auth: getUserFromToken(token) Auth->>JWT: decodeJwt(token) JWT-->>Auth: { sub: UserId, exp } par Parallel fetches Auth->>DB: profilesRepo.getByAuthUserId(sub) Auth->>DB: userEntitiesRepo.getByUserId(sub) end DB-->>Auth: profile, userEntity Auth->>DB: entitiesRepo.getById(userEntity.entity_id) DB-->>Auth: entity Auth-->>Caller: User object

    The function decodes the JWT payload (base64, no signature verification -- Supabase handles that), then fetches the user's profile and organization in parallel from @repo/db. It uses unwrap() from @repo/safe, so any repo error propagates as a thrown exception.

    import { getUserFromToken } from "@repo/auth";

    // Typically called from @repo/auth-next's session layer, not directly
    const user = await getUserFromToken(token);
    // user: { id, email, first_name, last_name, title, user_id, entity_id, entity_name }

    An abstract class defining the auth provider interface:

    import type { BaseAuthClient } from "@repo/auth/clients/base";
    
    Method Description
    signUpWithEmailAndPassword(email, password) Register a new user
    signInWithPassword(email, password) Sign in, returns OAuthTokens
    signInWithGoogle() Initiate Google OAuth, returns { url }
    signInWithOTP(params) Verify an OTP code, returns OAuthTokens
    exchangeCodeForSession(code) Exchange OAuth/reset code for session tokens
    passwordReset(email) Send a password reset email
    passwordUpdate(password) Update the current user's password

    The concrete implementation using @supabase/ssr. It creates a Supabase server client with cookie-based session management and PKCE flow.

    import { SupabaseAuthClient } from "@repo/auth/clients/supabase";

    const client = new SupabaseAuthClient({
    cookies: async () => cookieStore,
    oauthRedirectUrl: "https://myapp.com/api/auth/oauth",
    passwordResetRedirectUrl: "https://myapp.com/api/auth/reset-password",
    });

    const tokens = await client.signInWithPassword("user@example.com", "password");
    // tokens: { accessToken: AuthToken, refreshToken: RefreshToken }

    To use a different auth provider, extend BaseAuthClient:

    import { BaseAuthClient } from "@repo/auth/clients/base";
    import type { OAuthTokens } from "@repo/auth/types";

    class Auth0Client extends BaseAuthClient {
    async signUpWithEmailAndPassword(email: string, password: string): Promise<void> {
    // Call Auth0 Management API
    }

    async signInWithPassword(email: string, password: string): Promise<OAuthTokens> {
    // Call Auth0 Authentication API
    return { accessToken, refreshToken };
    }

    async signInWithGoogle(): Promise<{ url: string }> {
    // Return Auth0 universal login URL with Google connection
    return { url: "https://your-tenant.auth0.com/authorize?..." };
    }

    async signInWithOTP(params: VerifyOtpParams): Promise<OAuthTokens> { /* ... */ }
    async exchangeCodeForSession(code: string): Promise<OAuthTokens> { /* ... */ }
    async passwordReset(email: string): Promise<void> { /* ... */ }
    async passwordUpdate(password: string): Promise<void> { /* ... */ }
    }

    The custom client can then be passed to any @repo/auth-next request handler factory, since they all accept BaseAuthClient.

    All auth errors extend CircleError with domain: "auth".

    Error Code Description
    UnhandledAuthError UNHANDLED_AUTH_ERROR Catch-all for unexpected auth failures
    UnhandledSupabaseAuthError (from Supabase) Wraps Supabase AuthError with its code and status
    UnauthorizedError UNAUTHORIZED_ERROR User is not authenticated or lacks permissions
    import { UnauthorizedError } from "@repo/auth/errors";

    throw new UnauthorizedError("Token expired");
    Type Description
    User Profile fields (id, email, first_name, last_name, title) plus user_id, entity_id, entity_name
    OAuthTokens { accessToken: AuthToken, refreshToken: RefreshToken }
    AuthToken Branded string for access tokens
    RefreshToken Branded string for refresh tokens
    SupabaseToken Branded string for Supabase-specific tokens (v1 compatibility)
    import type { User, OAuthTokens, AuthToken } from "@repo/auth/types";
    
    Script Description
    test Runs Vitest with coverage.
    test:watch Runs Vitest in watch mode.
    check-types Runs tsc --noEmit to typecheck the package.

    Modules

    clients/base
    clients/supabase
    errors
    types