feat(reception): add passport device authentication flows and alert delivery management

This commit is contained in:
2026-04-20 10:26:22 +00:00
parent 3cd7499f3f
commit 6044928c70
26 changed files with 2943 additions and 4 deletions
+3
View File
@@ -3,6 +3,9 @@ export type TActivityAction =
| 'logout'
| 'session_created'
| 'session_revoked'
| 'passport_device_enrolled'
| 'passport_device_revoked'
| 'passport_challenge_approved'
| 'org_created'
| 'org_joined'
| 'org_left'
+35
View File
@@ -0,0 +1,35 @@
export type TAlertSeverity = 'low' | 'medium' | 'high' | 'critical';
export type TAlertStatus = 'pending' | 'seen' | 'dismissed';
export type TAlertCategory = 'security' | 'admin' | 'system';
export type TAlertNotificationStatus = 'pending' | 'sent' | 'failed' | 'seen';
export interface IAlert {
id: string;
data: {
recipientUserId: string;
organizationId?: string;
category: TAlertCategory;
eventType: string;
severity: TAlertSeverity;
title: string;
body: string;
actorUserId?: string;
relatedEntityId?: string;
relatedEntityType?: string;
notification: {
hintId: string;
status: TAlertNotificationStatus;
attemptCount: number;
createdAt: number;
deliveredAt?: number | null;
seenAt?: number | null;
lastError?: string | null;
};
createdAt: number;
seenAt?: number | null;
dismissedAt?: number | null;
};
}
+22
View File
@@ -0,0 +1,22 @@
import type { TAlertSeverity } from './alert.js';
export type TAlertRuleScope = 'global' | 'organization';
export type TAlertRuleRecipientMode = 'global_admins' | 'org_admins' | 'specific_users';
export interface IAlertRule {
id: string;
data: {
scope: TAlertRuleScope;
organizationId?: string;
eventType: string;
minimumSeverity: TAlertSeverity;
recipientMode: TAlertRuleRecipientMode;
recipientUserIds?: string[];
push: boolean;
enabled: boolean;
createdByUserId: string;
createdAt: number;
updatedAt: number;
};
}
+5
View File
@@ -1,5 +1,7 @@
export * from './abusewindow.js';
export * from './activity.js';
export * from './alert.js';
export * from './alertrule.js';
export * from './app.js';
export * from './emailactiontoken.js';
export * from './oidc.js';
@@ -10,6 +12,9 @@ export * from './jwt.js';
export * from './loginsession.js';
export * from './organization.js';
export * from './paddlecheckoutdata.js';
export * from './passportchallenge.js';
export * from './passportdevice.js';
export * from './passportnonce.js';
export * from './registrationsession.js';
export * from './role.js';
export * from './user.js';
+63
View File
@@ -0,0 +1,63 @@
import type { IPassportCapabilities } from './passportdevice.js';
export type TPassportChallengeType =
| 'device_enrollment'
| 'authentication'
| 'step_up'
| 'physical_access';
export type TPassportChallengeStatus = 'pending' | 'approved' | 'expired' | 'rejected';
export type TPassportChallengeDeliveryStatus = 'pending' | 'sent' | 'failed' | 'seen';
export type TPassportSignatureFormat = 'raw' | 'der';
export interface IPassportLocationEvidence {
latitude: number;
longitude: number;
accuracyMeters: number;
capturedAt: number;
}
export interface IPassportNfcEvidence {
tagId?: string;
readerId?: string;
}
export interface IPassportChallenge {
id: string;
data: {
userId: string;
deviceId?: string | null;
type: TPassportChallengeType;
status: TPassportChallengeStatus;
tokenHash?: string | null;
challenge: string;
metadata: {
originHost?: string;
audience?: string;
notificationTitle?: string;
deviceLabel?: string;
requireLocation: boolean;
requireNfc: boolean;
requestedCapabilities?: Partial<IPassportCapabilities>;
};
evidence?: {
signatureFormat?: TPassportSignatureFormat;
location?: IPassportLocationEvidence;
nfc?: IPassportNfcEvidence;
};
notification?: {
hintId: string;
status: TPassportChallengeDeliveryStatus;
attemptCount: number;
createdAt: number;
deliveredAt?: number | null;
seenAt?: number | null;
lastError?: string | null;
};
createdAt: number;
expiresAt: number;
completedAt?: number | null;
};
}
+46
View File
@@ -0,0 +1,46 @@
export type TPassportDevicePlatform =
| 'ios'
| 'ipados'
| 'macos'
| 'watchos'
| 'android'
| 'web'
| 'unknown';
export type TPassportDeviceStatus = 'active' | 'revoked';
export type TPassportPushProvider = 'apns';
export type TPassportPushEnvironment = 'development' | 'production';
export interface IPassportCapabilities {
gps: boolean;
nfc: boolean;
push: boolean;
}
export interface IPassportDevice {
id: string;
data: {
userId: string;
label: string;
platform: TPassportDevicePlatform;
status: TPassportDeviceStatus;
publicKeyAlgorithm: 'p256';
publicKeyX963Base64: string;
capabilities: IPassportCapabilities;
pushRegistration?: {
provider: TPassportPushProvider;
token: string;
topic: string;
environment: TPassportPushEnvironment;
registeredAt: number;
lastDeliveredAt?: number;
lastError?: string;
};
appVersion?: string;
createdAt: number;
lastSeenAt?: number;
lastChallengeAt?: number;
};
}
+9
View File
@@ -0,0 +1,9 @@
export interface IPassportNonce {
id: string;
data: {
deviceId: string;
nonceHash: string;
createdAt: number;
expiresAt: number;
};
}
+97
View File
@@ -0,0 +1,97 @@
import * as plugins from '../plugins.js';
import * as data from '../data/index.js';
import type { IPassportDeviceSignedRequest } from './passport.js';
export interface IReq_ListPassportAlerts
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_ListPassportAlerts
> {
method: 'listPassportAlerts';
request: IPassportDeviceSignedRequest;
response: {
alerts: data.IAlert[];
};
}
export interface IReq_GetPassportAlertByHint
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_GetPassportAlertByHint
> {
method: 'getPassportAlertByHint';
request: IPassportDeviceSignedRequest & {
hintId: string;
};
response: {
alert?: data.IAlert;
};
}
export interface IReq_MarkPassportAlertSeen
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_MarkPassportAlertSeen
> {
method: 'markPassportAlertSeen';
request: IPassportDeviceSignedRequest & {
hintId: string;
};
response: {
success: boolean;
};
}
export interface IReq_UpsertAlertRule
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_UpsertAlertRule
> {
method: 'upsertAlertRule';
request: {
jwt: string;
ruleId?: string;
scope: data.TAlertRuleScope;
organizationId?: string;
eventType: string;
minimumSeverity: data.TAlertSeverity;
recipientMode: data.TAlertRuleRecipientMode;
recipientUserIds?: string[];
push: boolean;
enabled: boolean;
};
response: {
rule: data.IAlertRule;
};
}
export interface IReq_GetAlertRules
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_GetAlertRules
> {
method: 'getAlertRules';
request: {
jwt: string;
scope?: data.TAlertRuleScope;
organizationId?: string;
};
response: {
rules: data.IAlertRule[];
};
}
export interface IReq_DeleteAlertRule
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_DeleteAlertRule
> {
method: 'deleteAlertRule';
request: {
jwt: string;
ruleId: string;
};
response: {
success: boolean;
};
}
+2
View File
@@ -1,11 +1,13 @@
export * from './admin.js';
export * from './apitoken.js';
export * from './alert.js';
export * from './app.js';
export * from './authorization.js';
export * from './billingplan.js';
export * from './jwt.js';
export * from './login.js';
export * from './organization.js';
export * from './passport.js';
export * from './plan.js';
export * from './registration.js';
export * from './user.js';
+183
View File
@@ -0,0 +1,183 @@
import * as plugins from '../plugins.js';
import * as data from '../data/index.js';
export interface IPassportDeviceSignedRequest {
deviceId: string;
timestamp: number;
nonce: string;
signatureBase64: string;
signatureFormat?: data.TPassportSignatureFormat;
}
export interface IReq_CreatePassportEnrollmentChallenge
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_CreatePassportEnrollmentChallenge
> {
method: 'createPassportEnrollmentChallenge';
request: {
jwt: string;
deviceLabel: string;
platform: data.TPassportDevicePlatform;
appVersion?: string;
capabilities?: Partial<data.IPassportCapabilities>;
};
response: {
challengeId: string;
pairingToken: string;
pairingPayload: string;
signingPayload: string;
expiresAt: number;
};
}
export interface IReq_CompletePassportEnrollment
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_CompletePassportEnrollment
> {
method: 'completePassportEnrollment';
request: {
pairingToken: string;
deviceLabel: string;
platform: data.TPassportDevicePlatform;
publicKeyX963Base64: string;
signatureBase64: string;
signatureFormat?: data.TPassportSignatureFormat;
appVersion?: string;
capabilities?: Partial<data.IPassportCapabilities>;
};
response: {
device: data.IPassportDevice;
};
}
export interface IReq_GetPassportDevices
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_GetPassportDevices
> {
method: 'getPassportDevices';
request: {
jwt: string;
};
response: {
devices: data.IPassportDevice[];
};
}
export interface IReq_RevokePassportDevice
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_RevokePassportDevice
> {
method: 'revokePassportDevice';
request: {
jwt: string;
deviceId: string;
};
response: {
success: boolean;
};
}
export interface IReq_CreatePassportChallenge
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_CreatePassportChallenge
> {
method: 'createPassportChallenge';
request: {
jwt: string;
type?: Exclude<data.TPassportChallengeType, 'device_enrollment'>;
preferredDeviceId?: string;
audience?: string;
notificationTitle?: string;
requireLocation?: boolean;
requireNfc?: boolean;
};
response: {
challengeId: string;
challenge: string;
signingPayload: string;
deviceId: string;
expiresAt: number;
};
}
export interface IReq_ApprovePassportChallenge
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_ApprovePassportChallenge
> {
method: 'approvePassportChallenge';
request: {
challengeId: string;
deviceId: string;
signatureBase64: string;
signatureFormat?: data.TPassportSignatureFormat;
location?: data.IPassportLocationEvidence;
nfc?: data.IPassportNfcEvidence;
};
response: {
success: boolean;
challenge: data.IPassportChallenge;
};
}
export interface IReq_RegisterPassportPushToken
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_RegisterPassportPushToken
> {
method: 'registerPassportPushToken';
request: IPassportDeviceSignedRequest & {
provider: data.TPassportPushProvider;
token: string;
topic: string;
environment: data.TPassportPushEnvironment;
};
response: {
success: boolean;
};
}
export interface IReq_ListPendingPassportChallenges
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_ListPendingPassportChallenges
> {
method: 'listPendingPassportChallenges';
request: IPassportDeviceSignedRequest;
response: {
challenges: data.IPassportChallenge[];
};
}
export interface IReq_GetPassportChallengeByHint
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_GetPassportChallengeByHint
> {
method: 'getPassportChallengeByHint';
request: IPassportDeviceSignedRequest & {
hintId: string;
};
response: {
challenge?: data.IPassportChallenge;
};
}
export interface IReq_MarkPassportChallengeSeen
extends plugins.typedRequestInterfaces.implementsTR<
plugins.typedRequestInterfaces.ITypedRequest,
IReq_MarkPassportChallengeSeen
> {
method: 'markPassportChallengeSeen';
request: IPassportDeviceSignedRequest & {
hintId: string;
};
response: {
success: boolean;
};
}