derp.auth – Authentication¶
Derp Auth - Authentication library for FastAPI applications.
- class derp.auth.AuthConfig[source]
Bases:
_StrictModelAuth 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:
_StrictModelConfiguration 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
- 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:
_StrictModelConfiguration 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:
_StrictModelConfiguration 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:
_StrictModelConfiguration for JWT tokens.
- secret: str
- algorithm: str
- access_token_expire_minutes: int
- refresh_token_expire_days: int
- model_config = {'extra': 'forbid'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class derp.auth.NativeAuthConfig[source]
Bases:
_StrictModelConfiguration 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:
_StrictModelConfiguration 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:
_StrictModelConfiguration for Supabase GoTrue authentication.
- url: str
- anon_key: str
- service_role_key: str
- jwt_secret: str
- model_config = {'extra': 'forbid'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class derp.auth.WorkOSConfig[source]
Bases:
_StrictModelConfiguration for WorkOS authentication.
- api_key: str
- client_id: str
- model_config = {'extra': 'forbid'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- exception derp.auth.ConfirmationURLMissingError[source]
Bases:
AuthErrorRaised when a confirmation URL is missing.
- exception derp.auth.EmailSendError[source]
Bases:
AuthErrorRaised when sending an email fails.
- exception derp.auth.OrgMismatchError[source]
Bases:
AuthErrorRaised when
assert_same_orgfinds 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.
- exception derp.auth.PasswordValidationError[source]
Bases:
AuthErrorRaised when a password fails validation.
- exception derp.auth.SignupDisabledError[source]
Bases:
AuthErrorRaised when signup is disabled.
- class derp.auth.TokenPair[source]
Bases:
objectAccess and refresh token pair.
- access_token: str
- refresh_token: str
- token_type: str = 'bearer'
- expires_in: int = 0
- expires_at: datetime
- class derp.auth.TokenPayload[source]
Bases:
objectPayload data from a decoded JWT token.
- sub: str
- session_id: str
- exp: datetime
- iat: datetime
- class derp.auth.AuthResult[source]
Bases:
objectResult 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:
GenericCursor-paginated result.
- data: list[T]
- has_more: bool
- class derp.auth.AuthOrgMember[source]
Bases:
TableOrganization 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).
- class derp.auth.AuthOrganization[source]
Bases:
TableOrganization 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).
- class derp.auth.AuthProvider[source]
Bases:
StrEnumAuthentication provider types.
- EMAIL = 'email'
- MAGIC_LINK = 'magic_link'
- GOOGLE = 'google'
- GITHUB = 'github'
- __new__(value)
- class derp.auth.AuthRequest[source]
Bases:
ProtocolProtocol for objects that carry HTTP headers (e.g. FastAPI Request).
- __init__(*args, **kwargs)
- class derp.auth.AuthSession[source]
Bases:
TableAuthentication session table with integrated refresh tokens.
Each row represents a refresh token. Rows sharing the same
session_idbelong 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).
- class derp.auth.AuthUser[source]
Bases:
TableUser 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
- class derp.auth.OrgInfo[source]
Bases:
BaseModelUnified organization information returned by all auth backends.
- id: str
- name: str
- slug: 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.OrgMemberInfo[source]
Bases:
BaseModelUnified 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:
BaseModelUnified session information returned by authenticate.
- user_id: str
- session_id: str
- role: str
- expires_at: datetime
- model_config = {'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class derp.auth.UserInfo[source]
Bases:
BaseModelUnified user information returned by all auth backends.
- id: str
- email: str
- 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
- model_config = {'frozen': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class derp.auth.Argon2Hasher[source]
Bases:
PasswordHasherPassword hasher using Argon2id (recommended).
- __init__(time_cost=3, memory_cost=65536, parallelism=4, *, max_workers=8)[source]
- verify(password, hashed)[source]
Verify a password against an Argon2 hash.
- class derp.auth.PasswordHasher[source]
Bases:
ABCAbstract base class for password hashers.
- abstractmethod hash(password)[source]
Hash a password synchronously.
- abstractmethod verify(password, hashed)[source]
Verify a password synchronously.
- abstractmethod needs_rehash(hashed)[source]
Check if a hash needs to be rehashed (e.g., algorithm upgrade).
- async async_hash(password)[source]
Hash a password without blocking the event loop.
- class derp.auth.PasswordValidationResult[source]
Bases:
objectResult of password validation.
- valid: bool
- derp.auth.generate_secure_token(length=32)[source]
Generate a cryptographically secure random token.
- class derp.auth.BaseOAuthProvider[source]
-
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.
- abstractmethod async exchange_code(code, redirect_uri=None)[source]
Exchange authorization code for tokens.
- 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.
- async exchange_code(code, redirect_uri=None)[source]
Exchange authorization code for GitHub tokens.
- 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.
- async exchange_code(code, redirect_uri=None)[source]
Exchange authorization code for Google tokens.
- class derp.auth.OAuthUserInfo[source]
Bases:
objectUser information retrieved from OAuth provider.
- id: str
- email: str
- email_verified: bool = False
- class derp.auth.EmailClient[source]
Bases:
objectAsync 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.
- class derp.auth.BaseAuthClient[source]
Bases:
ABCAbstract base authentication client.
Defines the full interface shared by all auth backends (native, Supabase, WorkOS). Core methods are abstract; optional methods raise
NotImplementedErrorby 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.
- abstractmethod async list_users(*, limit=None, offset=None)[source]
List users.
- abstractmethod async update_user(*, user_id, email=None, **kwargs)[source]
Update user data.
- abstractmethod async delete_user(user_id)[source]
Delete a user and all their sessions. Returns
Falseif not found.
- 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.
- abstractmethod async sign_out(session_id)[source]
Sign out by revoking a session.
- abstractmethod async sign_out_all(user_id)[source]
Sign out all sessions for a user.
- is_authorized(session, *roles)[source]
Check if the session’s role is in the allowed set.
- 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_agentandip_addressare 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:
- 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.
- async sign_in_with_magic_link(*, email, magic_link_url)[source]
Send a magic link email for passwordless sign in.
- async verify_magic_link(token, *, user_agent=None, ip_address=None)[source]
Verify a magic link and sign in.
- 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.
- async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]
Complete OAuth sign in with authorization code.
- 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.
- async reset_password(token, new_password)[source]
Reset password using recovery token.
- 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.
- async create_org(*, name, slug, creator_id, **kwargs)[source]
Create an organization. The creator is added as owner.
- async get_org(*, org_id=None, slug=None)[source]
Get an organization by ID or slug. Provide exactly one.
- async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]
Update an organization.
Identify the org by
org_idORorg_slug(exactly one). Theslugkwarg sets the org’s new slug — it does NOT identify the target. This is the one placeslug=means a value rather than a lookup, since slug is updateable.
- async delete_org(*, org_id=None, slug=None)[source]
Delete an organization and all its memberships.
Identify by
org_idorslug. ReturnsFalseif not found.
- async list_orgs(*, user_id=None, limit=None, offset=None)[source]
List organizations, optionally filtered by user membership.
- async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]
Add a user to an organization (identify by
org_idorslug).
- async update_org_member(*, org_id=None, slug=None, user_id, role)[source]
Update a member’s role.
- async remove_org_member(*, org_id=None, slug=None, user_id)[source]
Remove a user from an organization.
Returns
Falseif the org or membership does not exist.
- async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]
List members of an organization (identify by
org_idorslug).
- async get_org_member(*, org_id=None, slug=None, user_id)[source]
Get a single membership record.
- async list_users_by_cursor(*, limit=10, after=None)[source]
List users with cursor-based pagination.
- async list_sessions_by_cursor(*, user_id, limit=10, after=None)[source]
List sessions with cursor-based pagination.
- async list_orgs_by_cursor(*, user_id=None, limit=10, after=None)[source]
List organizations with cursor-based pagination.
- 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_idorslug.
- async set_active_org(*, session_id, org_id)[source]
Switch the active organization for a session.
Pass
org_id=Noneto clear the active org (sign out of org context).
- is_org_authorized(session, org_id, *roles)[source]
Check if the session has an active org with an allowed role.
- 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.
- assert_same_org(session, org_id)[source]
Raise
OrgMismatchErrorif 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
OrgMismatchErrorand map it to whatever HTTP/RPC error your framework expects.
- class derp.auth.NativeAuthClient[source]
Bases:
BaseAuthClientNative 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.
- async list_users(*, limit=None, offset=None)[source]
List users ordered by creation date (newest first).
- async update_user(*, user_id, email=None, **kwargs)[source]
Update user data.
- async delete_user(user_id)[source]
Delete a user and all their sessions.
- 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_confirmationis on.PasswordValidationError – Password did not meet requirements.
EmailAlreadyExistsError – An account with this email exists.
- Parameters:
- 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
WARNINGfor ops; the caller-visible error is opaque to avoid email-enumeration leaks.- Parameters:
- 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:
- Return type:
None
- async verify_magic_link(token, *, user_agent=None, ip_address=None)[source]
Verify a magic link and sign in.
- 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.
- 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.
- 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
SessionInfobuilt from JWT claims, orNoneif 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.
- async sign_out(session_id)[source]
Sign out by deleting all tokens for a session.
- async sign_out_all(user_id)[source]
Sign out all sessions for a user by deleting all tokens.
- 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.
- async reset_password(token, new_password)[source]
Reset password using recovery token.
- 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.
- async create_org(*, name, slug, creator_id, **kwargs)[source]
Create an organization. The creator is added as owner.
- async get_org(*, org_id=None, slug=None)[source]
Get an organization by ID or slug. Provide exactly one.
- async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]
Update an organization. Identify by
org_idororg_slug.slugis the new slug to assign — not the lookup key.
- async delete_org(*, org_id=None, slug=None)[source]
Delete an organization and all its memberships.
- async list_orgs(*, user_id=None, limit=None, offset=None)[source]
List organizations, optionally filtered by user membership.
- async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]
Add a user to an organization (identify by
org_idorslug).
- async update_org_member(*, org_id=None, slug=None, user_id, role)[source]
Update a member’s role.
- async remove_org_member(*, org_id=None, slug=None, user_id)[source]
Remove a user from an organization.
Returns
Falseif the org or membership does not exist.
- async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]
List members of an organization (identify by
org_idorslug).
- async get_org_member(*, org_id=None, slug=None, user_id)[source]
Get a single membership record.
- async set_active_org(*, session_id, org_id)[source]
Switch the active organization for a session.
Pass
org_id=Noneto clear the active org.
- class derp.auth.SupabaseAuthClient[source]
Bases:
BaseAuthClientSupabase 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.
- async list_users(*, limit=None, offset=None)[source]
List users.
- async update_user(*, user_id, email=None, **kwargs)[source]
Update user data.
- async delete_user(user_id)[source]
Delete a user and all their sessions. Returns
Falseif not found.
- 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_agentandip_addressare 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:
- 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.
- async sign_in_with_magic_link(*, email, magic_link_url)[source]
Send a magic link email for passwordless sign in.
- async verify_magic_link(token, *, user_agent=None, ip_address=None)[source]
Verify a magic link and sign in.
- 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.
- async reset_password(token, new_password)[source]
Reset password using recovery token.
- 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.
- async sign_out(session_id)[source]
Sign out by revoking a session.
- async sign_out_all(user_id)[source]
Sign out all sessions for a user.
- get_oauth_authorization_url(provider, state, scopes=None, redirect_uri=None)[source]
Get the OAuth authorization URL for a provider.
- async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]
Complete OAuth sign in with authorization code.
- async create_org(*, name, slug, creator_id, **kwargs)[source]
Raises: OrgSlugConflictError: Slug is already taken.
- async get_org(*, org_id=None, slug=None)[source]
Raises: OrgNotFoundError: No org matches the given id or slug.
- async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]
Identify by
org_idororg_slug;slugis the new value.
- async delete_org(*, org_id=None, slug=None)[source]
Delete an organization and all its memberships.
Identify by
org_idorslug. ReturnsFalseif not found.
- async list_orgs(*, user_id=None, limit=None, offset=None)[source]
List organizations, optionally filtered by user membership.
- 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.
- 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.
- async remove_org_member(*, org_id=None, slug=None, user_id)[source]
Returns
Falseif the org or membership does not exist.
- async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]
Raises: OrgNotFoundError: Org identifier did not resolve.
- 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.
- class derp.auth.SupabaseOrgMember[source]
Bases:
TableOrganization 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).
- class derp.auth.WorkOSAuthClient[source]
Bases:
BaseAuthClientWorkOS-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, andlist_org_membersmethods raiseNotImplementedError— use the*_by_cursorvariants 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.
- async list_users(*, limit=None, offset=None)[source]
List users.
- async update_user(*, user_id, email=None, **kwargs)[source]
Update user data.
- async delete_user(user_id)[source]
Delete a user and all their sessions. Returns
Falseif not found.
- async list_sessions(*, user_id=None, limit=None, offset=None)[source]
List active sessions, optionally filtered by user.
- async sign_out(session_id)[source]
Sign out by revoking a session.
- async sign_out_all(user_id)[source]
Sign out all sessions for a user.
- 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_agentandip_addressare 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:
- 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.
- async sign_in_with_magic_link(*, email, magic_link_url)[source]
Send a magic link email for passwordless sign in.
- async verify_magic_link(token, *, email=None, user_agent=None, ip_address=None)[source]
Verify a magic link and sign in.
- get_oauth_authorization_url(provider, state, scopes=None, redirect_uri=None)[source]
Get the OAuth authorization URL for a provider.
- async sign_in_with_oauth(provider, code, *, redirect_uri=None, user_agent=None, ip_address=None)[source]
Complete OAuth sign in with authorization code.
- 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.
- async get_org(*, org_id=None, slug=None)[source]
Look up an org by id (passthrough) or slug (local lookup).
- async update_org(*, org_id=None, org_slug=None, name=None, slug=None, **kwargs)[source]
Update name and/or slug.
Identify by
org_idororg_slug;slugis the new value.
- async delete_org(*, org_id=None, slug=None)[source]
Delete on WorkOS + locally. Cleans up local row even if WorkOS 404s.
Returns
Falseif the org cannot be found by id or slug.
- async list_orgs(*, user_id=None, limit=None, offset=None)[source]
List orgs, scoped to a user’s memberships if
user_idis given.Slugs come from each org’s WorkOS metadata; orgs without a slug in metadata surface with an empty slug rather than being filtered.
- async add_org_member(*, org_id=None, slug=None, user_id, role='member')[source]
Add a user to an organization (identify by
org_idorslug).
- async update_org_member(*, org_id=None, slug=None, user_id, role)[source]
Update a member’s role.
- async remove_org_member(*, org_id=None, slug=None, user_id)[source]
Remove a user from an organization.
Returns
Falseif the org or membership does not exist.
- async list_org_members(*, org_id=None, slug=None, limit=None, offset=None)[source]
List members of an organization (identify by
org_idorslug).
- async get_org_member(*, org_id=None, slug=None, user_id)[source]
Get a single membership record.
- async list_users_by_cursor(*, limit=10, after=None)[source]
List users with cursor-based pagination.
- async list_sessions_by_cursor(*, user_id, limit=10, after=None)[source]
List sessions with cursor-based pagination.
- 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.
- 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_idorslug.
- class derp.auth.WorkOSOrganization[source]
Bases:
TableWorkOS-managed organizations — slug index for the WorkOS API.
WorkOS owns name, metadata, members, and timestamps. This local table exists ONLY as a slug index:
idIS the WorkOS org id (the same string the WorkOS API and JWT carry), andslugis a locally- enforced unique handle so slug→id lookup is O(1) instead of paginating the WorkOS API.Application FKs to
organizations.idtherefore hold the same value asSessionInfo.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