2026-03-06 00:22:57 +05:45
// API Client for Secuird Backend
2026-01-07 14:29:40 +00:00
// Uses Bearer token authentication
2026-01-06 15:33:03 +00:00
2026-01-06 15:42:41 +00:00
import { config } from '@/config' ;
2026-01-06 15:33:03 +00:00
interface ApiResponse < T = unknown > {
version : string ;
success : boolean ;
code : number ;
message : string ;
data? : T ;
error ? : {
type : string ;
details : Record < string , unknown > ;
} ;
}
export interface User {
id : string ;
email : string ;
2026-01-06 16:06:53 +00:00
email_verified : boolean ;
2026-01-06 15:33:03 +00:00
full_name : string | null ;
avatar_url : string | null ;
2026-01-06 16:06:53 +00:00
status : string ;
last_login_at : string | null ;
created_at : string ;
updated_at : string ;
2026-03-01 16:50:19 +05:45
// Fields present in admin list view
org_role? : string ;
org_id? : string ;
activated? : boolean ;
2026-03-04 18:43:12 +05:45
// Auth method capabilities — present on /users/me response
has_password? : boolean ;
totp_enabled? : boolean ;
linked_providers? : string [ ] ;
2026-03-06 14:19:54 +05:45
/** Session-derived group memberships (from OIDC claims or session device_info). */
groups? : string [ ] ;
/** Whether the current user is allowed to access the system-wide audit log. */
can_view_system_logs? : boolean ;
2026-01-06 16:06:53 +00:00
}
export interface Organization {
id : string ;
name : string ;
slug : string ;
description : string | null ;
logo_url : string | null ;
2026-01-06 15:33:03 +00:00
is_active : boolean ;
2026-01-06 16:06:53 +00:00
role : string ;
2026-01-06 15:33:03 +00:00
created_at : string ;
updated_at : string ;
}
2026-01-06 16:06:53 +00:00
export interface OrganizationsResponse {
organizations : Organization [ ] ;
count : number ;
}
2026-01-16 17:31:25 +10:30
export interface MfaComplianceOrgSummary {
organization_id : string ;
organization_name : string ;
status : string ;
deadline_at : string | null ;
effective_mode : string ;
applied_at : string ;
}
export interface MfaComplianceSummary {
overall_status : string ;
missing_methods : string [ ] ;
deadline_at : string | null ;
orgs : MfaComplianceOrgSummary [ ] ;
}
/**
* Check if MFA is required for the user based on their compliance status.
* This checks if any organization has an effective_mode that starts with "require_",
* which handles require_webauthn, require_totp, or any future MFA methods.
*/
export function isMfaRequired ( compliance : MfaComplianceSummary | null ) : boolean {
if ( ! compliance || ! compliance . orgs ) return false ;
return compliance . orgs . some (
org = > org . effective_mode && org . effective_mode . startsWith ( 'require_' )
) ;
}
2026-01-06 15:33:03 +00:00
export interface LoginResponse {
2026-01-14 07:21:55 +00:00
user? : User ;
token? : string ;
expires_at? : string ;
2026-01-12 06:28:36 +00:00
requires_totp? : boolean ;
2026-01-16 17:50:56 +10:30
requires_webauthn? : boolean ;
2026-01-16 17:31:25 +10:30
requires_mfa_enrollment? : boolean ;
mfa_compliance? : MfaComplianceSummary ;
2026-03-01 20:11:22 +05:45
/** Set on login when the user belongs to no organisations. */
requires_org_setup? : boolean ;
/** Pending invitations for the user's email (present when requires_org_setup is true). */
pending_invites? : PendingInvite [ ] ;
/** True when the registering user is the very first user on this instance. */
is_first_user? : boolean ;
2026-01-12 06:28:36 +00:00
}
export interface TotpEnrollResponse {
secret : string ;
provisioning_uri : string ;
qr_code : string ; // base64 PNG
backup_codes : string [ ] ;
}
export interface TotpStatusResponse {
totp_enabled : boolean ;
verified_at : string | null ;
backup_codes_remaining : number ;
}
export interface TotpVerifyResponse {
user : User ;
token : string ;
expires_at : string ;
2026-01-06 15:33:03 +00:00
}
export interface ProfileResponse {
user : User ;
}
2026-01-14 15:32:30 +00:00
// WebAuthn types
export interface PasskeyCredential {
id : string ;
name : string ;
transports : string [ ] ;
device_type : string ;
created_at : string ;
last_used_at : string | null ;
}
export interface WebAuthnStatusResponse {
webauthn_enabled : boolean ;
credential_count : number ;
}
export interface WebAuthnCredentialsResponse {
credentials : PasskeyCredential [ ] ;
count : number ;
}
export interface WebAuthnLoginCompleteResponse {
user : User ;
token : string ;
expires_at : string ;
}
2026-03-04 18:43:12 +05:45
// Admin MFA management types
export interface AdminMfaMethod {
/** Unique identifier: auth_method.id for TOTP, credential id for WebAuthn */
id : string ;
/** 'totp' or 'webauthn' */
type : 'totp' | 'webauthn' ;
/** Human-readable name */
name : string ;
device_type? : string ;
transports? : string [ ] ;
verified : boolean ;
created_at : string | null ;
last_used_at : string | null ;
}
export interface AdminLinkedAccount {
/** UUID of the AuthenticationMethod row */
id : string ;
/** Provider name: 'google' | 'github' | 'microsoft' | 'oidc' */
provider_type : string ;
/** Email address from the OAuth provider, if available */
email : string | null ;
/** Display name from the OAuth provider, if available */
name : string | null ;
/** ISO timestamp when the account was linked */
linked_at : string | null ;
}
2026-01-21 03:09:38 +10:30
// External Auth Types
2026-02-26 23:38:45 +05:45
export type ExternalProviderId = 'google' | 'github' | 'microsoft' ;
2026-01-20 15:54:11 +10:30
export interface ExternalProvider {
2026-01-21 03:09:38 +10:30
id : string ;
2026-01-20 15:54:11 +10:30
name : string ;
2026-01-21 03:09:38 +10:30
type : string ;
is_configured : boolean ;
2026-01-20 15:54:11 +10:30
is_active : boolean ;
2026-01-21 03:09:38 +10:30
settings : {
requires_domain : boolean ;
supports_refresh_tokens : boolean ;
} ;
2026-01-20 15:54:11 +10:30
}
2026-01-21 03:09:38 +10:30
export interface ExternalProvidersResponse {
providers : ExternalProvider [ ] ;
2026-01-20 15:54:11 +10:30
}
export interface LinkedAccount {
id : string ;
2026-01-21 03:09:38 +10:30
provider_type : string ;
provider_user_id : string ;
email : string | null ;
name : string | null ;
picture : string | null ;
verified : boolean ;
linked_at : string | null ;
last_used_at : string | null ;
}
export interface LinkedAccountsResponse {
linked_accounts : LinkedAccount [ ] ;
unlink_available : boolean ;
2026-01-20 15:54:11 +10:30
}
2026-02-28 23:35:32 +05:45
export interface PrincipalOption {
id : string ;
name : string ;
description : string | null ;
}
export interface MyPrincipalsOrg {
org_id : string ;
org_name : string ;
role : string ;
is_admin : boolean ;
my_principals : PrincipalOption [ ] ;
all_principals : PrincipalOption [ ] ; // populated for admin/owner only
}
export interface MyPrincipalsResponse {
orgs : MyPrincipalsOrg [ ] ;
}
2026-01-20 15:54:11 +10:30
export interface OAuthAuthorizeResponse {
authorization_url : string ;
state : string ;
}
export interface OAuthCallbackResponse {
2026-01-21 03:09:38 +10:30
token : string ;
expires_in : number ;
token_type : string ;
user : User ;
}
export interface LinkAccountResponse {
linked_account : LinkedAccount ;
2026-01-20 15:54:11 +10:30
}
2026-03-08 18:08:42 +05:45
export interface OrganizationApiKey {
id : string ;
organization_id : string ;
name : string ;
description : string | null ;
key_hash? : string ; // Usually excluded from responses for security
last_used_at : string | null ;
is_revoked : boolean ;
revoked_at : string | null ;
revoke_reason : string | null ;
created_at : string ;
updated_at : string ;
}
export interface CertificateAuditLog {
id : string ;
action : string ;
certificate_serial : string ;
key_id : string ;
principals : string [ ] ;
user_id : string ;
user_email : string | null ;
issued_at : string ;
valid_after : string ;
valid_before : string ;
ip_address : string | null ;
user_agent : string | null ;
message : string | null ;
success : boolean ;
created_at : string ;
}
2026-01-06 15:33:03 +00:00
class ApiError extends Error {
code : number ;
type : string ;
details : Record < string , unknown > ;
constructor ( message : string , code : number , type : string , details : Record < string , unknown > = { } ) {
super ( message ) ;
this . name = 'ApiError' ;
this . code = code ;
this . type = type ;
this . details = details ;
}
}
2026-01-07 14:29:40 +00:00
// Token storage keys
2026-03-06 00:22:57 +05:45
const TOKEN_KEY = 'secuird_token' ;
const TOKEN_EXPIRY_KEY = 'secuird_token_expiry' ;
2026-01-07 14:29:40 +00:00
// Token management
export const tokenManager = {
getToken : ( ) : string | null = > {
const token = localStorage . getItem ( TOKEN_KEY ) ;
const expiry = localStorage . getItem ( TOKEN_EXPIRY_KEY ) ;
// Check if token is expired
if ( token && expiry ) {
const expiryDate = new Date ( expiry ) ;
if ( expiryDate <= new Date ( ) ) {
tokenManager . clearToken ( ) ;
return null ;
}
}
return token ;
} ,
2026-01-15 23:15:04 +00:00
setToken : ( token : string , expiresAt? : string | null ) : void = > {
2026-01-07 14:29:40 +00:00
localStorage . setItem ( TOKEN_KEY , token ) ;
2026-01-15 23:15:04 +00:00
if ( expiresAt ) {
localStorage . setItem ( TOKEN_EXPIRY_KEY , expiresAt ) ;
} else {
localStorage . removeItem ( TOKEN_EXPIRY_KEY ) ;
}
2026-01-07 14:29:40 +00:00
} ,
clearToken : ( ) : void = > {
localStorage . removeItem ( TOKEN_KEY ) ;
localStorage . removeItem ( TOKEN_EXPIRY_KEY ) ;
} ,
hasValidToken : ( ) : boolean = > {
2026-02-27 21:08:16 +05:45
return tokenManager . getToken ( ) !== null ;
2026-01-07 14:29:40 +00:00
} ,
} ;
2026-01-14 02:10:23 +00:00
// Error types that indicate the session/token is truly invalid
const SESSION_INVALID_ERROR_TYPES = [
'INVALID_TOKEN' ,
'TOKEN_EXPIRED' ,
'SESSION_EXPIRED' ,
'AUTH_ERROR' ,
'UNAUTHORIZED' ,
] ;
2026-01-16 17:31:25 +10:30
export const AUTHORIZATION_ERROR_TYPES = [ 'AUTHORIZATION_ERROR' ] as const ;
2026-01-14 02:10:23 +00:00
interface RequestConfig {
// Controls token clearing on 401:
// - 'auto' (default): Clear only if error type indicates invalid session
// - true: Always clear token on 401
// - false: Never clear token on 401
clearTokenOn401? : boolean | 'auto' ;
2026-01-16 17:31:25 +10:30
// Optional callback for handling 403 authorization errors
on403 ? : ( error : ApiError ) = > void ;
2026-01-14 02:10:23 +00:00
}
2026-01-07 14:29:40 +00:00
// Central request function - all API calls go through here
2026-01-06 15:33:03 +00:00
async function request < T > (
endpoint : string ,
2026-01-07 14:29:40 +00:00
options : RequestInit = { } ,
2026-01-14 02:10:23 +00:00
requiresAuth = true ,
requestConfig : RequestConfig = { }
2026-01-06 15:33:03 +00:00
) : Promise < T > {
2026-01-16 17:31:25 +10:30
const { clearTokenOn401 = 'auto' , on403 } = requestConfig ;
2026-01-14 02:10:23 +00:00
2026-01-07 14:29:40 +00:00
const headers : Record < string , string > = {
'Content-Type' : 'application/json' ,
2026-01-12 01:24:59 +00:00
'Cache-Control' : 'no-cache, no-store, must-revalidate' ,
'Pragma' : 'no-cache' ,
2026-01-07 14:29:40 +00:00
. . . ( options . headers as Record < string , string > ) ,
} ;
// Add Authorization header if we have a token and auth is required
if ( requiresAuth ) {
const token = tokenManager . getToken ( ) ;
if ( token ) {
headers [ 'Authorization' ] = ` Bearer ${ token } ` ;
}
}
2026-01-06 15:42:41 +00:00
const response = await fetch ( ` ${ config . api . baseUrl } ${ endpoint } ` , {
2026-01-06 15:33:03 +00:00
. . . options ,
2026-01-07 14:29:40 +00:00
headers ,
2026-01-15 22:46:27 +00:00
credentials : 'include' , // Always include cookies for session consistency
2026-01-12 01:24:59 +00:00
cache : 'no-store' ,
2026-01-06 15:33:03 +00:00
} ) ;
const json : ApiResponse < T > = await response . json ( ) ;
if ( ! json . success ) {
2026-01-14 02:10:23 +00:00
const errorType = json . error ? . type || 'UNKNOWN_ERROR' ;
// Handle 401 token clearing based on configuration
2026-01-07 14:29:40 +00:00
if ( json . code === 401 ) {
2026-01-14 02:10:23 +00:00
const shouldClearToken =
clearTokenOn401 === true ||
( clearTokenOn401 === 'auto' && SESSION_INVALID_ERROR_TYPES . includes ( errorType ) ) ;
if ( shouldClearToken ) {
tokenManager . clearToken ( ) ;
if ( import . meta . env . DEV ) {
console . log ( ` [API] Token cleared on 401 (type: ${ errorType } , endpoint: ${ endpoint } ) ` ) ;
}
} else if ( import . meta . env . DEV ) {
console . log ( ` [API] 401 received but token preserved (type: ${ errorType } , endpoint: ${ endpoint } ) ` ) ;
}
2026-01-07 14:29:40 +00:00
}
2026-01-16 17:31:25 +10:30
// Handle 403 authorization errors
if ( json . code === 403 ) {
const error = new ApiError (
json . message || 'Access denied' ,
json . code ,
errorType ,
json . error ? . details || { }
) ;
if ( on403 ) {
on403 ( error ) ;
}
throw error ;
}
2026-01-07 14:29:40 +00:00
2026-01-06 15:33:03 +00:00
throw new ApiError (
json . message || 'An error occurred' ,
json . code ,
2026-01-14 02:10:23 +00:00
errorType ,
2026-01-06 15:33:03 +00:00
json . error ? . details || { }
) ;
}
return json . data as T ;
}
2026-01-07 14:29:40 +00:00
// Centralized API client - all routes defined here
2026-01-06 15:33:03 +00:00
export const api = {
auth : {
2026-01-07 14:29:40 +00:00
login : async ( email : string , password : string , remember_me = false ) : Promise < LoginResponse > = > {
const response = await request < LoginResponse > ( '/auth/login' , {
2026-01-06 15:33:03 +00:00
method : 'POST' ,
body : JSON.stringify ( { email , password , remember_me } ) ,
2026-01-14 07:21:55 +00:00
credentials : 'include' , // Required for TOTP session tracking
2026-01-07 14:29:40 +00:00
} , false ) ; // Login doesn't require auth
2026-01-14 07:21:55 +00:00
// Only store token if login is complete (no TOTP required)
2026-01-15 23:15:04 +00:00
if ( response . token && ! response . requires_totp ) {
tokenManager . setToken ( response . token , response . expires_at ? ? null ) ;
2026-01-14 07:21:55 +00:00
}
2026-01-07 14:29:40 +00:00
return response ;
} ,
2026-01-06 15:33:03 +00:00
2026-02-27 21:08:16 +05:45
register : async ( email : string , password : string , full_name? : string ) : Promise < LoginResponse > = > {
const response = await request < LoginResponse > ( '/auth/register' , {
method : 'POST' ,
body : JSON.stringify ( { email , password , password_confirm : password , full_name } ) ,
} , false ) ;
if ( response . token ) {
tokenManager . setToken ( response . token , response . expires_at ? ? null ) ;
}
return response ;
} ,
forgotPassword : ( email : string ) : Promise < { message : string } > = >
request < { message : string } > ( '/auth/forgot-password' , {
method : 'POST' ,
body : JSON.stringify ( { email } ) ,
} , false ) ,
resetPassword : ( token : string , password : string ) : Promise < { message : string } > = >
request < { message : string } > ( '/auth/reset-password' , {
method : 'POST' ,
body : JSON.stringify ( { token , password , password_confirm : password } ) ,
} , false ) ,
verifyEmail : ( token : string ) : Promise < { message : string } > = >
request < { message : string } > ( '/auth/verify-email' , {
method : 'POST' ,
body : JSON.stringify ( { token } ) ,
} , false ) ,
resendVerification : ( email : string ) : Promise < { message : string } > = >
request < { message : string } > ( '/auth/resend-verification' , {
method : 'POST' ,
body : JSON.stringify ( { email } ) ,
} , false ) ,
2026-02-28 23:35:32 +05:45
activate : ( activation_key : string ) : Promise < { message : string } > = >
request < { message : string } > ( '/auth/activate' , {
method : 'POST' ,
body : JSON.stringify ( { activation_key } ) ,
} , false ) ,
resendActivation : ( email : string ) : Promise < { message : string } > = >
request < { message : string } > ( '/auth/resend-activation' , {
method : 'POST' ,
body : JSON.stringify ( { email } ) ,
} , false ) ,
2026-01-07 14:29:40 +00:00
logout : async ( ) : Promise < void > = > {
try {
await request < void > ( '/auth/logout' , {
method : 'POST' ,
} ) ;
} finally {
// Always clear token on logout
tokenManager . clearToken ( ) ;
}
} ,
2026-01-06 15:33:03 +00:00
} ,
users : {
2026-01-14 02:10:23 +00:00
// me() is the canonical session validity check - always clear token on 401
me : ( ) = > request < ProfileResponse > ( '/users/me' , { } , true , { clearTokenOn401 : true } ) ,
2026-01-06 15:33:03 +00:00
updateMe : ( data : { full_name? : string ; avatar_url? : string } ) = >
request < ProfileResponse > ( '/users/me' , {
method : 'PATCH' ,
body : JSON.stringify ( data ) ,
} ) ,
2026-01-06 16:06:53 +00:00
2026-03-02 23:55:47 +05:45
// Delete the current user's own account (soft delete)
deleteMe : ( requestConfig? : RequestConfig ) = >
request < { message : string } > ( '/users/me' , { method : 'DELETE' } , true , requestConfig ) ,
2026-01-16 17:31:25 +10:30
organizations : ( requestConfig? : RequestConfig ) = >
request < OrganizationsResponse > ( '/users/me/organizations' , { } , true , requestConfig ) ,
2026-01-11 08:17:15 +00:00
2026-02-28 23:35:32 +05:45
// Get the current user's effective principals across all orgs
myPrincipals : ( requestConfig? : RequestConfig ) = >
request < MyPrincipalsResponse > ( '/users/me/principals' , { } , true , requestConfig ) ,
2026-01-14 02:10:23 +00:00
// Password change can return 401 for wrong current password - don't clear token
2026-01-11 08:17:15 +00:00
changePassword : ( currentPassword : string , newPassword : string , newPasswordConfirm : string ) = >
request < { message : string } > ( '/users/me/password' , {
method : 'POST' ,
body : JSON.stringify ( {
current_password : currentPassword ,
new_password : newPassword ,
new_password_confirm : newPasswordConfirm ,
} ) ,
2026-01-14 02:10:23 +00:00
} , true , { clearTokenOn401 : false } ) ,
2026-02-27 21:08:16 +05:45
// Get audit logs for the currently authenticated user
auditLogs : ( params? : Record < string , string > , requestConfig? : RequestConfig ) = >
request < { audit_logs : AuditLogEntry [ ] ; count : number ; page : number ; per_page : number ; pages : number } > (
` /auth/audit-logs ${ params ? '?' + new URLSearchParams ( params ) . toString ( ) : '' } ` ,
{ } ,
true ,
requestConfig ,
) ,
2026-03-01 16:50:19 +05:45
// Get pending (unaccepted) invitations for the logged-in user
getMyInvites : ( requestConfig? : RequestConfig ) = >
request < { invites : PendingInvite [ ] } > ( '/users/me/invites' , { } , true , requestConfig ) ,
// Get the current user's department + principal memberships across all orgs
getMyMemberships : ( requestConfig? : RequestConfig ) = >
request < { orgs : MyOrgMembership [ ] } > ( '/users/me/memberships' , { } , true , requestConfig ) ,
2026-02-27 21:08:16 +05:45
} ,
admin : {
// Get all system audit logs (admin view — returns all logs for org owners, own logs otherwise)
getAuditLogs : ( params? : Record < string , string > , requestConfig? : RequestConfig ) = >
request < { audit_logs : AuditLogEntry [ ] ; count : number ; page : number ; per_page : number ; pages : number ; is_admin_view : boolean } > (
` /audit-logs ${ params ? '?' + new URLSearchParams ( params ) . toString ( ) : '' } ` ,
{ } ,
true ,
requestConfig ,
) ,
2026-02-28 23:35:32 +05:45
// List users visible to the calling admin
listUsers : ( params? : Record < string , string > , requestConfig? : RequestConfig ) = >
request < { users : User [ ] ; count : number ; page : number ; per_page : number ; pages : number } > (
` /admin/users ${ params ? '?' + new URLSearchParams ( params ) . toString ( ) : '' } ` ,
{ } ,
true ,
requestConfig ,
) ,
// Get a single user's profile + SSH keys (admin view)
getUser : ( userId : string , requestConfig? : RequestConfig ) = >
request < { user : User ; ssh_keys : SSHKey [ ] } > ( ` /admin/users/ ${ userId } ` , { } , true , requestConfig ) ,
2026-03-01 16:50:19 +05:45
// Update a user's role in a shared org (admin action)
updateUserRole : ( orgId : string , userId : string , role : string , requestConfig? : RequestConfig ) = >
request < { member : OrganizationMember } > ( ` /organizations/ ${ orgId } /members/ ${ userId } /role ` , {
method : 'PATCH' ,
body : JSON.stringify ( { role } ) ,
} , true , requestConfig ) ,
// List application-level OAuth provider configurations
listOAuthProviders : ( requestConfig? : RequestConfig ) = >
request < { providers : { id : string ; name : string ; is_configured : boolean ; is_enabled : boolean ; client_id : string | null } [ ] } > (
'/admin/oauth/providers' , { } , true , requestConfig ,
) ,
// Create or update an application-level OAuth provider
configureOAuthProvider : ( provider : string , clientId : string , clientSecret : string , isEnabled : boolean , requestConfig? : RequestConfig ) = >
request < { provider : { id : string ; client_id : string ; is_enabled : boolean } } > (
` /admin/oauth/providers/ ${ provider } ` ,
{ method : 'PUT' , body : JSON.stringify ( { client_id : clientId , client_secret : clientSecret , is_enabled : isEnabled } ) } ,
true ,
requestConfig ,
) ,
// Delete an application-level OAuth provider
deleteOAuthProvider : ( provider : string , requestConfig? : RequestConfig ) = >
request < Record < string , never > > ( ` /admin/oauth/providers/ ${ provider } ` , { method : 'DELETE' } , true , requestConfig ) ,
// Suspend a user account (blocks login & CA issuance)
suspendUser : ( userId : string , requestConfig? : RequestConfig ) = >
request < { user : User } > ( ` /admin/users/ ${ userId } /suspend ` , { method : 'POST' } , true , requestConfig ) ,
// Restore a suspended user to active status
unsuspendUser : ( userId : string , requestConfig? : RequestConfig ) = >
request < { user : User } > ( ` /admin/users/ ${ userId } /unsuspend ` , { method : 'POST' } , true , requestConfig ) ,
2026-03-04 18:43:12 +05:45
// Force-verify a user's email and activate their account (clears stale verification tokens)
adminVerifyUserEmail : ( userId : string , requestConfig? : RequestConfig ) = >
request < { user : User } > ( ` /admin/users/ ${ userId } /verify-email ` , { method : 'POST' } , true , requestConfig ) ,
2026-03-02 23:55:47 +05:45
// Permanently delete a user — revokes certs, cascades DB delete, unrecoverable
hardDeleteUser : ( userId : string , requestConfig? : RequestConfig ) = >
request < { deleted_user_id : string ; deleted_user_email : string ; ssh_keys_deleted : number ; certs_revoked : number } > (
` /admin/users/ ${ userId } /delete ` ,
{ method : 'POST' , body : JSON.stringify ( { confirm : true } ) } ,
true ,
requestConfig ,
) ,
2026-03-04 18:43:12 +05:45
// Get all MFA methods configured for a user (admin view)
getUserMfa : ( userId : string , requestConfig? : RequestConfig ) = >
request < { user : { id : string ; email : string ; full_name : string | null } ; mfa_methods : AdminMfaMethod [ ] } > (
` /admin/users/ ${ userId } /mfa ` ,
{ } ,
true ,
requestConfig ,
) ,
// Remove an MFA method for a user (admin action — use when user lost access)
// method_type: 'totp' | 'webauthn' | 'all'
// credentialId: optional WebAuthn credential ID to remove a single passkey
removeUserMfa : ( userId : string , methodType : 'totp' | 'webauthn' | 'all' , credentialId? : string , requestConfig? : RequestConfig ) = > {
const qs = credentialId ? ` ?credential_id= ${ encodeURIComponent ( credentialId ) } ` : '' ;
return request < { removed_methods : string [ ] ; removed_count : number ; user : { id : string ; email : string } } > (
` /admin/users/ ${ userId } /mfa/ ${ methodType } ${ qs } ` ,
{ method : 'DELETE' } ,
true ,
requestConfig ,
) ;
} ,
// Get linked OAuth/OIDC accounts for a user (admin view)
getUserLinkedAccounts : ( userId : string , requestConfig? : RequestConfig ) = >
request < {
user : { id : string ; email : string ; full_name : string | null } ;
linked_accounts : AdminLinkedAccount [ ] ;
total_auth_methods : number ;
} > (
` /admin/users/ ${ userId } /linked-accounts ` ,
{ } ,
true ,
requestConfig ,
) ,
// Unlink an OAuth/OIDC provider from a user's account (admin action)
// provider: provider name ('google', 'github', 'microsoft', 'oidc') or method UUID
adminUnlinkUserProvider : ( userId : string , provider : string , requestConfig? : RequestConfig ) = >
request < { provider : string ; user : { id : string ; email : string } } > (
` /admin/users/ ${ userId } /linked-accounts/ ${ encodeURIComponent ( provider ) } ` ,
{ method : 'DELETE' } ,
true ,
requestConfig ,
) ,
// Set or reset a user's password (admin action — no current password needed)
// Creates the password auth method if the user doesn't have one (e.g. OAuth-only users)
adminSetUserPassword : ( userId : string , password : string , requestConfig? : RequestConfig ) = >
request < { user : { id : string ; email : string } } > (
` /admin/users/ ${ userId } /password ` ,
{ method : 'POST' , body : JSON.stringify ( { password } ) } ,
true ,
requestConfig ,
) ,
2026-03-01 16:50:19 +05:45
// Get the cert policy for a department
getDeptCertPolicy : ( orgId : string , deptId : string , requestConfig? : RequestConfig ) = >
request < { cert_policy : DeptCertPolicy } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } /cert-policy ` , { } , true , requestConfig ) ,
// Create or update the cert policy for a department
setDeptCertPolicy : ( orgId : string , deptId : string , policy : Partial < DeptCertPolicy > , requestConfig? : RequestConfig ) = >
request < { cert_policy : DeptCertPolicy } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } /cert-policy ` , {
method : 'PUT' ,
body : JSON.stringify ( policy ) ,
} , true , requestConfig ) ,
2026-01-06 15:33:03 +00:00
} ,
2026-01-12 06:28:36 +00:00
totp : {
// Initiate TOTP enrollment - returns secret, QR code, and backup codes
enroll : ( ) = >
request < TotpEnrollResponse > ( '/auth/totp/enroll' , {
method : 'POST' ,
} ) ,
2026-01-14 02:10:23 +00:00
// Verify TOTP enrollment - wrong code should not log user out
2026-01-12 06:28:36 +00:00
verifyEnrollment : ( code : string ) = >
request < { message : string } > ( '/auth/totp/verify-enrollment' , {
method : 'POST' ,
body : JSON.stringify ( { code } ) ,
2026-01-14 02:10:23 +00:00
} , true , { clearTokenOn401 : false } ) ,
2026-01-12 06:28:36 +00:00
// Verify TOTP code during login (no auth required - uses session state)
2026-01-14 07:21:55 +00:00
verify : async ( code : string , isBackupCode = false ) : Promise < TotpVerifyResponse > = > {
const response = await request < TotpVerifyResponse > ( '/auth/totp/verify' , {
2026-01-12 06:28:36 +00:00
method : 'POST' ,
body : JSON.stringify ( { code , is_backup_code : isBackupCode } ) ,
2026-01-14 07:21:55 +00:00
credentials : 'include' , // Required for TOTP session tracking
} , false ) ;
2026-01-15 23:15:04 +00:00
if ( response . token ) {
tokenManager . setToken ( response . token , response . expires_at ? ? null ) ;
2026-01-14 07:21:55 +00:00
}
return response ;
} ,
2026-01-12 06:28:36 +00:00
2026-01-20 15:54:11 +10:30
// Verify TOTP code with an mfa_token (used after OAuth callback when MFA is required)
verifyWithMfaToken : async ( code : string , mfaToken : string , isBackupCode = false ) : Promise < TotpVerifyResponse > = > {
const response = await request < TotpVerifyResponse > ( '/auth/totp/verify' , {
method : 'POST' ,
body : JSON.stringify ( { code , mfa_token : mfaToken , is_backup_code : isBackupCode } ) ,
credentials : 'include' ,
} , false ) ;
if ( response . token ) {
tokenManager . setToken ( response . token , response . expires_at ? ? null ) ;
}
return response ;
} ,
2026-01-12 06:28:36 +00:00
// Get TOTP status
status : ( ) = >
request < TotpStatusResponse > ( '/auth/totp/status' ) ,
2026-01-14 02:10:23 +00:00
// Disable TOTP - wrong password should not log user out
2026-03-04 18:43:12 +05:45
disable : ( password? : string | null ) = >
2026-01-12 06:28:36 +00:00
request < { message : string } > ( '/auth/totp/disable' , {
method : 'DELETE' ,
2026-03-04 18:43:12 +05:45
body : JSON.stringify ( { password : password || null } ) ,
2026-01-14 02:10:23 +00:00
} , true , { clearTokenOn401 : false } ) ,
2026-01-12 06:28:36 +00:00
2026-01-14 02:10:23 +00:00
// Regenerate backup codes - wrong password should not log user out
2026-01-12 06:28:36 +00:00
regenerateBackupCodes : ( password : string ) = >
request < { backup_codes : string [ ] } > ( '/auth/totp/regenerate-backup-codes' , {
method : 'POST' ,
body : JSON.stringify ( { password } ) ,
2026-01-14 02:10:23 +00:00
} , true , { clearTokenOn401 : false } ) ,
2026-01-12 06:28:36 +00:00
} ,
2026-01-14 15:32:30 +00:00
webauthn : {
// Get WebAuthn status
status : ( ) = >
request < WebAuthnStatusResponse > ( '/auth/webauthn/status' ) ,
// List all passkeys for current user
listCredentials : ( ) = >
request < WebAuthnCredentialsResponse > ( '/auth/webauthn/credentials' ) ,
// Begin passkey registration (returns raw WebAuthn options)
beginRegistration : async ( ) : Promise < Record < string , unknown > > = > {
const response = await fetch ( ` ${ config . api . baseUrl } /auth/webauthn/register/begin ` , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
'Authorization' : ` Bearer ${ tokenManager . getToken ( ) } ` ,
} ,
} ) ;
if ( ! response . ok ) {
const error = await response . json ( ) ;
throw new ApiError (
error . message || 'Failed to begin registration' ,
error . code || response . status ,
error . error ? . type || 'WEBAUTHN_ERROR' ,
error . error ? . details || { }
) ;
}
// Returns raw WebAuthn options (not wrapped in standard response)
return response . json ( ) ;
} ,
// Complete passkey registration
completeRegistration : ( credential : Record < string , unknown > , name? : string ) = >
request < { message : string ; credential_id : string } > ( '/auth/webauthn/register/complete' , {
method : 'POST' ,
body : JSON.stringify ( { . . . credential , name } ) ,
} ) ,
// Begin passkey login (returns raw WebAuthn options)
beginLogin : async ( email : string ) : Promise < Record < string , unknown > > = > {
const response = await fetch ( ` ${ config . api . baseUrl } /auth/webauthn/login/begin ` , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' } ,
2026-01-16 11:35:21 +10:30
credentials : 'include' , // Required for session cookie
2026-01-14 15:32:30 +00:00
body : JSON.stringify ( { email } ) ,
} ) ;
if ( ! response . ok ) {
const error = await response . json ( ) ;
throw new ApiError (
error . message || 'No passkeys found for this account' ,
error . code || response . status ,
error . error ? . type || 'WEBAUTHN_ERROR' ,
error . error ? . details || { }
) ;
}
// Returns raw WebAuthn options (not wrapped in standard response)
return response . json ( ) ;
} ,
// Complete passkey login
completeLogin : async ( assertion : Record < string , unknown > ) : Promise < WebAuthnLoginCompleteResponse > = > {
2026-01-16 11:35:21 +10:30
const response = await fetch ( ` ${ config . api . baseUrl } /auth/webauthn/login/complete ` , {
2026-01-14 15:32:30 +00:00
method : 'POST' ,
2026-01-16 11:35:21 +10:30
headers : {
'Content-Type' : 'application/json' ,
} ,
credentials : 'include' , // Required for session cookie
2026-01-14 15:32:30 +00:00
body : JSON.stringify ( assertion ) ,
2026-01-16 11:35:21 +10:30
} ) ;
const json : ApiResponse < WebAuthnLoginCompleteResponse > = await response . json ( ) ;
if ( ! json . success ) {
throw new ApiError (
json . message || 'Authentication failed' ,
json . code ,
json . error ? . type || 'AUTHENTICATION_ERROR' ,
json . error ? . details || { }
) ;
}
2026-01-14 15:32:30 +00:00
// Store token after successful passkey login
2026-01-16 11:35:21 +10:30
if ( json . data ? . token ) {
tokenManager . setToken ( json . data . token , json . data . expires_at ? ? null ) ;
2026-01-14 15:32:30 +00:00
}
2026-01-16 11:35:21 +10:30
return json . data as WebAuthnLoginCompleteResponse ;
2026-01-14 15:32:30 +00:00
} ,
// Rename a passkey
renameCredential : ( credentialId : string , name : string ) = >
request < { message : string } > ( ` /auth/webauthn/credentials/ ${ credentialId } ` , {
method : 'PATCH' ,
body : JSON.stringify ( { name } ) ,
} ) ,
// Delete a passkey
deleteCredential : ( credentialId : string ) = >
request < { message : string } > ( ` /auth/webauthn/credentials/ ${ credentialId } ` , {
method : 'DELETE' ,
} ) ,
} ,
2026-01-16 17:31:25 +10:30
policies : {
// Get organization security policy
getOrgPolicy : ( orgId : string , requestConfig? : RequestConfig ) = >
request < OrgPolicyResponse > ( ` /organizations/ ${ orgId } /security-policy ` , { } , true , requestConfig ) ,
// Update organization security policy
updateOrgPolicy : ( orgId : string , body : UpdateOrgPolicyDto , requestConfig? : RequestConfig ) = >
request < OrgPolicyResponse > ( ` /organizations/ ${ orgId } /security-policy ` , {
method : 'PUT' ,
body : JSON.stringify ( body ) ,
} , true , requestConfig ) ,
// List organization compliance (paginated)
listOrgCompliance : ( orgId : string , params : Record < string , string > , requestConfig? : RequestConfig ) = >
request < OrgCompliancePage > (
` /organizations/ ${ orgId } /mfa-compliance? ${ new URLSearchParams ( params ) } ` ,
{ } ,
true ,
requestConfig
) ,
// Get current user's MFA compliance summary
getMyCompliance : ( ) = >
request < MfaComplianceSummary > ( '/users/me/mfa-compliance' ) ,
} ,
2026-01-20 15:54:11 +10:30
externalAuth : {
2026-01-21 03:09:38 +10:30
// List available providers
2026-01-20 15:54:11 +10:30
listProviders : ( ) = >
2026-01-21 03:09:38 +10:30
request < ExternalProvidersResponse > ( '/auth/external/providers' ) ,
2026-01-20 15:54:11 +10:30
2026-01-21 03:09:38 +10:30
// Get linked accounts for current user
2026-01-20 15:54:11 +10:30
listLinkedAccounts : ( ) = >
request < LinkedAccountsResponse > ( '/auth/external/linked-accounts' ) ,
2026-02-26 23:38:45 +05:45
// Initiate OAuth login flow — returns authorization_url to redirect the browser to
initiateLogin : ( provider : string , options ? : { redirect_uri? : string ; organization_id? : string ; flow? : string ; oidc_session_id? : string } ) = > {
const params = new URLSearchParams ( { flow : options?.flow ? ? 'login' } ) ;
if ( options ? . redirect_uri ) params . set ( 'redirect_uri' , options . redirect_uri ) ;
if ( options ? . organization_id ) params . set ( 'organization_id' , options . organization_id ) ;
if ( options ? . oidc_session_id ) params . set ( 'oidc_session_id' , options . oidc_session_id ) ;
return request < OAuthAuthorizeResponse > ( ` /auth/external/ ${ provider } /authorize? ${ params . toString ( ) } ` , {
2026-01-21 03:09:38 +10:30
method : 'GET' ,
2026-01-20 15:54:11 +10:30
credentials : 'include' ,
2026-02-26 23:38:45 +05:45
} , false ) ;
} ,
2026-01-20 15:54:11 +10:30
2026-01-21 03:09:38 +10:30
// Initiate account linking flow (requires auth)
initiateLink : ( provider : string , redirect_uri? : string ) = >
2026-01-20 15:54:11 +10:30
request < OAuthAuthorizeResponse > ( ` /auth/external/ ${ provider } /link ` , {
method : 'POST' ,
2026-01-21 03:09:38 +10:30
body : JSON.stringify ( { redirect_uri } ) ,
2026-01-20 15:54:11 +10:30
credentials : 'include' ,
} ) ,
2026-01-21 03:09:38 +10:30
// Unlink an external account
unlinkAccount : ( provider : string ) = >
request < { message : string } > ( ` /auth/external/ ${ provider } /unlink ` , {
method : 'DELETE' ,
credentials : 'include' ,
} ) ,
2026-01-20 15:54:11 +10:30
} ,
2026-02-27 21:08:16 +05:45
organizations : {
2026-03-01 20:11:22 +05:45
// Create a new organization (caller becomes owner)
create : ( name : string , slug : string , description? : string ) = >
request < { organization : Organization } > ( '/organizations' , {
method : 'POST' ,
body : JSON.stringify ( { name , slug , description } ) ,
} , true ) ,
2026-02-27 21:08:16 +05:45
// Get organization by ID
getById : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { organization : Organization ; member_count : number } > ( ` /organizations/ ${ orgId } ` , { } , true , requestConfig ) ,
2026-03-03 23:23:18 +05:45
// Delete an organization (owner only; pass confirm=true when other members exist)
deleteOrganization : ( orgId : string , confirm? : boolean , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } ` , {
method : 'DELETE' ,
. . . ( confirm ? { body : JSON.stringify ( { confirm : true } ) } : { } ) ,
} , true , requestConfig ) ,
2026-03-02 23:55:47 +05:45
2026-02-27 21:08:16 +05:45
// Get organization members
getMembers : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { members : OrganizationMember [ ] ; count : number } > ( ` /organizations/ ${ orgId } /members ` , { } , true , requestConfig ) ,
// Add member to organization
addMember : ( orgId : string , email : string , role : string , requestConfig? : RequestConfig ) = >
request < { member : OrganizationMember } > ( ` /organizations/ ${ orgId } /members ` , {
method : 'POST' ,
body : JSON.stringify ( { email , role } ) ,
} , true , requestConfig ) ,
// Remove member from organization
removeMember : ( orgId : string , userId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /members/ ${ userId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Update member role
updateMemberRole : ( orgId : string , userId : string , role : string , requestConfig? : RequestConfig ) = >
request < { member : OrganizationMember } > ( ` /organizations/ ${ orgId } /members/ ${ userId } /role ` , {
method : 'PATCH' ,
body : JSON.stringify ( { role } ) ,
} , true , requestConfig ) ,
// Get organization audit logs
getAuditLogs : ( orgId : string , params? : Record < string , string > , requestConfig? : RequestConfig ) = >
2026-03-06 14:19:54 +05:45
request < { audit_logs : AuditLogEntry [ ] ; count : number ; page : number ; per_page : number ; pages : number } > (
2026-02-27 21:08:16 +05:45
` /organizations/ ${ orgId } /audit-logs ${ params ? '?' + new URLSearchParams ( params ) . toString ( ) : '' } ` ,
{ } ,
true ,
requestConfig
) ,
// Get departments
getDepartments : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { departments : Department [ ] ; count : number } > ( ` /organizations/ ${ orgId } /departments ` , { } , true , requestConfig ) ,
// Create department
2026-03-08 18:08:42 +05:45
createDepartment : ( orgId : string , name : string , description? : string , canSudo? : boolean , requestConfig? : RequestConfig ) = >
2026-02-27 21:08:16 +05:45
request < { department : Department } > ( ` /organizations/ ${ orgId } /departments ` , {
method : 'POST' ,
2026-03-08 18:08:42 +05:45
body : JSON.stringify ( { name , description , can_sudo : canSudo } ) ,
2026-02-27 21:08:16 +05:45
} , true , requestConfig ) ,
// Update department
2026-03-08 18:08:42 +05:45
updateDepartment : ( orgId : string , deptId : string , data : { name? : string ; description? : string ; can_sudo? : boolean } , requestConfig? : RequestConfig ) = >
2026-02-27 21:08:16 +05:45
request < { department : Department } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } ` , {
method : 'PATCH' ,
body : JSON.stringify ( data ) ,
} , true , requestConfig ) ,
// Delete department
deleteDepartment : ( orgId : string , deptId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Get department members
getDepartmentMembers : ( orgId : string , deptId : string , requestConfig? : RequestConfig ) = >
request < { members : DepartmentMember [ ] ; count : number } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } /members ` , { } , true , requestConfig ) ,
// Add member to department
addDepartmentMember : ( orgId : string , deptId : string , email : string , requestConfig? : RequestConfig ) = >
request < { member : DepartmentMember } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } /members ` , {
method : 'POST' ,
body : JSON.stringify ( { email } ) ,
} , true , requestConfig ) ,
// Remove member from department
removeDepartmentMember : ( orgId : string , deptId : string , userId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } /members/ ${ userId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Get principals
getPrincipals : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { principals : Principal [ ] ; count : number } > ( ` /organizations/ ${ orgId } /principals ` , { } , true , requestConfig ) ,
// Create principal
createPrincipal : ( orgId : string , name : string , description? : string , requestConfig? : RequestConfig ) = >
request < { principal : Principal } > ( ` /organizations/ ${ orgId } /principals ` , {
method : 'POST' ,
body : JSON.stringify ( { name , description } ) ,
} , true , requestConfig ) ,
// Update principal
updatePrincipal : ( orgId : string , principalId : string , data : { name? : string ; description? : string } , requestConfig? : RequestConfig ) = >
request < { principal : Principal } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } ` , {
method : 'PATCH' ,
body : JSON.stringify ( data ) ,
} , true , requestConfig ) ,
// Delete principal
deletePrincipal : ( orgId : string , principalId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Get principal members
getPrincipalMembers : ( orgId : string , principalId : string , requestConfig? : RequestConfig ) = >
request < { members : PrincipalMember [ ] ; count : number } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } /members ` , { } , true , requestConfig ) ,
// Add member to principal
addPrincipalMember : ( orgId : string , principalId : string , email : string , requestConfig? : RequestConfig ) = >
request < { member : PrincipalMember } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } /members ` , {
method : 'POST' ,
body : JSON.stringify ( { email } ) ,
} , true , requestConfig ) ,
// Remove member from principal
removePrincipalMember : ( orgId : string , principalId : string , userId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } /members/ ${ userId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Link principal to department
linkPrincipalToDepartment : ( orgId : string , principalId : string , departmentId : string , requestConfig? : RequestConfig ) = >
2026-02-28 23:35:32 +05:45
request < { message : string } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } /departments/ ${ departmentId } ` , {
2026-02-27 21:08:16 +05:45
method : 'POST' ,
} , true , requestConfig ) ,
// Unlink principal from department
unlinkPrincipalFromDepartment : ( orgId : string , principalId : string , departmentId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } /departments/ ${ departmentId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
2026-02-28 23:35:32 +05:45
// Get departments linked to a principal
getPrincipalDepartments : ( orgId : string , principalId : string , requestConfig? : RequestConfig ) = >
request < { departments : Department [ ] ; count : number } > ( ` /organizations/ ${ orgId } /principals/ ${ principalId } /departments ` , { } , true , requestConfig ) ,
// Get principals linked to a department
getDepartmentPrincipals : ( orgId : string , deptId : string , requestConfig? : RequestConfig ) = >
request < { principals : Principal [ ] ; count : number } > ( ` /organizations/ ${ orgId } /departments/ ${ deptId } /principals ` , { } , true , requestConfig ) ,
2026-02-27 21:08:16 +05:45
// Create invite token
createInvite : ( orgId : string , email : string , role : string , requestConfig? : RequestConfig ) = >
request < { invite : OrgInvite } > ( ` /organizations/ ${ orgId } /invites ` , {
method : 'POST' ,
body : JSON.stringify ( { email , role } ) ,
} , true , requestConfig ) ,
2026-03-01 16:50:19 +05:45
// List pending invites for an organization
getInvites : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { invites : OrgInvite [ ] } > ( ` /organizations/ ${ orgId } /invites ` , { } , true , requestConfig ) ,
// Cancel (delete) an invite
cancelInvite : ( orgId : string , inviteId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /invites/ ${ inviteId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
2026-02-27 21:08:16 +05:45
// List OIDC clients
getClients : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { clients : OIDCClient [ ] ; count : number } > ( ` /organizations/ ${ orgId } /clients ` , { } , true , requestConfig ) ,
// Create OIDC client
createClient : ( orgId : string , name : string , redirect_uris : string [ ] , requestConfig? : RequestConfig ) = >
request < { client : OIDCClientWithSecret } > ( ` /organizations/ ${ orgId } /clients ` , {
method : 'POST' ,
body : JSON.stringify ( { name , redirect_uris } ) ,
} , true , requestConfig ) ,
// Delete OIDC client
deleteClient : ( orgId : string , clientId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /clients/ ${ clientId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Send MFA reminder to a member
sendMfaReminder : ( orgId : string , userId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /members/ ${ userId } /send-mfa-reminder ` , {
method : 'POST' ,
} , true , requestConfig ) ,
2026-02-28 23:35:32 +05:45
2026-03-02 23:55:47 +05:45
// Transfer organization ownership to another member
transferOwnership : ( orgId : string , newOwnerUserId : string , requestConfig? : RequestConfig ) = >
request < { previous_owner : OrganizationMember ; new_owner : OrganizationMember } > (
` /organizations/ ${ orgId } /transfer-ownership ` ,
{ method : 'POST' , body : JSON.stringify ( { new_owner_user_id : newOwnerUserId } ) } ,
true ,
requestConfig ,
) ,
2026-02-28 23:35:32 +05:45
// List Certificate Authorities for an org
getCAs : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { cas : OrgCA [ ] ; count : number } > ( ` /organizations/ ${ orgId } /cas ` , { } , true , requestConfig ) ,
// Create a new Certificate Authority for an org
createCA : ( orgId : string , data : { name : string ; description? : string ; ca_type ? : 'user' | 'host' ; key_type ? : 'ed25519' | 'rsa' | 'ecdsa' ; default_cert_validity_hours? : number ; max_cert_validity_hours? : number } , requestConfig? : RequestConfig ) = >
request < { ca : OrgCA } > ( ` /organizations/ ${ orgId } /cas ` , {
method : 'POST' ,
body : JSON.stringify ( data ) ,
} , true , requestConfig ) ,
// Update CA configuration
updateCA : ( orgId : string , caId : string , data : { default_cert_validity_hours? : number ; max_cert_validity_hours? : number } , requestConfig? : RequestConfig ) = >
request < { ca : OrgCA } > ( ` /organizations/ ${ orgId } /cas/ ${ caId } ` , {
method : 'PATCH' ,
body : JSON.stringify ( data ) ,
} , true , requestConfig ) ,
2026-03-01 20:11:22 +05:45
// Rotate (replace) a CA's key pair — returns updated CA + old_fingerprint
rotateCA : ( orgId : string , caId : string , data ? : { key_type ? : 'ed25519' | 'rsa' | 'ecdsa' ; reason? : string } , requestConfig? : RequestConfig ) = >
request < { ca : OrgCA ; old_fingerprint : string } > ( ` /organizations/ ${ orgId } /cas/ ${ caId } /rotate ` , {
method : 'POST' ,
body : JSON.stringify ( data ? ? { } ) ,
} , true , requestConfig ) ,
// Soft-delete a CA
deleteCA : ( orgId : string , caId : string , requestConfig? : RequestConfig ) = >
request < { ca_id : string } > ( ` /organizations/ ${ orgId } /cas/ ${ caId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
2026-03-08 18:08:42 +05:45
// Get API keys for organization
getApiKeys : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { api_keys : OrganizationApiKey [ ] ; count : number } > ( ` /organizations/ ${ orgId } /api-keys ` , { } , true , requestConfig ) ,
// Create new API key
createApiKey : ( orgId : string , name : string , description? : string , requestConfig? : RequestConfig ) = >
request < { api_key : OrganizationApiKey & { key? : string } } > ( ` /organizations/ ${ orgId } /api-keys ` , {
method : 'POST' ,
body : JSON.stringify ( { name , description } ) ,
} , true , requestConfig ) ,
// Update API key
updateApiKey : ( orgId : string , keyId : string , data : { name? : string ; description? : string } , requestConfig? : RequestConfig ) = >
request < { api_key : OrganizationApiKey } > ( ` /organizations/ ${ orgId } /api-keys/ ${ keyId } ` , {
method : 'PATCH' ,
body : JSON.stringify ( data ) ,
} , true , requestConfig ) ,
// Delete API key
deleteApiKey : ( orgId : string , keyId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /organizations/ ${ orgId } /api-keys/ ${ keyId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Get certificate audit logs for organization
getCertificateAuditLogs : ( orgId : string , params? : Record < string , string > , requestConfig? : RequestConfig ) = >
request < { audit_logs : CertificateAuditLog [ ] ; count : number ; page : number ; per_page : number ; pages : number } > (
` /organizations/ ${ orgId } /certificates/audit ${ params ? '?' + new URLSearchParams ( params ) . toString ( ) : '' } ` ,
{ } ,
true ,
requestConfig
) ,
2026-02-27 21:08:16 +05:45
} ,
invites : {
// Get invite details by token (unauthenticated)
getInfo : ( token : string ) = >
2026-03-01 16:50:19 +05:45
request < { email : string ; organization : { id : string ; name : string } ; role : string ; user_exists? : boolean } > (
2026-02-27 21:08:16 +05:45
` /invites/ ${ token } ` ,
{ } ,
false ,
) ,
2026-03-04 18:43:12 +05:45
// Accept invite — sends Bearer token if present (OAuth users skip password)
2026-03-01 16:50:19 +05:45
accept : ( token : string , full_name? : string , password? : string ) = >
2026-02-27 21:08:16 +05:45
request < LoginResponse > (
` /invites/ ${ token } /accept ` ,
{
method : 'POST' ,
body : JSON.stringify ( { full_name , password , password_confirm : password } ) ,
} ,
2026-03-04 18:43:12 +05:45
true ,
2026-02-27 21:08:16 +05:45
) ,
} ,
2026-02-28 23:35:32 +05:45
ssh : {
// List all SSH keys for the current user
listKeys : ( requestConfig? : RequestConfig ) = >
request < SSHKeysResponse > ( '/ssh/keys' , { } , true , requestConfig ) ,
// Add a new SSH public key
addKey : ( public_key : string , description? : string , requestConfig? : RequestConfig ) = >
request < SSHKey > ( '/ssh/keys' , {
method : 'POST' ,
body : JSON.stringify ( { public_key , description } ) ,
} , true , requestConfig ) ,
// Delete an SSH key
deleteKey : ( keyId : string , requestConfig? : RequestConfig ) = >
request < { status : string } > ( ` /ssh/keys/ ${ keyId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
// Update SSH key description
updateKeyDescription : ( keyId : string , description : string , requestConfig? : RequestConfig ) = >
request < SSHKey > ( ` /ssh/keys/ ${ keyId } /update-description ` , {
method : 'PATCH' ,
body : JSON.stringify ( { description } ) ,
} , true , requestConfig ) ,
// Get a verification challenge for a key
getChallenge : ( keyId : string , requestConfig? : RequestConfig ) = >
request < SSHChallengeResponse > ( ` /ssh/keys/ ${ keyId } /verify ` , { } , true , requestConfig ) ,
// Submit signature to verify key ownership
verifyKey : ( keyId : string , signature : string , requestConfig? : RequestConfig ) = >
request < SSHVerifyResponse > ( ` /ssh/keys/ ${ keyId } /verify ` , {
method : 'POST' ,
body : JSON.stringify ( { signature , action : 'verify_signature' } ) ,
} , true , requestConfig ) ,
// Sign a certificate for the given key
signCertificate : ( key_id : string , principals? : string [ ] , cert_type ? : 'user' | 'host' , expiry_hours? : number , requestConfig? : RequestConfig ) = >
request < SSHSignResponse > ( '/ssh/sign' , {
method : 'POST' ,
body : JSON.stringify ( { key_id , principals , cert_type , expiry_hours } ) ,
} , true , requestConfig ) ,
2026-03-03 18:02:21 +05:45
// Issue a host certificate by submitting a raw server host public key
// (admin-only; does not require a pre-registered SSHKey record)
signHostCert : (
hostPublicKey : string ,
principals : string [ ] ,
validityHours : number ,
caId : string ,
requestConfig? : RequestConfig ,
) = >
request < SSHSignResponse > ( '/ssh/sign/host' , {
method : 'POST' ,
body : JSON.stringify ( {
host_public_key : hostPublicKey ,
principals ,
validity_hours : validityHours ,
ca_id : caId ,
} ) ,
} , true , requestConfig ) ,
2026-03-01 16:50:19 +05:45
// Get the merged department certificate policy for the current user (used in sign dialog)
getMyDeptCertPolicy : ( requestConfig? : RequestConfig ) = >
request < { policy : DeptCertPolicy } > ( '/ssh/dept-cert-policy' , { } , true , requestConfig ) ,
2026-02-28 23:35:32 +05:45
// List issued certificates for the current user
listCertificates : ( requestConfig? : RequestConfig ) = >
request < { certificates : SSHCertificate [ ] ; count : number } > ( '/ssh/certificates' , { } , true , requestConfig ) ,
// Get a single certificate (includes full cert text)
getCertificate : ( certId : string , requestConfig? : RequestConfig ) = >
request < SSHCertificate > ( ` /ssh/certificates/ ${ certId } ` , { } , true , requestConfig ) ,
// Revoke a certificate
revokeCertificate : ( certId : string , reason? : string , requestConfig? : RequestConfig ) = >
request < { status : string ; cert_id : string ; reason : string } > ( ` /ssh/certificates/ ${ certId } /revoke ` , {
method : 'POST' ,
body : JSON.stringify ( { reason } ) ,
} , true , requestConfig ) ,
// Get the CA public key for the current user's org
getCaPublicKey : ( requestConfig? : RequestConfig ) = >
request < { public_key : string ; fingerprint : string ; ca_name : string ; source : string } > ( '/ssh/ca/public-key' , { } , true , requestConfig ) ,
// Add SSH key on behalf of another user (admin)
adminAddKey : ( userId : string , public_key : string , description? : string , requestConfig? : RequestConfig ) = >
request < SSHKey > ( ` /ssh/keys/admin/ ${ userId } ` , {
method : 'POST' ,
body : JSON.stringify ( { public_key , description } ) ,
} , true , requestConfig ) ,
// List CA permissions for a CA
listCaPermissions : ( caId : string , requestConfig? : RequestConfig ) = >
request < { ca_id : string ; permissions : CAPermission [ ] ; open_to_all : boolean } > ( ` /ssh/ca/ ${ caId } /permissions ` , { } , true , requestConfig ) ,
// Grant a user permission on a CA
addCaPermission : ( caId : string , user_id : string , permission : 'sign' | 'admin' , requestConfig? : RequestConfig ) = >
request < { message : string ; permission : CAPermission } > ( ` /ssh/ca/ ${ caId } /permissions ` , {
method : 'POST' ,
body : JSON.stringify ( { user_id , permission } ) ,
} , true , requestConfig ) ,
// Revoke a user's CA permission
removeCaPermission : ( caId : string , userId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > ( ` /ssh/ca/ ${ caId } /permissions/ ${ userId } ` , {
method : 'DELETE' ,
} , true , requestConfig ) ,
} ,
2026-03-20 21:52:52 +10:30
zerotier : {
// ── Portal Networks ─────────────────────────────────────────────────────────
listNetworks : ( orgId : string , includeInactive = false , requestConfig? : RequestConfig ) = >
request < { networks : PortalNetwork [ ] ; count : number } > (
` /organizations/ ${ orgId } /networks ${ includeInactive ? "?include_inactive=true" : "" } ` ,
{ } , true , requestConfig ,
) ,
createNetwork : ( orgId : string , data : {
name : string ;
zerotier_network_id : string ;
description? : string ;
environment? : string ;
request_mode? : string ;
default_activation_lifetime_minutes? : number ;
max_activation_lifetime_minutes? : number ;
} , requestConfig? : RequestConfig ) = >
request < { network : PortalNetwork } > (
` /organizations/ ${ orgId } /networks ` ,
{ method : "POST" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
getNetwork : ( orgId : string , networkId : string , requestConfig? : RequestConfig ) = >
request < { network : PortalNetwork } > (
` /organizations/ ${ orgId } /networks/ ${ networkId } ` ,
{ } , true , requestConfig ,
) ,
updateNetwork : ( orgId : string , networkId : string , data : Record < string , unknown > , requestConfig? : RequestConfig ) = >
request < { network : PortalNetwork } > (
` /organizations/ ${ orgId } /networks/ ${ networkId } ` ,
{ method : "PATCH" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
deleteNetwork : ( orgId : string , networkId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > (
` /organizations/ ${ orgId } /networks/ ${ networkId } ` ,
{ method : "DELETE" } , true , requestConfig ,
) ,
2026-03-29 21:33:37 +05:45
/** List all ZeroTier networks from the org's controller/account, annotated
* with whether each is already managed as a portal network. */
listAvailableZtNetworks : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { networks : AvailableZtNetwork [ ] ; count : number ; zt_error? : string } > (
` /organizations/ ${ orgId } /zerotier/available-networks ` ,
{ } , true , requestConfig ,
) ,
2026-03-20 21:52:52 +10:30
getNetworkMembers : ( orgId : string , networkId : string , requestConfig? : RequestConfig ) = >
request < { memberships : DeviceNetworkMembership [ ] ; count : number } > (
` /organizations/ ${ orgId } /networks/ ${ networkId } /members ` ,
{ } , true , requestConfig ,
) ,
getNetworkPendingRequests : ( orgId : string , networkId : string , requestConfig? : RequestConfig ) = >
request < { requests : UserNetworkApproval [ ] ; count : number } > (
` /organizations/ ${ orgId } /networks/ ${ networkId } /requests ` ,
{ } , true , requestConfig ,
) ,
// ── Devices ───────────────────────────────────────────────────────────────
listDevices : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { devices : Device [ ] ; count : number } > (
` /organizations/ ${ orgId } /devices ` , { } , true , requestConfig ,
) ,
registerDevice : ( orgId : string , data : {
node_id : string ;
nickname? : string ;
hostname? : string ;
asset_tag? : string ;
serial_number? : string ;
} , requestConfig? : RequestConfig ) = >
request < { device : Device ; memberships_created : number } > (
` /organizations/ ${ orgId } /devices ` ,
{ method : "POST" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
getDevice : ( orgId : string , deviceId : string , requestConfig? : RequestConfig ) = >
request < { device : Device } > (
` /organizations/ ${ orgId } /devices/ ${ deviceId } ` ,
{ } , true , requestConfig ,
) ,
updateDevice : ( orgId : string , deviceId : string , data : {
nickname? : string ;
hostname? : string ;
asset_tag? : string ;
serial_number? : string ;
} , requestConfig? : RequestConfig ) = >
request < { device : Device } > (
` /organizations/ ${ orgId } /devices/ ${ deviceId } ` ,
{ method : "PATCH" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
removeDevice : ( orgId : string , deviceId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > (
` /organizations/ ${ orgId } /devices/ ${ deviceId } ` ,
{ method : "DELETE" } , true , requestConfig ,
) ,
// ── Approvals ─────────────────────────────────────────────────────────────
requestAccess : ( orgId : string , data : {
portal_network_id : string ;
device_id : string ;
justification? : string ;
} , requestConfig? : RequestConfig ) = >
request < { approval : UserNetworkApproval } > (
` /organizations/ ${ orgId } /approvals ` ,
{ method : "POST" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
listMyApprovals : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { approvals : UserNetworkApproval [ ] ; count : number } > (
` /organizations/ ${ orgId } /approvals ` , { } , true , requestConfig ,
) ,
2026-03-29 21:33:37 +05:45
adminListAllApprovals : ( orgId : string , networkId? : string , state? : string , requestConfig? : RequestConfig ) = >
request < { approvals : UserNetworkApproval [ ] ; count : number } > (
` /organizations/ ${ orgId } /admin/approvals ${ networkId || state ? ` ? ${ new URLSearchParams ( Object . fromEntries ( Object . entries ( { network_id : networkId , state } ).filter(([, v]) => v != null) as [string, string][]))} ` : "" } ` ,
{ } , true , requestConfig ,
) ,
2026-03-20 21:52:52 +10:30
listPendingApprovals : ( orgId : string , networkId? : string , requestConfig? : RequestConfig ) = >
request < { approvals : UserNetworkApproval [ ] ; count : number } > (
` /organizations/ ${ orgId } /approvals/pending ${ networkId ? ` ?network_id= ${ networkId } ` : "" } ` ,
{ } , true , requestConfig ,
) ,
approveRequest : ( orgId : string , approvalId : string , requestConfig? : RequestConfig ) = >
request < { approval : UserNetworkApproval } > (
` /organizations/ ${ orgId } /approvals/ ${ approvalId } /approve ` ,
{ method : "POST" } , true , requestConfig ,
) ,
rejectRequest : ( orgId : string , approvalId : string , requestConfig? : RequestConfig ) = >
request < { approval : UserNetworkApproval } > (
` /organizations/ ${ orgId } /approvals/ ${ approvalId } /reject ` ,
{ method : "POST" } , true , requestConfig ,
) ,
revokeApproval : ( orgId : string , approvalId : string , requestConfig? : RequestConfig ) = >
request < { approval : UserNetworkApproval } > (
` /organizations/ ${ orgId } /approvals/ ${ approvalId } /revoke ` ,
{ method : "POST" } , true , requestConfig ,
) ,
assignAccess : ( orgId : string , data : {
target_user_id : string ;
portal_network_id : string ;
justification? : string ;
} , requestConfig? : RequestConfig ) = >
request < { approval : UserNetworkApproval } > (
` /organizations/ ${ orgId } /approvals/assign ` ,
{ method : "POST" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
// ── Memberships ────────────────────────────────────────────────────────────
listMemberships : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { memberships : DeviceNetworkMembership [ ] ; count : number } > (
` /organizations/ ${ orgId } /memberships ` , { } , true , requestConfig ,
) ,
activateMembership : ( orgId : string , membershipId : string , lifetimeMinutes? : number , requestConfig? : RequestConfig ) = >
request < { session : ActivationSession ; membership : DeviceNetworkMembership } > (
` /organizations/ ${ orgId } /memberships/ ${ membershipId } /activate ` ,
{ method : "POST" , body : JSON.stringify ( { lifetime_minutes : lifetimeMinutes } ) } ,
true , requestConfig ,
) ,
deactivateMembership : ( orgId : string , membershipId : string , requestConfig? : RequestConfig ) = >
request < { membership : DeviceNetworkMembership } > (
` /organizations/ ${ orgId } /memberships/ ${ membershipId } /deactivate ` ,
{ method : "POST" } , true , requestConfig ,
) ,
activateAllMemberships : ( orgId : string , lifetimeMinutes? : number , requestConfig? : RequestConfig ) = >
request < { sessions : ActivationSession [ ] ; count : number } > (
` /organizations/ ${ orgId } /memberships/activate-all ` ,
{ method : "POST" , body : JSON.stringify ( { lifetime_minutes : lifetimeMinutes } ) } ,
true , requestConfig ,
) ,
joinNetworkForDevice : ( orgId : string , deviceId : string , networkId : string , requestConfig? : RequestConfig ) = >
request < { membership : DeviceNetworkMembership } > (
` /organizations/ ${ orgId } /devices/ ${ deviceId } /join-network/ ${ networkId } ` ,
{ method : "POST" } ,
true , requestConfig ,
) ,
deleteMembership : ( orgId : string , membershipId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > (
` /organizations/ ${ orgId } /memberships/ ${ membershipId } ` ,
{ method : "DELETE" } ,
true , requestConfig ,
) ,
// ── Admin ─────────────────────────────────────────────────────────────────
adminListAllMemberships : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { memberships : EnrichedMembership [ ] ; count : number } > (
` /organizations/ ${ orgId } /admin/memberships ` ,
{ } ,
true ,
requestConfig ,
) ,
adminDeleteMembership : ( orgId : string , membershipId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > (
` /organizations/ ${ orgId } /admin/memberships/ ${ membershipId } ` ,
{ method : "DELETE" } ,
true , requestConfig ,
) ,
// ── Sessions ──────────────────────────────────────────────────────────────
listSessions : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { sessions : ActivationSession [ ] ; count : number } > (
` /organizations/ ${ orgId } /sessions ` , { } , true , requestConfig ,
) ,
endSession : ( orgId : string , sessionId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > (
` /organizations/ ${ orgId } /sessions/ ${ sessionId } ` ,
{ method : "DELETE" } , true , requestConfig ,
) ,
// ── Kill Switch ───────────────────────────────────────────────────────────
triggerKillSwitch : ( orgId : string , data : {
target_user_id : string ;
scope? : string ;
reason? : string ;
network_ids? : string [ ] ;
} , requestConfig? : RequestConfig ) = >
request < { event : KillSwitchEvent } > (
` /organizations/ ${ orgId } /kill-switch ` ,
{ method : "POST" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
2026-03-29 21:33:37 +05:45
// ── ZeroTier Controller (org-scoped admin) ─────────────────────────────────
getZtStatus : ( orgId : string , requestConfig? : RequestConfig ) = >
2026-03-20 21:52:52 +10:30
request < { status : Record < string , unknown > } > (
2026-03-29 21:33:37 +05:45
` /admin/zerotier/status?org_id= ${ orgId } ` , { } , true , requestConfig ,
2026-03-20 21:52:52 +10:30
) ,
2026-03-29 21:33:37 +05:45
listZtNetworks : ( orgId : string , requestConfig? : RequestConfig ) = >
2026-03-20 21:52:52 +10:30
request < { networks : ZeroTierNetwork [ ] ; count : number } > (
2026-03-29 21:33:37 +05:45
` /admin/zerotier/networks?org_id= ${ orgId } ` , { } , true , requestConfig ,
2026-03-20 21:52:52 +10:30
) ,
2026-03-29 21:33:37 +05:45
getZtNetwork : ( orgId : string , networkId : string , requestConfig? : RequestConfig ) = >
2026-03-20 21:52:52 +10:30
request < { network : ZeroTierNetwork } > (
2026-03-29 21:33:37 +05:45
` /admin/zerotier/networks/ ${ networkId } ?org_id= ${ orgId } ` , { } , true , requestConfig ,
2026-03-20 21:52:52 +10:30
) ,
2026-03-29 21:33:37 +05:45
listZtMembers : ( orgId : string , networkId : string , requestConfig? : RequestConfig ) = >
2026-03-20 21:52:52 +10:30
request < { members : ZeroTierMember [ ] ; count : number } > (
2026-03-29 21:33:37 +05:45
` /admin/zerotier/networks/ ${ networkId } /members?org_id= ${ orgId } ` , { } , true , requestConfig ,
2026-03-20 21:52:52 +10:30
) ,
triggerReconciliation : ( requestConfig? : RequestConfig ) = >
request < { networks_processed : number ; errors : number } > (
"/admin/zerotier/reconcile" ,
{ method : "POST" } , true , requestConfig ,
) ,
2026-03-29 21:33:37 +05:45
// ── Per-org ZeroTier config ──────────────────────────────────────────────
getOrgZtConfig : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { zerotier_config : ZeroTierOrgConfig } > (
` /organizations/ ${ orgId } /zerotier-config ` ,
{ } , true , requestConfig ,
) ,
setOrgZtConfig : ( orgId : string , data : ZeroTierOrgConfigInput , requestConfig? : RequestConfig ) = >
request < { zerotier_config : ZeroTierOrgConfig ; connectivity_test : { ok : boolean ; error : string | null } } > (
` /organizations/ ${ orgId } /zerotier-config ` ,
{ method : "PUT" , body : JSON.stringify ( data ) } ,
true , requestConfig ,
) ,
deleteOrgZtConfig : ( orgId : string , requestConfig? : RequestConfig ) = >
request < { message : string } > (
` /organizations/ ${ orgId } /zerotier-config ` ,
{ method : "DELETE" } , true , requestConfig ,
) ,
2026-03-20 21:52:52 +10:30
} ,
2026-01-06 15:33:03 +00:00
} ;
2026-02-27 21:08:16 +05:45
// Organization types
export interface OrganizationMember {
id : string ;
user_id : string ;
organization_id : string ;
role : string ;
created_at : string ;
updated_at : string ;
user? : User ;
}
export interface AuditLogEntry {
id : string ;
action : string ;
user_id : string | null ;
organization_id : string | null ;
resource_type : string | null ;
resource_id : string | null ;
ip_address : string | null ;
user_agent : string | null ;
request_id : string | null ;
description : string | null ;
success : boolean ;
error_message : string | null ;
metadata? : Record < string , unknown > ;
created_at : string ;
updated_at : string ;
user? : User ;
}
export interface Department {
id : string ;
organization_id : string ;
name : string ;
description : string | null ;
2026-03-08 18:08:42 +05:45
can_sudo : boolean ;
2026-02-27 21:08:16 +05:45
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
}
2026-03-01 16:50:19 +05:45
export const STANDARD_SSH_EXTENSIONS = [
'permit-X11-forwarding' ,
'permit-agent-forwarding' ,
'permit-pty' ,
'permit-port-forwarding' ,
'permit-user-rc' ,
] as const ;
export interface DeptCertPolicy {
department_id : string ;
allow_user_expiry : boolean ;
default_expiry_hours : number ;
max_expiry_hours : number ;
allowed_extensions : string [ ] ;
custom_extensions : string [ ] ;
all_extensions? : string [ ] ;
standard_extensions? : string [ ] ;
}
export interface PendingInvite {
token : string ;
organization : { id : string ; name : string } ;
role : string ;
expires_at : string ;
}
export interface MyOrgMembership {
org_id : string ;
org_name : string ;
role : string ;
departments : { id : string ; name : string ; description : string | null } [ ] ;
principals : { id : string ; name : string ; description : string | null ; via_department : boolean } [ ] ;
}
2026-02-27 21:08:16 +05:45
export interface DepartmentMember {
id : string ;
user_id : string ;
department_id : string ;
created_at : string ;
updated_at : string ;
user? : User ;
}
export interface Principal {
id : string ;
organization_id : string ;
name : string ;
description : string | null ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
}
export interface PrincipalMember {
id : string ;
user_id : string ;
principal_id : string ;
created_at : string ;
updated_at : string ;
user? : User ;
}
export interface OrgInvite {
id : string ;
email : string ;
role : string ;
expires_at : string ;
2026-03-01 16:50:19 +05:45
invite_link? : string ; // only present on create response (dev/when email disabled)
2026-02-27 21:08:16 +05:45
}
export interface OIDCClient {
id : string ;
name : string ;
client_id : string ;
redirect_uris : string [ ] ;
scopes : string [ ] ;
grant_types : string [ ] ;
is_active : boolean ;
created_at : string ;
}
export interface OIDCClientWithSecret extends OIDCClient {
client_secret : string ;
}
2026-01-16 17:31:25 +10:30
// Policy types
export interface OrgPolicyResponse {
security_policy : {
organization_id : string ;
mfa_policy_mode : string ;
mfa_grace_period_days : number ;
notify_days_before : number ;
policy_version : number ;
} ;
}
export interface UpdateOrgPolicyDto {
mfa_policy_mode : string ;
mfa_grace_period_days : number ;
notify_days_before : number ;
}
export interface OrgCompliancePage {
members : OrgComplianceMember [ ] ;
count : number ;
page : number ;
page_size : number ;
}
export interface OrgComplianceMember {
user_id : string ;
user_email : string ;
user_name : string ;
status : string ;
deadline_at : string | null ;
compliant_at : string | null ;
last_notified_at : string | null ;
}
2026-01-06 15:33:03 +00:00
export { ApiError } ;
2026-01-16 17:31:25 +10:30
2026-02-28 23:35:32 +05:45
// SSH Key types
export interface SSHKey {
id : string ;
user_id : string ;
public_key : string ;
description : string | null ;
key_type : string | null ;
fingerprint : string | null ;
verified : boolean ;
verified_at : string | null ;
created_at : string ;
updated_at : string ;
}
export interface SSHKeysResponse {
keys : SSHKey [ ] ;
count : number ;
}
export interface SSHChallengeResponse {
challenge_text : string ;
validationText : string ;
key_id : string ;
}
export interface SSHVerifyResponse {
verified : boolean ;
}
export interface SSHCertificate {
id : string ;
user_id : string ;
ssh_key_id : string | null ;
certificate : string ;
serial : number | null ;
key_id : string | null ;
cert_type : string ;
principals : string [ ] ;
valid_after : string ;
valid_before : string ;
revoked : boolean ;
status : string ;
created_at : string ;
}
export interface SSHSignResponse {
certificate : string ;
serial : number ;
principals : string [ ] ;
valid_after : string ;
valid_before : string ;
cert_id? : string ;
}
export interface CAPermission {
id : string ;
ca_id : string ;
user_id : string ;
user_email : string | null ;
permission : 'sign' | 'admin' ;
created_at : string ;
}
export interface OrgCA {
id : string ;
organization_id : string | null ;
name : string ;
description : string | null ;
ca_type : 'user' | 'host' ;
key_type : string ;
public_key : string ;
fingerprint : string ;
is_active : boolean ;
2026-03-01 20:11:22 +05:45
/** True when this entry represents the server-wide config-file CA.
* System CAs are read-only — they cannot be edited, deleted, or replaced
* from the UI. */
is_system? : boolean ;
2026-02-28 23:35:32 +05:45
default_cert_validity_hours : number ;
max_cert_validity_hours : number ;
total_certs : number ;
active_certs : number ;
revoked_certs : number ;
2026-03-02 23:55:47 +05:45
/** Next serial number that will be assigned when a certificate is issued. */
next_serial_number : number | null ;
2026-03-01 20:11:22 +05:45
created_at : string | null ;
updated_at : string | null ;
/** Set when the key was last rotated. */
rotated_at : string | null ;
/** Reason provided when the key was last rotated. */
rotation_reason : string | null ;
2026-02-28 23:35:32 +05:45
}
2026-01-16 17:31:25 +10:30
// Reusable 403 error handler for API calls
// Shows a user-friendly toast message when access is denied
export function create403Handler ( toastFn : ( options : { title : string ; description : string ; variant : "destructive" } ) = > void ) {
return ( error : ApiError ) = > {
console . warn ( '[API] 403 Access Denied:' , error . message ) ;
toastFn ( {
title : "Access Denied" ,
description : "You don't have permission to view this section. Please contact your organization administrator." ,
variant : "destructive" ,
} ) ;
} ;
2026-03-20 21:52:52 +10:30
}
// ── ZeroTier / Portal Network Types ──────────────────────────────────────────
export type NetworkEnvironment = "production" | "staging" | "development" | "lab" ;
export type NetworkRequestMode = "open" | "approval_required" | "invite_only" ;
export type ApprovalGrantType = "requested" | "assigned" ;
export type ApprovalState = "pending" | "approved" | "rejected" | "revoked" | "suspended" ;
export type MembershipState =
| "pending_device_registration"
| "pending_request"
| "pending_manager_approval"
| "approved_inactive"
| "joined_deauthorized"
| "active_authorized"
| "activation_expired"
| "suspended"
| "revoked"
| "rejected" ;
export type ActivationEndReason = "expired" | "logout" | "kill_switch" | "manual_revoke" | "approval_revoked" | "admin_action" ;
export type KillSwitchScope = "organization" | "global" | "selected_networks" ;
export type DeviceStatus = "active" | "inactive" ;
export interface PortalNetwork {
id : string ;
organization_id : string ;
name : string ;
description : string | null ;
owner_user_id : string ;
zerotier_network_id : string ;
environment : NetworkEnvironment ;
request_mode : NetworkRequestMode ;
default_activation_lifetime_minutes : number ;
max_activation_lifetime_minutes : number | null ;
is_active : boolean ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
approved_user_count? : number ;
active_membership_count? : number ;
}
2026-03-29 21:33:37 +05:45
/** A ZeroTier network returned from the controller, annotated with whether
* it is already managed as a portal network in Secuird. */
export interface AvailableZtNetwork {
id : string ;
name : string ;
description : string | null ;
owner_id : string | null ;
online_member_count : number ;
authorized_member_count : number ;
total_member_count : number ;
already_managed : boolean ;
portal_network_id : string | null ;
portal_network_name : string | null ;
}
2026-03-20 21:52:52 +10:30
export interface Device {
id : string ;
user_id : string ;
organization_id : string ;
node_id : string ;
device_nickname : string | null ;
hostname : string | null ;
asset_tag : string | null ;
serial_number : string | null ;
status : DeviceStatus ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
display_name? : string ;
active_membership_count? : number ;
}
export interface UserNetworkApproval {
id : string ;
organization_id : string ;
user_id : string ;
portal_network_id : string ;
granted_by_user_id : string | null ;
grant_type : ApprovalGrantType ;
state : ApprovalState ;
justification : string | null ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
active_membership_count? : number ;
}
export interface DeviceNetworkMembership {
id : string ;
organization_id : string ;
user_id : string ;
device_id : string ;
portal_network_id : string ;
user_network_approval_id : string | null ;
state : MembershipState ;
join_seen : boolean ;
currently_authorized : boolean ;
approved_for_activation : boolean ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
active_session : ActivationSession | null ;
}
export interface EnrichedMembership {
id : string ;
user_id : string ;
user_email : string | null ;
user_full_name : string | null ;
device_id : string ;
device_nickname : string | null ;
device_hostname : string | null ;
device_node_id : string | null ;
device_status : DeviceStatus | null ;
portal_network_id : string ;
network_name : string | null ;
network_environment : NetworkEnvironment | null ;
state : MembershipState | null ;
join_seen : boolean ;
currently_authorized : boolean ;
approved_for_activation : boolean ;
user_network_approval_id : string | null ;
approval_state : ApprovalState | null ;
active_session : ActivationSession | null ;
created_at : string | null ;
updated_at : string | null ;
}
export interface ActivationSession {
id : string ;
organization_id : string ;
user_id : string ;
device_network_membership_id : string ;
authenticated_at : string ;
expires_at : string ;
ended_at : string | null ;
end_reason : ActivationEndReason | null ;
created_by : string ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
is_expired : boolean ;
is_active : boolean ;
}
export interface KillSwitchEvent {
id : string ;
organization_id : string ;
target_user_id : string ;
scope : KillSwitchScope ;
triggered_by_user_id : string ;
reason : string | null ;
network_ids : string [ ] | null ;
created_at : string ;
updated_at : string ;
deleted_at : string | null ;
}
export interface ZeroTierMember {
id : string ;
network_id : string ;
node_id : string ;
name : string | null ;
description : string | null ;
hidden : boolean ;
is_authorized : boolean ;
display_name : string ;
ip_list : string ;
last_online : number | null ;
last_seen : number | null ;
last_seen_str : string ;
client_version : string | null ;
controller_id : string | null ;
config : {
authorized : boolean ;
active_bridge : boolean ;
ip_assignments : string [ ] ;
creation_time : number | null ;
last_authorized_time : number | null ;
last_deauthorized_time : number | null ;
version_string : string ;
} ;
}
export interface ZeroTierNetwork {
id : string ;
name : string ;
description : string | null ;
owner_id : string | null ;
online_member_count : number ;
authorized_member_count : number ;
total_member_count : number ;
config : {
name : string ;
private : boolean ;
creation_time : number | null ;
ip_assignment_pools : Record < string , unknown > [ ] ;
routes : Record < string , unknown > [ ] ;
} ;
2026-03-29 21:33:37 +05:45
}
/** Current per-org ZeroTier config as returned by GET /organizations/:id/zerotier-config */
export interface ZeroTierOrgConfig {
/** Whether an API token has been saved (the actual value is never returned). */
zt_api_token_set : boolean ;
/** Custom controller / Central base URL, or null when server default is used. */
zt_api_url : string | null ;
/** "central" | "controller", or null when server default is used. */
zt_api_mode : "central" | "controller" | null ;
}
/** Body for PUT /organizations/:id/zerotier-config */
export interface ZeroTierOrgConfigInput {
zt_api_token : string ;
zt_api_url : string ;
zt_api_mode : "central" | "controller" ;
2026-01-16 17:31:25 +10:30
}