derp.auth – Authentication

Derp Auth - Authentication library for FastAPI applications.

class derp.auth.AuthConfig[source]

Bases: _StrictModel

Auth configuration — exactly one backend must be set.

native: NativeAuthConfig | None
supabase: SupabaseConfig | None
workos: WorkOSConfig | None
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.EmailConfig[source]

Bases: _StrictModel

Configuration for email sending via SMTP.

site_name: str
site_url: str
from_email: str
smtp_host: str
smtp_port: int
smtp_user: str
smtp_password: str
templates_dir: str | None
use_tls: bool
start_tls: bool
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.GitHubOAuthConfig[source]

Bases: _StrictModel

Configuration for GitHub OAuth.

client_id: str
client_secret: str
redirect_uri: str
scopes: Sequence[str]
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.GoogleOAuthConfig[source]

Bases: _StrictModel

Configuration for Google OAuth.

client_id: str
client_secret: str
redirect_uri: str
scopes: Sequence[str]
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.JWTConfig[source]

Bases: _StrictModel

Configuration for JWT tokens.

secret: str
algorithm: str
access_token_expire_minutes: int
refresh_token_expire_days: int
issuer: str | None
audience: str | None
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.NativeAuthConfig[source]

Bases: _StrictModel

Configuration for native authentication (email/password, magic link, OAuth).

jwt: JWTConfig
password: PasswordConfig
google_oauth: GoogleOAuthConfig | None
github_oauth: GitHubOAuthConfig | None
enable_signup: bool
enable_confirmation: bool
enable_magic_link: bool
magic_link_expire_minutes: int
recovery_token_expire_minutes: int
confirmation_token_expire_hours: int
session_expire_days: int
use_kv_cache: bool
cache_prefix: str
cache_session_ttl_seconds: int
cache_user_ttl_seconds: int
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.PasswordConfig[source]

Bases: _StrictModel

Configuration for password validation.

min_length: int
max_length: int
require_uppercase: bool
require_lowercase: bool
require_digit: bool
require_special: bool
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.SupabaseConfig[source]

Bases: _StrictModel

Configuration for Supabase GoTrue authentication.

url: str
anon_key: str
service_role_key: str
jwt_secret: str
redirect_uri: str | None
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.WorkOSConfig[source]

Bases: _StrictModel

Configuration for WorkOS authentication.

api_key: str
client_id: str
redirect_uri: str | None
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

exception derp.auth.AuthError[source]

Bases: Exception

Base exception for all auth errors.

__init__(message, code=None)[source]
Parameters:
  • message (str)

  • code (str | None)

exception derp.auth.ConfirmationURLMissingError[source]

Bases: AuthError

Raised when a confirmation URL is missing.

__init__(message='Confirmation URL is missing')[source]
Parameters:

message (str)

exception derp.auth.EmailSendError[source]

Bases: AuthError

Raised when sending an email fails.

__init__(message='Failed to send email')[source]
Parameters:

message (str)

exception derp.auth.OrgMismatchError[source]

Bases: AuthError

Raised when assert_same_org finds the session belongs to a different org.

Used as the default tenant-scoping failure so handlers can map it to a 403 (or whatever their framework prefers) without inspecting strings.

__init__(message='Session does not belong to this organization')[source]
Parameters:

message (str)

exception derp.auth.PasswordValidationError[source]

Bases: AuthError

Raised when a password fails validation.

__init__(message='Password does not meet requirements')[source]
Parameters:

message (str)

exception derp.auth.SignupDisabledError[source]

Bases: AuthError

Raised when signup is disabled.

__init__(message='Signup is currently disabled')[source]
Parameters:

message (str)

class derp.auth.TokenPair[source]

Bases: object

Access and refresh token pair.

access_token: str
refresh_token: str
token_type: str = 'bearer'
expires_in: int = 0
expires_at: datetime
__init__(*, access_token, refresh_token, token_type='bearer', expires_in=0, expires_at)
Parameters:
Return type:

None

class derp.auth.TokenPayload[source]

Bases: object

Payload data from a decoded JWT token.

sub: str
session_id: str
exp: datetime
iat: datetime
iss: str | None = None
aud: str | None = None
extra: dict[str, Any] | None = None
__init__(*, sub, session_id, exp, iat, iss=None, aud=None, extra=None)
Parameters:
Return type:

None

class derp.auth.AuthResult[source]

Bases: object

Result of a sign-up or sign-in operation.

user: UserInfo
tokens: TokenPair
__init__(*, user, tokens)
Parameters:
  • user (UserInfo)

  • tokens (TokenPair)

Return type:

None

class derp.auth.CursorResult[source]

Bases: Generic

Cursor-paginated result.

data: list[T]
has_more: bool
next_cursor: str | None = None
__init__(*, data, has_more, next_cursor=None)
Parameters:
  • data (list[T])

  • has_more (bool)

  • next_cursor (str | None)

Return type:

None

class derp.auth.AuthOrgMember[source]

Bases: Table

Organization membership table (native auth — FK to AuthUser).

id: UUID

UUID type.

org_id: UUID

UUID type.

user_id: UUID

UUID type.

role: Varchar
created_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

updated_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

classmethod indexes()[source]

Override to define indexes for this table.

Return type:

list[Index]

class derp.auth.AuthOrganization[source]

Bases: Table

Organization table for multi-tenancy.

id: UUID

UUID type.

name: Varchar
slug: Varchar
metadata: Nullable_JSONB
created_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

updated_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

classmethod indexes()[source]

Override to define indexes for this table.

Return type:

list[Index]

class derp.auth.AuthProvider[source]

Bases: StrEnum

Authentication provider types.

EMAIL = 'email'
MAGIC_LINK = 'magic_link'
GOOGLE = 'google'
GITHUB = 'github'
__new__(value)
class derp.auth.AuthRequest[source]

Bases: Protocol

Protocol for objects that carry HTTP headers (e.g. FastAPI Request).

property headers: Mapping[str, str]
__init__(*args, **kwargs)
class derp.auth.AuthSession[source]

Bases: Table

Authentication session table with integrated refresh tokens.

Each row represents a refresh token. Rows sharing the same session_id belong to the same logical session (one login event). Token rotation inserts a new row and revokes the old one.

id: UUID

UUID type.

user_id: UUID

UUID type.

session_id: UUID

UUID type.

token: Varchar
role: Varchar
revoked: Boolean

Boolean type (BOOLEAN).

user_agent: Nullable_Text
ip_address: Nullable_Varchar
org_id: Nullable_UUID
not_after: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

created_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

classmethod indexes()[source]

Override to define indexes for this table.

Return type:

list[Index]

class derp.auth.AuthUser[source]

Bases: Table

User authentication table.

id: UUID

UUID type.

email: Varchar
email_confirmed_at: Nullable_TimestampTZ
encrypted_password: Nullable_Text
first_name: Nullable_Varchar
last_name: Nullable_Varchar
username: Nullable_Varchar
image_url: Nullable_Text
provider: Enum
provider_id: Nullable_Varchar
is_active: Boolean

Boolean type (BOOLEAN).

is_superuser: Boolean

Boolean type (BOOLEAN).

role: Varchar
created_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

updated_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

last_sign_in_at: Nullable_TimestampTZ
classmethod indexes()[source]

Override to define indexes for this table.

Return type:

list[Index]

class derp.auth.OrgInfo[source]

Bases: BaseModel

Unified organization information returned by all auth backends.

id: str
name: str
slug: str
metadata: dict[str, Any]
created_at: datetime
updated_at: datetime
model_config = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.OrgMemberInfo[source]

Bases: BaseModel

Unified organization membership information.

org_id: str
user_id: str
role: str
created_at: datetime
updated_at: datetime
model_config = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.SessionInfo[source]

Bases: BaseModel

Unified session information returned by authenticate.

user_id: str
session_id: str
role: str
expires_at: datetime
metadata: dict[str, Any]
org_id: str | None
org_role: str | None
model_config = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.UserInfo[source]

Bases: BaseModel

Unified user information returned by all auth backends.

id: str
email: str
first_name: str | None
last_name: str | None
username: str | None
image_url: str | None
role: str
is_active: bool
is_superuser: bool
email_confirmed_at: datetime | None
last_sign_in_at: datetime | None
created_at: datetime
updated_at: datetime
metadata: dict[str, Any]
model_config = {'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class derp.auth.Argon2Hasher[source]

Bases: PasswordHasher

Password hasher using Argon2id (recommended).

__init__(time_cost=3, memory_cost=65536, parallelism=4, *, max_workers=8)[source]
Parameters:
  • time_cost (int)

  • memory_cost (int)

  • parallelism (int)

  • max_workers (int)

hash(password)[source]

Hash a password using Argon2id.

Parameters:

password (str)

Return type:

str

verify(password, hashed)[source]

Verify a password against an Argon2 hash.

Parameters:
Return type:

bool

needs_rehash(hashed)[source]

Check if the hash needs to be updated with new parameters.

Parameters:

hashed (str)

Return type:

bool

class derp.auth.PasswordHasher[source]

Bases: ABC

Abstract base class for password hashers.

__init__(*, max_workers=8)[source]
Parameters:

max_workers (int)

Return type:

None

abstractmethod hash(password)[source]

Hash a password synchronously.

Parameters:

password (str)

Return type:

str

abstractmethod verify(password, hashed)[source]

Verify a password synchronously.

Parameters:
Return type:

bool

abstractmethod needs_rehash(hashed)[source]

Check if a hash needs to be rehashed (e.g., algorithm upgrade).

Parameters:

hashed (str)

Return type:

bool

async async_hash(password)[source]

Hash a password without blocking the event loop.

Parameters:

password (str)

Return type:

str

async async_verify(password, hashed)[source]

Verify a password without blocking the event loop.

Parameters:
Return type:

bool

class derp.auth.PasswordValidationResult[source]

Bases: object

Result of password validation.

valid: bool
errors: list[str]
__init__(valid, errors)
Parameters:
Return type:

None

derp.auth.generate_secure_token(length=32)[source]

Generate a cryptographically secure random token.

Parameters:

length (int)

Return type:

str

class derp.auth.BaseOAuthProvider[source]

Bases: ABC, Generic

Abstract base class for OAuth providers.

provider_name: str
__init__(config)[source]
Parameters:

config (ConfigT)

abstractmethod get_authorization_url(state, scopes=None, redirect_uri=None)[source]

Generate the OAuth authorization URL.

Parameters:
  • state (str) – CSRF protection state token

  • scopes (list[str] | None) – Optional list of scopes to request

  • redirect_uri (str | None) – Optional override for redirect URI

Returns:

Authorization URL to redirect the user to

Return type:

str

abstractmethod async exchange_code(code, redirect_uri=None)[source]

Exchange authorization code for tokens.

Parameters:
  • code (str) – Authorization code from callback

  • redirect_uri (str | None) – Redirect URI (must match authorization request)

Returns:

OAuthTokens with access token and optional refresh token

Return type:

OAuthTokens | None

abstractmethod async get_user_info(access_token)[source]

Get user information from the provider.

Parameters:

access_token (str) – Access token from token exchange

Returns:

OAuthUserInfo with user details

Return type:

OAuthUserInfo | None

async authenticate(code, redirect_uri=None)[source]

Complete OAuth flow: exchange code and get user info.

Returns None if the provider request fails.

Parameters:
  • code (str)

  • redirect_uri (str | None)

Return type:

OAuthUserInfo | None

class derp.auth.GitHubProvider[source]

Bases: BaseOAuthProvider[GitHubOAuthConfig]

GitHub OAuth provider.

provider_name: str = 'github'
AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize'
TOKEN_URL = 'https://github.com/login/oauth/access_token'
USERINFO_URL = 'https://api.github.com/user'
EMAILS_URL = 'https://api.github.com/user/emails'
get_authorization_url(state, scopes=None, redirect_uri=None)[source]

Generate GitHub OAuth authorization URL.

Parameters:
  • state (str)

  • scopes (list[str] | None)

  • redirect_uri (str | None)

Return type:

str

async exchange_code(code, redirect_uri=None)[source]

Exchange authorization code for GitHub tokens.

Parameters:
  • code (str)

  • redirect_uri (str | None)

Return type:

OAuthTokens | None

async get_user_info(access_token)[source]

Get user info from GitHub.

Parameters:

access_token (str)

Return type:

OAuthUserInfo | None

class derp.auth.GoogleProvider[source]

Bases: BaseOAuthProvider[GoogleOAuthConfig]

Google OAuth 2.0 provider.

provider_name: str = 'google'
AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
TOKEN_URL = 'https://oauth2.googleapis.com/token'
USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo'
get_authorization_url(state, scopes=None, redirect_uri=None)[source]

Generate Google OAuth authorization URL.

Parameters:
  • state (str)

  • scopes (list[str] | None)

  • redirect_uri (str | None)

Return type:

str

async exchange_code(code, redirect_uri=None)[source]

Exchange authorization code for Google tokens.

Parameters:
  • code (str)

  • redirect_uri (str | None)

Return type:

OAuthTokens | None

async get_user_info(access_token)[source]

Get user info from Google.

Parameters:

access_token (str)

Return type:

OAuthUserInfo | None

class derp.auth.OAuthUserInfo[source]

Bases: object

User information retrieved from OAuth provider.

id: str
email: str
email_verified: bool = False
name: str | None = None
picture: str | None = None
raw_data: dict[str, Any] | None = None
__init__(id, email, email_verified=False, name=None, picture=None, raw_data=None)
Parameters:
Return type:

None

class derp.auth.EmailClient[source]

Bases: object

Async SMTP client for sending emails.

__init__(config)[source]
Parameters:

config (EmailConfig)

Return type:

None

async send_email(*, subject, to_email, template, fallback_text=None, **kwargs)[source]

Send an email.

Parameters:
  • subject (str) – Email subject.

  • to_email (str) – Recipient email address.

  • template (str) – Template name.

  • fallback_text (str | None) – Fallback text content.

  • **kwargs (Any) – Template variables.

Raises:

EmailSendError – If sending fails

Return type:

None

class derp.auth.BaseAuthClient[source]

Bases: ABC

Abstract base authentication client.

Defines the full interface shared by all auth backends (native, Supabase, WorkOS). Core methods are abstract; optional methods raise NotImplementedError by default.

async connect()[source]

Initialize backend-specific connections.

Return type:

None

async disconnect()[source]

Close backend-specific connections.

Return type:

None

set_db(db)[source]

Set the database client.

Parameters:

db (DatabaseEngine | None)

Return type:

None

set_kv(kv)[source]

Set the KV store for caching and token storage.

Parameters:

kv (KVClient | None)

Return type:

None

set_email(email_client)[source]

Set the email client.

Parameters:

email_client (EmailClient | None)

Return type:

None

abstractmethod async get_user(user_id)[source]

Get a user by their ID.

Raises:

UserNotFoundError – No user with that id.

Parameters:

user_id (str | UUID)

Return type:

UserInfo

abstractmethod async list_users(*, limit=None, offset=None)[source]

List users.

Parameters:
  • limit (int | None)

  • offset (int | None)

Return type:

list[UserInfo]

abstractmethod async update_user(*, user_id, email=None, **kwargs)[source]

Update user data.

Raises:

UserNotFoundError – No user with that id.

Parameters:
Return type:

UserInfo

abstractmethod async delete_user(user_id)[source]

Delete a user and all their sessions. Returns False if not found.

Parameters:

user_id (str | UUID)

Return type:

bool

abstractmethod async count_users()[source]

Return the total number of users.

Return type:

int

abstractmethod async authenticate(request)[source]

Authenticate a request and return session info.

Parameters:

request (AuthRequest)

Return type:

SessionInfo | None

abstractmethod async list_sessions(*, user_id=None, limit=None, offset=None)[source]

List active sessions, optionally filtered by user.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[SessionInfo]

abstractmethod async sign_out(session_id)[source]

Sign out by revoking a session.

Parameters:

session_id (str | UUID)

Return type:

None

abstractmethod async sign_out_all(user_id)[source]

Sign out all sessions for a user.

Parameters:

user_id (str | UUID)

Return type:

None

is_authorized(session, *roles)[source]

Check if the session’s role is in the allowed set.

Parameters:
  • session (SessionInfo)

  • roles (str)

Return type:

bool

async sign_up(*, email, password, request=None, confirmation_url=None, confirmation_subject='Confirm your email address', user_agent=None, ip_address=None, **kwargs)[source]

Register a new user with email and password.

If request is provided, user_agent and ip_address are extracted automatically when not explicitly given.

Raises:
  • EmailAlreadyExistsError – An account with this email exists.

  • PasswordValidationError – Password did not meet requirements.

  • SignupDisabledError – Signup is turned off via config.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • confirmation_url (str | None)

  • confirmation_subject (str)

  • user_agent (str | None)

  • ip_address (str | None)

  • kwargs (Any)

Return type:

AuthResult

async sign_in_with_password(email, password, *, request=None, first_name=None, last_name=None, user_agent=None, ip_address=None)[source]

Sign in with email and password.

Raises:

InvalidCredentialsError – Email or password did not match.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • first_name (str | None)

  • last_name (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async sign_in_with_magic_link(*, email, magic_link_url)[source]

Send a magic link email for passwordless sign in.

Parameters:
  • email (str)

  • magic_link_url (str)

Return type:

None

async verify_magic_link(token, *, user_agent=None, ip_address=None)[source]

Verify a magic link and sign in.

Raises:

InvalidTokenError – Token is unknown, expired, or already used.

Parameters:
  • token (str)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

get_oauth_provider(provider)[source]

Get an OAuth provider by name.

Parameters:

provider (str | AuthProvider)

Return type:

BaseOAuthProvider

get_oauth_authorization_url(provider, state, scopes=None, redirect_uri=None)[source]

Get the OAuth authorization URL for a provider.

Parameters:
  • provider (str | AuthProvider)

  • state (str)

  • scopes (list[str] | None)

  • redirect_uri (str | None)

Return type:

str

async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]

Complete OAuth sign in with authorization code.

Raises:

InvalidCredentialsError – Provider rejected the code or user lookup failed.

Parameters:
  • provider (str | AuthProvider)

  • code (str)

  • redirect_uri (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async refresh_token(refresh_token)[source]

Refresh an access token using a refresh token.

Raises:

InvalidTokenError – Refresh token is unknown, expired, or revoked.

Parameters:

refresh_token (str)

Return type:

TokenPair

async request_password_recovery(*, email, recovery_url, recovery_subject='Reset your password', **kwargs)[source]

Send a password recovery email.

Parameters:
  • email (str)

  • recovery_url (str)

  • recovery_subject (str)

  • kwargs (Any)

Return type:

None

async reset_password(token, new_password)[source]

Reset password using recovery token.

Raises:
  • InvalidTokenError – Recovery token is unknown, expired, or used.

  • PasswordValidationError – New password did not meet requirements.

Parameters:
  • token (str)

  • new_password (str)

Return type:

UserInfo

async confirm_email(token)[source]

Confirm email address with token.

Raises:

InvalidTokenError – Confirmation token is unknown, expired, or used.

Parameters:

token (str)

Return type:

UserInfo

async resend_confirmation_email(*, email, confirmation_url, confirmation_subject='Confirm your email address', **kwargs)[source]

Resend email confirmation.

Parameters:
  • email (str)

  • confirmation_url (str)

  • confirmation_subject (str)

  • kwargs (Any)

Return type:

None

async create_org(*, name, slug, creator_id, **kwargs)[source]

Create an organization. The creator is added as owner.

Raises:

OrgSlugConflictError – Slug is already taken.

Parameters:
Return type:

OrgInfo

async get_org(*, org_id=None, slug=None)[source]

Get an organization by ID or slug. Provide exactly one.

Raises:

OrgNotFoundError – No org matches the given id or slug.

Parameters:
Return type:

OrgInfo

async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]

Update an organization.

Identify the org by org_id OR org_slug (exactly one). The slug kwarg sets the org’s new slug — it does NOT identify the target. This is the one place slug= means a value rather than a lookup, since slug is updateable.

Raises:
  • OrgNotFoundError – No org matches the identifier.

  • OrgSlugConflictError – New slug is already taken.

Parameters:
  • org_id (str | UUID | None)

  • org_slug (str | None)

  • name (str | None)

  • slug (str | None)

  • kwargs (Any)

Return type:

OrgInfo

async delete_org(*, org_id=None, slug=None)[source]

Delete an organization and all its memberships.

Identify by org_id or slug. Returns False if not found.

Parameters:
Return type:

bool

async list_orgs(*, user_id=None, limit=None, offset=None)[source]

List organizations, optionally filtered by user membership.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgInfo]

async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]

Add a user to an organization (identify by org_id or slug).

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • MemberAlreadyExistsError – User is already a member.

Parameters:
Return type:

OrgMemberInfo

async update_org_member(*, org_id=None, slug=None, user_id, role)[source]

Update a member’s role.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgMemberNotFoundError – User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async remove_org_member(*, org_id=None, slug=None, user_id)[source]

Remove a user from an organization.

Returns False if the org or membership does not exist.

Raises:

LastOwnerError – Removing this member would leave the org without an owner.

Parameters:
Return type:

bool

async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]

List members of an organization (identify by org_id or slug).

Raises:

OrgNotFoundError – Org identifier did not resolve.

Parameters:
  • org_id (str | UUID | None)

  • slug (str | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgMemberInfo]

async get_org_member(*, org_id=None, slug=None, user_id)[source]

Get a single membership record.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgMemberNotFoundError – User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async list_users_by_cursor(*, limit=10, after=None)[source]

List users with cursor-based pagination.

Parameters:
  • limit (int)

  • after (str | None)

Return type:

CursorResult[UserInfo]

async list_sessions_by_cursor(*, user_id, limit=10, after=None)[source]

List sessions with cursor-based pagination.

Parameters:
Return type:

CursorResult[Any]

async list_orgs_by_cursor(*, user_id=None, limit=10, after=None)[source]

List organizations with cursor-based pagination.

Parameters:
Return type:

CursorResult[OrgInfo]

async list_org_members_by_cursor(*, org_id=None, slug=None, limit=10, after=None)[source]

List organization members with cursor-based pagination.

Identify the org by org_id or slug.

Parameters:
Return type:

CursorResult[OrgMemberInfo]

async set_active_org(*, session_id, org_id)[source]

Switch the active organization for a session.

Pass org_id=None to clear the active org (sign out of org context).

Raises:

OrgMemberNotFoundError – User is not a member of the target org.

Parameters:
Return type:

TokenPair

is_org_authorized(session, org_id, *roles)[source]

Check if the session has an active org with an allowed role.

Parameters:
  • session (SessionInfo)

  • org_id (str)

  • roles (str)

Return type:

bool

is_same_org(session, org_id)[source]

True iff the session’s active org matches org_id.

Tenant-scoping check with no role gating — use this in request handlers that do their own role logic, or just need to confirm the resource belongs to the caller’s org.

Parameters:
  • session (SessionInfo)

  • org_id (str | UUID)

Return type:

bool

assert_same_org(session, org_id)[source]

Raise OrgMismatchError if the session’s org does not match.

The framework-recommended one-liner for tenant scoping at the top of an endpoint:

session = await derp.auth.authenticate(request) derp.auth.assert_same_org(session, resource.org_id)

Both sides hold the same id type (no translation step), so the comparison is correct by construction. Catch OrgMismatchError and map it to whatever HTTP/RPC error your framework expects.

Parameters:
  • session (SessionInfo)

  • org_id (str | UUID)

Return type:

None

class derp.auth.NativeAuthClient[source]

Bases: BaseAuthClient

Native authentication client (email/password, magic link, OAuth).

__init__(config)[source]
Parameters:

config (NativeAuthConfig)

set_db(db)[source]

Set the database client.

Parameters:

db (DatabaseEngine | None)

Return type:

None

set_kv(kv)[source]

Set the KV store for caching and token storage.

Parameters:

kv (KVClient | None)

Return type:

None

set_email(email_client)[source]

Set the email client.

Parameters:

email_client (EmailClient | None)

Return type:

None

async get_user(user_id)[source]

Get a user by their ID.

Raises:

UserNotFoundError – No user with that id.

Parameters:

user_id (str | UUID)

Return type:

UserInfo

async list_users(*, limit=None, offset=None)[source]

List users ordered by creation date (newest first).

Parameters:
  • limit (int | None)

  • offset (int | None)

Return type:

list[UserInfo]

async update_user(*, user_id, email=None, **kwargs)[source]

Update user data.

Raises:

UserNotFoundError – No user with that id.

Parameters:
Return type:

UserInfo

async delete_user(user_id)[source]

Delete a user and all their sessions.

Parameters:

user_id (str | UUID)

Return type:

bool

async count_users()[source]

Return the total number of users.

Return type:

int

async sign_up(*, email, password, request=None, confirmation_url=None, confirmation_subject='Confirm your email address', user_agent=None, ip_address=None, **kwargs)[source]

Register a new user with email and password.

Raises:
  • SignupDisabledError – Signup is disabled in config.

  • ConfirmationURLMissingError – confirmation_url omitted while enable_confirmation is on.

  • PasswordValidationError – Password did not meet requirements.

  • EmailAlreadyExistsError – An account with this email exists.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • confirmation_url (str | None)

  • confirmation_subject (str)

  • user_agent (str | None)

  • ip_address (str | None)

  • kwargs (Any)

Return type:

AuthResult

async sign_in_with_password(email, password, *, request=None, first_name=None, last_name=None, user_agent=None, ip_address=None)[source]

Sign in with email and password.

Raises:

InvalidCredentialsError – Email/password did not match, or the account is disabled / unconfirmed. The reason is logged at WARNING for ops; the caller-visible error is opaque to avoid email-enumeration leaks.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • first_name (str | None)

  • last_name (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async sign_in_with_magic_link(*, email, magic_link_url)[source]

Send a magic link email for passwordless sign in.

Creates user if they don’t exist (if signup enabled).

Raises:

ValueError – If magic link authentication is not enabled

Parameters:
  • email (str)

  • magic_link_url (str)

Return type:

None

async verify_magic_link(token, *, user_agent=None, ip_address=None)[source]

Verify a magic link and sign in.

Raises:

InvalidTokenError – Token is unknown, expired, already used, or points at a missing/disabled account. Specific reason is logged at WARNING for ops.

Parameters:
  • token (str)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

get_oauth_provider(provider)[source]

Get an OAuth provider by name.

Parameters:

provider (str | AuthProvider)

Return type:

BaseOAuthProvider

get_oauth_authorization_url(provider, state, scopes=None, redirect_uri=None)[source]

Get the OAuth authorization URL for a provider.

Parameters:
  • provider_name – Name of the OAuth provider

  • state (str) – CSRF protection state token

  • scopes (list[str] | None) – Optional scopes to request

  • redirect_uri (str | None) – Optional redirect URI override

  • provider (str | AuthProvider)

Returns:

Authorization URL

Return type:

str

async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]

Complete OAuth sign in with authorization code.

Creates the user record if this email has never signed in before.

Raises:

InvalidCredentialsError – Provider rejected the code, or the matching account is disabled.

Parameters:
  • provider (str | AuthProvider)

  • code (str)

  • redirect_uri (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async refresh_token(refresh_token)[source]

Refresh an access token using a refresh token.

Implements token rotation for security. Happy path is 2 DB calls: one UPDATE…RETURNING to atomically revoke the old token, one INSERT for the new token.

Raises:

InvalidTokenError – Token is unknown, revoked, reused, or expired. Reuse detection additionally revokes every token for the session before raising.

Parameters:

refresh_token (str)

Return type:

TokenPair

async authenticate(request)[source]

Authenticate a request via JWT (networkless).

Extracts the Bearer token from the Authorization header, decodes and verifies the JWT signature and expiry. Returns SessionInfo built from JWT claims, or None if the token is missing, invalid, or expired.

Parameters:

request (AuthRequest)

Return type:

SessionInfo | None

async list_sessions(*, user_id=None, limit=None, offset=None)[source]

List active (non-revoked) sessions ordered by creation date.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[SessionInfo]

async sign_out(session_id)[source]

Sign out by deleting all tokens for a session.

Parameters:

session_id (str | UUID)

Return type:

None

async sign_out_all(user_id)[source]

Sign out all sessions for a user by deleting all tokens.

Parameters:

user_id (str | UUID)

Return type:

None

async request_password_recovery(*, email, recovery_url, recovery_subject='Reset your password', **kwargs)[source]

Send a password recovery email.

Does not reveal whether user exists for security.

Parameters:
  • email (str)

  • recovery_url (str)

  • recovery_subject (str)

  • kwargs (Any)

Return type:

None

async reset_password(token, new_password)[source]

Reset password using recovery token.

Raises:
  • PasswordValidationError – New password did not meet requirements.

  • InvalidTokenError – Recovery token is unknown, expired, used, or points at a missing user.

Parameters:
  • token (str)

  • new_password (str)

Return type:

UserInfo

async confirm_email(token)[source]

Confirm email address with token.

Raises:

InvalidTokenError – Confirmation token is unknown, expired, used, or points at a missing user.

Parameters:

token (str)

Return type:

UserInfo

async resend_confirmation_email(*, email, confirmation_url, confirmation_subject='Confirm your email address', **kwargs)[source]

Resend email confirmation.

Does not reveal whether user exists for security.

Parameters:
  • email (str)

  • confirmation_url (str)

  • confirmation_subject (str)

  • kwargs (Any)

Return type:

None

async create_org(*, name, slug, creator_id, **kwargs)[source]

Create an organization. The creator is added as owner.

Raises:

OrgSlugConflictError – Slug is already taken.

Parameters:
Return type:

OrgInfo

async get_org(*, org_id=None, slug=None)[source]

Get an organization by ID or slug. Provide exactly one.

Raises:

OrgNotFoundError – No matching org.

Parameters:
Return type:

OrgInfo

async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]

Update an organization. Identify by org_id or org_slug.

slug is the new slug to assign — not the lookup key.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgSlugConflictError – New slug collides with another org.

Parameters:
  • org_id (str | UUID | None)

  • org_slug (str | None)

  • name (str | None)

  • slug (str | None)

  • kwargs (Any)

Return type:

OrgInfo

async delete_org(*, org_id=None, slug=None)[source]

Delete an organization and all its memberships.

Parameters:
Return type:

bool

async list_orgs(*, user_id=None, limit=None, offset=None)[source]

List organizations, optionally filtered by user membership.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgInfo]

async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]

Add a user to an organization (identify by org_id or slug).

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • MemberAlreadyExistsError – User is already a member.

Parameters:
Return type:

OrgMemberInfo

async update_org_member(*, org_id=None, slug=None, user_id, role)[source]

Update a member’s role.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgMemberNotFoundError – User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async remove_org_member(*, org_id=None, slug=None, user_id)[source]

Remove a user from an organization.

Returns False if the org or membership does not exist.

Raises:

LastOwnerError – Removing this member would leave the org without an owner.

Parameters:
Return type:

bool

async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]

List members of an organization (identify by org_id or slug).

Raises:

OrgNotFoundError – Org identifier did not resolve.

Parameters:
  • org_id (str | UUID | None)

  • slug (str | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgMemberInfo]

async get_org_member(*, org_id=None, slug=None, user_id)[source]

Get a single membership record.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgMemberNotFoundError – User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async set_active_org(*, session_id, org_id)[source]

Switch the active organization for a session.

Pass org_id=None to clear the active org.

Raises:
  • InvalidTokenError – Session id is unknown or already revoked.

  • OrgMemberNotFoundError – User is not a member of the target org.

Parameters:
Return type:

TokenPair

class derp.auth.SupabaseAuthClient[source]

Bases: BaseAuthClient

Supabase GoTrue-backed authentication client.

Delegates user management, sign-up, sign-in, and token operations to the Supabase GoTrue REST API via raw httpx calls. JWT verification is performed locally using the project’s JWT secret. Organizations are stored in the local database.

__init__(config)[source]
Parameters:

config (SupabaseConfig)

Return type:

None

async connect()[source]

Initialize backend-specific connections.

Return type:

None

async disconnect()[source]

Close backend-specific connections.

Return type:

None

set_db(db)[source]

Set the database client.

Parameters:

db (DatabaseEngine | None)

Return type:

None

async authenticate(request)[source]

Authenticate a request and return session info.

Parameters:

request (AuthRequest)

Return type:

SessionInfo | None

async get_user(user_id)[source]

Get a user by their ID.

Raises:

UserNotFoundError – No user with that id.

Parameters:

user_id (str | UUID)

Return type:

UserInfo

async list_users(*, limit=None, offset=None)[source]

List users.

Parameters:
  • limit (int | None)

  • offset (int | None)

Return type:

list[UserInfo]

async update_user(*, user_id, email=None, **kwargs)[source]

Update user data.

Raises:

UserNotFoundError – No user with that id.

Parameters:
Return type:

UserInfo

async delete_user(user_id)[source]

Delete a user and all their sessions. Returns False if not found.

Parameters:

user_id (str | UUID)

Return type:

bool

async count_users()[source]

Return the total number of users.

Return type:

int

async sign_up(*, email, password, request=None, confirmation_url=None, user_agent=None, ip_address=None, **kwargs)[source]

Register a new user with email and password.

If request is provided, user_agent and ip_address are extracted automatically when not explicitly given.

Raises:
  • EmailAlreadyExistsError – An account with this email exists.

  • PasswordValidationError – Password did not meet requirements.

  • SignupDisabledError – Signup is turned off via config.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • confirmation_url (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

  • kwargs (Any)

Return type:

AuthResult

async sign_in_with_password(email, password, *, request=None, first_name=None, last_name=None, user_agent=None, ip_address=None)[source]

Sign in with email and password.

Raises:

InvalidCredentialsError – Email or password did not match.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • first_name (str | None)

  • last_name (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async sign_in_with_magic_link(*, email, magic_link_url)[source]

Send a magic link email for passwordless sign in.

Parameters:
  • email (str)

  • magic_link_url (str)

Return type:

None

async verify_magic_link(token, *, user_agent=None, ip_address=None)[source]

Verify a magic link and sign in.

Raises:

InvalidTokenError – Token is unknown, expired, or already used.

Parameters:
  • token (str)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async refresh_token(refresh_token)[source]

Refresh an access token using a refresh token.

Raises:

InvalidTokenError – Refresh token is unknown, expired, or revoked.

Parameters:

refresh_token (str)

Return type:

TokenPair

async request_password_recovery(*, email, recovery_url='', recovery_subject='Reset your password', **kwargs)[source]

Send a password recovery email.

Parameters:
  • email (str)

  • recovery_url (str)

  • recovery_subject (str)

  • kwargs (Any)

Return type:

None

async reset_password(token, new_password)[source]

Reset password using recovery token.

Raises:
  • InvalidTokenError – Recovery token is unknown, expired, or used.

  • PasswordValidationError – New password did not meet requirements.

Parameters:
  • token (str)

  • new_password (str)

Return type:

UserInfo

async confirm_email(token)[source]

Confirm email address with token.

Raises:

InvalidTokenError – Confirmation token is unknown, expired, or used.

Parameters:

token (str)

Return type:

UserInfo

async list_sessions(*, user_id=None, limit=None, offset=None)[source]

List active sessions, optionally filtered by user.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[Any]

async sign_out(session_id)[source]

Sign out by revoking a session.

Parameters:

session_id (str | UUID)

Return type:

None

async sign_out_all(user_id)[source]

Sign out all sessions for a user.

Parameters:

user_id (str | UUID)

Return type:

None

get_oauth_authorization_url(provider, state, scopes=None, redirect_uri=None)[source]

Get the OAuth authorization URL for a provider.

Parameters:
  • provider (str | AuthProvider)

  • state (str)

  • scopes (list[str] | None)

  • redirect_uri (str | None)

Return type:

str

async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]

Complete OAuth sign in with authorization code.

Raises:

InvalidCredentialsError – Provider rejected the code or user lookup failed.

Parameters:
  • provider (str | AuthProvider)

  • code (str)

  • redirect_uri (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async create_org(*, name, slug, creator_id, **kwargs)[source]

Raises: OrgSlugConflictError: Slug is already taken.

Parameters:
Return type:

OrgInfo

async get_org(*, org_id=None, slug=None)[source]

Raises: OrgNotFoundError: No org matches the given id or slug.

Parameters:
Return type:

OrgInfo

async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]

Identify by org_id or org_slug; slug is the new value.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgSlugConflictError – New slug collides with another org.

Parameters:
  • org_id (str | UUID | None)

  • org_slug (str | None)

  • name (str | None)

  • slug (str | None)

  • kwargs (Any)

Return type:

OrgInfo

async delete_org(*, org_id=None, slug=None)[source]

Delete an organization and all its memberships.

Identify by org_id or slug. Returns False if not found.

Parameters:
Return type:

bool

async list_orgs(*, user_id=None, limit=None, offset=None)[source]

List organizations, optionally filtered by user membership.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgInfo]

async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]

Raises: OrgNotFoundError: Org identifier did not resolve. MemberAlreadyExistsError: User is already a member.

Parameters:
Return type:

OrgMemberInfo

async update_org_member(*, org_id=None, slug=None, user_id, role)[source]

Raises: OrgNotFoundError: Org identifier did not resolve. OrgMemberNotFoundError: User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async remove_org_member(*, org_id=None, slug=None, user_id)[source]

Returns False if the org or membership does not exist.

Raises:

LastOwnerError – Removing this member would leave the org without an owner.

Parameters:
Return type:

bool

async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]

Raises: OrgNotFoundError: Org identifier did not resolve.

Parameters:
  • org_id (str | UUID | None)

  • slug (str | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgMemberInfo]

async get_org_member(*, org_id=None, slug=None, user_id)[source]

Raises: OrgNotFoundError: Org identifier did not resolve. OrgMemberNotFoundError: User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async set_active_org(*, session_id, org_id)[source]

Raises: OrgMemberNotFoundError: User is not a member of the target org.

Parameters:
Return type:

TokenPair

class derp.auth.SupabaseOrgMember[source]

Bases: Table

Organization membership table (Supabase — no FK to users table).

id: UUID

UUID type.

org_id: UUID

UUID type.

user_id: UUID

UUID type.

role: Varchar
created_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

updated_at: TimestampTZ

Timestamp with timezone (TIMESTAMP WITH TIME ZONE).

classmethod indexes()[source]

Override to define indexes for this table.

Return type:

list[Index]

class derp.auth.WorkOSAuthClient[source]

Bases: BaseAuthClient

WorkOS-backed authentication client.

Delegates user management, sign-up, sign-in, and organization management to the WorkOS API. JWT verification is performed locally against the WorkOS JWKS endpoint.

WorkOS uses cursor-based pagination. The offset-based list_users, count_users, and list_org_members methods raise NotImplementedError — use the *_by_cursor variants instead.

__init__(config)[source]
Parameters:

config (WorkOSConfig)

Return type:

None

async connect()[source]

Create the WorkOS client connection.

Return type:

None

async disconnect()[source]

Close the underlying WorkOS HTTP client.

Return type:

None

set_db(db)[source]

Set the database client.

Parameters:

db (DatabaseEngine | None)

Return type:

None

async authenticate(request)[source]

Verify a WorkOS JWT from the Authorization header.

Parameters:

request (AuthRequest)

Return type:

SessionInfo | None

async get_user(user_id)[source]

Get a user by their ID.

Raises:

UserNotFoundError – No user with that id.

Parameters:

user_id (str | UUID)

Return type:

UserInfo

async list_users(*, limit=None, offset=None)[source]

List users.

Parameters:
  • limit (int | None)

  • offset (int | None)

Return type:

list[UserInfo]

async update_user(*, user_id, email=None, **kwargs)[source]

Update user data.

Raises:

UserNotFoundError – No user with that id.

Parameters:
Return type:

UserInfo

async delete_user(user_id)[source]

Delete a user and all their sessions. Returns False if not found.

Parameters:

user_id (str | UUID)

Return type:

bool

async count_users()[source]

Return the total number of users.

Return type:

int

async list_sessions(*, user_id=None, limit=None, offset=None)[source]

List active sessions, optionally filtered by user.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[SessionInfo]

async sign_out(session_id)[source]

Sign out by revoking a session.

Parameters:

session_id (str | UUID)

Return type:

None

async sign_out_all(user_id)[source]

Sign out all sessions for a user.

Parameters:

user_id (str | UUID)

Return type:

None

async sign_up(*, email, password, request=None, confirmation_url=None, confirmation_subject='Confirm your email address', user_agent=None, ip_address=None, **kwargs)[source]

Register a new user with email and password.

If request is provided, user_agent and ip_address are extracted automatically when not explicitly given.

Raises:
  • EmailAlreadyExistsError – An account with this email exists.

  • PasswordValidationError – Password did not meet requirements.

  • SignupDisabledError – Signup is turned off via config.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • confirmation_url (str | None)

  • confirmation_subject (str)

  • user_agent (str | None)

  • ip_address (str | None)

  • kwargs (Any)

Return type:

AuthResult

async sign_in_with_password(email, password, *, request=None, first_name=None, last_name=None, user_agent=None, ip_address=None)[source]

Sign in with email and password.

Raises:

InvalidCredentialsError – Email or password did not match.

Parameters:
  • email (str)

  • password (str)

  • request (AuthRequest | None)

  • first_name (str | None)

  • last_name (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async sign_in_with_magic_link(*, email, magic_link_url)[source]

Send a magic link email for passwordless sign in.

Parameters:
  • email (str)

  • magic_link_url (str)

Return type:

None

async verify_magic_link(token, *, email=None, user_agent=None, ip_address=None)[source]

Verify a magic link and sign in.

Raises:

InvalidTokenError – Token is unknown, expired, or already used.

Parameters:
  • token (str)

  • email (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

get_oauth_authorization_url(provider, state, scopes=None, redirect_uri=None)[source]

Get the OAuth authorization URL for a provider.

Parameters:
  • provider (str)

  • state (str)

  • scopes (list[str] | None)

  • redirect_uri (str | None)

Return type:

str

async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]

Complete OAuth sign in with authorization code.

Raises:

InvalidCredentialsError – Provider rejected the code or user lookup failed.

Parameters:
  • provider (str)

  • code (str)

  • redirect_uri (str | None)

  • user_agent (str | None)

  • ip_address (str | None)

Return type:

AuthResult

async refresh_token(refresh_token)[source]

Refresh an access token using a refresh token.

Raises:

InvalidTokenError – Refresh token is unknown, expired, or revoked.

Parameters:

refresh_token (str)

Return type:

TokenPair

async create_org(*, name, slug, creator_id, **kwargs)[source]

Create an org on WorkOS and record its local mapping.

WorkOS is created first so we can capture the workos_org_id for the local row. If the local INSERT loses a slug race, we roll back the WorkOS org so we never leave it dangling without a local mapping.

Raises:

OrgSlugConflictError – Slug is already taken (locally or on WorkOS).

Parameters:
Return type:

OrgInfo

async get_org(*, org_id=None, slug=None)[source]

Look up an org by id (passthrough) or slug (local lookup).

Parameters:
Return type:

OrgInfo

async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]

Update name and/or slug.

Identify by org_id or org_slug; slug is the new value.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgSlugConflictError – New slug is already taken.

Parameters:
  • org_id (str | UUID | None)

  • org_slug (str | None)

  • name (str | None)

  • slug (str | None)

  • kwargs (Any)

Return type:

OrgInfo

async delete_org(*, org_id=None, slug=None)[source]

Delete on WorkOS + locally. Cleans up local row even if WorkOS 404s.

Returns False if the org cannot be found by id or slug.

Parameters:
Return type:

bool

async list_orgs(*, user_id=None, limit=None, offset=None)[source]

List orgs, scoped to a user’s memberships if user_id is given.

Slugs come from each org’s WorkOS metadata; orgs without a slug in metadata surface with an empty slug rather than being filtered.

Parameters:
  • user_id (str | UUID | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgInfo]

async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]

Add a user to an organization (identify by org_id or slug).

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • MemberAlreadyExistsError – User is already a member.

Parameters:
Return type:

OrgMemberInfo

async update_org_member(*, org_id=None, slug=None, user_id, role)[source]

Update a member’s role.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgMemberNotFoundError – User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async remove_org_member(*, org_id=None, slug=None, user_id)[source]

Remove a user from an organization.

Returns False if the org or membership does not exist.

Raises:

LastOwnerError – Removing this member would leave the org without an owner.

Parameters:
Return type:

bool

async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]

List members of an organization (identify by org_id or slug).

Raises:

OrgNotFoundError – Org identifier did not resolve.

Parameters:
  • org_id (str | UUID | None)

  • slug (str | None)

  • limit (int | None)

  • offset (int | None)

Return type:

list[OrgMemberInfo]

async get_org_member(*, org_id=None, slug=None, user_id)[source]

Get a single membership record.

Raises:
  • OrgNotFoundError – Org identifier did not resolve.

  • OrgMemberNotFoundError – User is not a member of the org.

Parameters:
Return type:

OrgMemberInfo

async list_users_by_cursor(*, limit=10, after=None)[source]

List users with cursor-based pagination.

Parameters:
  • limit (int)

  • after (str | None)

Return type:

CursorResult[UserInfo]

async list_sessions_by_cursor(*, user_id, limit=10, after=None)[source]

List sessions with cursor-based pagination.

Parameters:
Return type:

CursorResult[Any]

async list_orgs_by_cursor(*, user_id=None, limit=10, after=None)[source]

Cursor-paginated org list.

WorkOS drives the cursor; slugs come from each org’s metadata. Orgs missing a slug in metadata surface with an empty slug.

Parameters:
Return type:

CursorResult[OrgInfo]

async list_org_members_by_cursor(*, org_id=None, slug=None, limit=10, after=None)[source]

List organization members with cursor-based pagination.

Identify the org by org_id or slug.

Parameters:
Return type:

CursorResult[OrgMemberInfo]

class derp.auth.WorkOSOrganization[source]

Bases: Table

WorkOS-managed organizations — slug index for the WorkOS API.

WorkOS owns name, metadata, members, and timestamps. This local table exists ONLY as a slug index: id IS the WorkOS org id (the same string the WorkOS API and JWT carry), and slug is a locally- enforced unique handle so slug→id lookup is O(1) instead of paginating the WorkOS API.

Application FKs to organizations.id therefore hold the same value as SessionInfo.org_id, so tenant-scoping comparisons need no translation step (which is the most common source of confused-deputy bugs in dual-id setups).

The unique columns get implicit unique indexes from PostgreSQL.

id: Varchar
slug: Varchar