@sgnl-ai/secevent
Version:
A comprehensive JavaScript/TypeScript library for Security Event Tokens (SET) implementing RFC 8417, CAEP, and SSF standards
745 lines (730 loc) • 22.3 kB
TypeScript
import { JWTPayload, KeyLike } from 'jose';
/**
* Subject Identifier Types
* Implements various subject identifier formats as defined in RFC 8417 and related specs
*/
interface SubjectIdentifier {
format: string;
[key: string]: unknown;
}
/**
* Account Identifier
* Identifies a subject using an account at a service provider
*/
interface AccountIdentifier extends SubjectIdentifier {
format: 'account';
uri: string;
}
/**
* Email Identifier
* Identifies a subject using an email address
*/
interface EmailIdentifier extends SubjectIdentifier {
format: 'email';
email: string;
}
/**
* Issuer and Subject Identifier
* Identifies a subject using an issuer and subject pair
*/
interface IssuerSubjectIdentifier extends SubjectIdentifier {
format: 'iss_sub';
iss: string;
sub: string;
}
/**
* Opaque Identifier
* Identifies a subject using an opaque identifier
*/
interface OpaqueIdentifier extends SubjectIdentifier {
format: 'opaque';
id: string;
}
/**
* Phone Number Identifier
* Identifies a subject using a phone number
*/
interface PhoneNumberIdentifier extends SubjectIdentifier {
format: 'phone_number';
phone_number: string;
}
/**
* Decentralized Identifier (DID)
* Identifies a subject using a W3C Decentralized Identifier
*/
interface DidIdentifier extends SubjectIdentifier {
format: 'did';
url: string;
}
/**
* URI Identifier
* Identifies a subject using a URI
*/
interface UriIdentifier extends SubjectIdentifier {
format: 'uri';
uri: string;
}
/**
* Aliases Identifier
* Groups multiple identifiers for the same subject
*/
interface AliasesIdentifier extends SubjectIdentifier {
format: 'aliases';
identifiers: SubjectIdentifier[];
}
/**
* Complex Subject with multiple identifier types
*/
interface ComplexSubject {
user?: SubjectIdentifier;
device?: SubjectIdentifier;
session?: SubjectIdentifier;
application?: SubjectIdentifier;
tenant?: SubjectIdentifier;
org?: SubjectIdentifier;
[key: string]: SubjectIdentifier | undefined;
}
/**
* Type guard functions
*/
declare function isAccountIdentifier(subject: SubjectIdentifier): subject is AccountIdentifier;
declare function isEmailIdentifier(subject: SubjectIdentifier): subject is EmailIdentifier;
declare function isIssuerSubjectIdentifier(subject: SubjectIdentifier): subject is IssuerSubjectIdentifier;
declare function isOpaqueIdentifier(subject: SubjectIdentifier): subject is OpaqueIdentifier;
declare function isPhoneNumberIdentifier(subject: SubjectIdentifier): subject is PhoneNumberIdentifier;
declare function isDidIdentifier(subject: SubjectIdentifier): subject is DidIdentifier;
declare function isUriIdentifier(subject: SubjectIdentifier): subject is UriIdentifier;
declare function isAliasesIdentifier(subject: SubjectIdentifier): subject is AliasesIdentifier;
/**
* Factory functions for creating subject identifiers
*/
declare class SubjectIdentifiers {
static account(uri: string): AccountIdentifier;
static email(email: string): EmailIdentifier;
static issuerSubject(iss: string, sub: string): IssuerSubjectIdentifier;
static opaque(id: string): OpaqueIdentifier;
static phoneNumber(phone_number: string): PhoneNumberIdentifier;
static did(url: string): DidIdentifier;
static uri(uri: string): UriIdentifier;
static aliases(...identifiers: SubjectIdentifier[]): AliasesIdentifier;
}
/**
* Security Event Types
* Implements CAEP and SSF event types according to their specifications
*/
/**
* Base Security Event interface
*/
interface SecurityEvent {
[eventType: string]: EventData | unknown;
}
interface EventData {
subject?: SubjectIdentifier | ComplexSubject;
[key: string]: unknown;
}
/**
* CAEP Event Types
*/
/**
* Session Revoked Event
* Indicates that the session identified by the subject has been revoked
*/
interface SessionRevokedEvent extends EventData {
event_timestamp: number;
reason?: string;
}
/**
* Token Claims Change Event
* Indicates that the claims in the token have changed
*/
interface TokenClaimsChangeEvent extends EventData {
event_timestamp: number;
claims?: Record<string, unknown>;
}
/**
* Credential Change Event
* Indicates that the user's credential has been changed
*/
interface CredentialChangeEvent extends EventData {
event_timestamp: number;
credential_type?: string;
change_type?: 'create' | 'update' | 'delete';
reason?: string;
x509_issuer?: string;
x509_serial?: string;
fido2_aaguid?: string;
friendly_name?: string;
}
/**
* Assurance Level Change Event
* Indicates that the user's assurance level has changed
*/
interface AssuranceLevelChangeEvent extends EventData {
event_timestamp: number;
current_level?: string;
previous_level?: string;
change_direction?: 'increase' | 'decrease';
initiating_entity?: 'policy' | 'user' | 'admin';
}
/**
* Device Compliance Change Event
* Indicates that the device's compliance status has changed
*/
interface DeviceComplianceChangeEvent extends EventData {
event_timestamp: number;
current_status?: 'compliant' | 'not_compliant';
previous_status?: 'compliant' | 'not_compliant';
compliance_policies?: string[];
}
/**
* SSF Event Types
*/
/**
* Stream Updated Event
* Indicates that the stream configuration has been updated
*/
interface StreamUpdatedEvent extends EventData {
effective_time?: number;
}
/**
* Verification Event
* Used to verify the stream connection
*/
interface VerificationEvent extends EventData {
state?: string;
}
/**
* CAEP Event URIs
*/
declare const CAEP_EVENT_TYPES: {
readonly SESSION_REVOKED: "https://schemas.openid.net/secevent/caep/event-type/session-revoked";
readonly TOKEN_CLAIMS_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/token-claims-change";
readonly CREDENTIAL_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/credential-change";
readonly ASSURANCE_LEVEL_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/assurance-level-change";
readonly DEVICE_COMPLIANCE_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/device-compliance-change";
};
/**
* SSF Event URIs
*/
declare const SSF_EVENT_TYPES: {
readonly STREAM_UPDATED: "https://schemas.openid.net/secevent/ssf/event-type/stream-updated";
readonly VERIFICATION: "https://schemas.openid.net/secevent/ssf/event-type/verification";
};
/**
* RISC Event URIs
*/
declare const RISC_EVENT_TYPES: {
readonly ACCOUNT_CREDENTIAL_CHANGE_REQUIRED: "https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required";
readonly ACCOUNT_PURGED: "https://schemas.openid.net/secevent/risc/event-type/account-purged";
readonly ACCOUNT_DISABLED: "https://schemas.openid.net/secevent/risc/event-type/account-disabled";
readonly ACCOUNT_ENABLED: "https://schemas.openid.net/secevent/risc/event-type/account-enabled";
readonly IDENTIFIER_CHANGED: "https://schemas.openid.net/secevent/risc/event-type/identifier-changed";
readonly IDENTIFIER_RECYCLED: "https://schemas.openid.net/secevent/risc/event-type/identifier-recycled";
readonly OPT_IN: "https://schemas.openid.net/secevent/risc/event-type/opt-in";
readonly OPT_OUT_INITIATED: "https://schemas.openid.net/secevent/risc/event-type/opt-out-initiated";
readonly OPT_OUT_CANCELLED: "https://schemas.openid.net/secevent/risc/event-type/opt-out-cancelled";
readonly OPT_OUT_EFFECTIVE: "https://schemas.openid.net/secevent/risc/event-type/opt-out-effective";
readonly RECOVERY_ACTIVATED: "https://schemas.openid.net/secevent/risc/event-type/recovery-activated";
readonly RECOVERY_INFORMATION_CHANGED: "https://schemas.openid.net/secevent/risc/event-type/recovery-information-changed";
readonly SESSIONS_REVOKED: "https://schemas.openid.net/secevent/risc/event-type/sessions-revoked";
};
/**
* All event type URIs
*/
declare const EVENT_TYPES: {
readonly ACCOUNT_CREDENTIAL_CHANGE_REQUIRED: "https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required";
readonly ACCOUNT_PURGED: "https://schemas.openid.net/secevent/risc/event-type/account-purged";
readonly ACCOUNT_DISABLED: "https://schemas.openid.net/secevent/risc/event-type/account-disabled";
readonly ACCOUNT_ENABLED: "https://schemas.openid.net/secevent/risc/event-type/account-enabled";
readonly IDENTIFIER_CHANGED: "https://schemas.openid.net/secevent/risc/event-type/identifier-changed";
readonly IDENTIFIER_RECYCLED: "https://schemas.openid.net/secevent/risc/event-type/identifier-recycled";
readonly OPT_IN: "https://schemas.openid.net/secevent/risc/event-type/opt-in";
readonly OPT_OUT_INITIATED: "https://schemas.openid.net/secevent/risc/event-type/opt-out-initiated";
readonly OPT_OUT_CANCELLED: "https://schemas.openid.net/secevent/risc/event-type/opt-out-cancelled";
readonly OPT_OUT_EFFECTIVE: "https://schemas.openid.net/secevent/risc/event-type/opt-out-effective";
readonly RECOVERY_ACTIVATED: "https://schemas.openid.net/secevent/risc/event-type/recovery-activated";
readonly RECOVERY_INFORMATION_CHANGED: "https://schemas.openid.net/secevent/risc/event-type/recovery-information-changed";
readonly SESSIONS_REVOKED: "https://schemas.openid.net/secevent/risc/event-type/sessions-revoked";
readonly STREAM_UPDATED: "https://schemas.openid.net/secevent/ssf/event-type/stream-updated";
readonly VERIFICATION: "https://schemas.openid.net/secevent/ssf/event-type/verification";
readonly SESSION_REVOKED: "https://schemas.openid.net/secevent/caep/event-type/session-revoked";
readonly TOKEN_CLAIMS_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/token-claims-change";
readonly CREDENTIAL_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/credential-change";
readonly ASSURANCE_LEVEL_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/assurance-level-change";
readonly DEVICE_COMPLIANCE_CHANGE: "https://schemas.openid.net/secevent/caep/event-type/device-compliance-change";
};
type EventTypeUri = (typeof EVENT_TYPES)[keyof typeof EVENT_TYPES];
/**
* Helper class for creating events
*/
declare class Events {
static sessionRevoked(subject: SubjectIdentifier | ComplexSubject, timestamp: number, reason?: string): SecurityEvent;
static tokenClaimsChange(subject: SubjectIdentifier | ComplexSubject, timestamp: number, claims?: Record<string, unknown>): SecurityEvent;
static credentialChange(subject: SubjectIdentifier | ComplexSubject, timestamp: number, options?: Partial<CredentialChangeEvent>): SecurityEvent;
static assuranceLevelChange(subject: SubjectIdentifier | ComplexSubject, timestamp: number, options?: Partial<AssuranceLevelChangeEvent>): SecurityEvent;
static deviceComplianceChange(subject: SubjectIdentifier | ComplexSubject, timestamp: number, options?: Partial<DeviceComplianceChangeEvent>): SecurityEvent;
static streamUpdated(effectiveTime?: number): SecurityEvent;
static verification(state?: string): SecurityEvent;
}
/**
* Security Event Token (SET) Types
* Core types for SEC Event tokens as defined in RFC 8417
*/
/**
* Security Event Token payload
* Extends JWT payload with SET-specific claims
*/
interface SecEventPayload extends JWTPayload {
/**
* Events claim containing one or more security events
*/
events: SecurityEvent;
/**
* Transaction identifier
*/
txn?: string;
/**
* Token identifier (jti is inherited from JWTPayload)
*/
jti: string;
/**
* Issued at timestamp (iat is inherited from JWTPayload)
*/
iat: number;
/**
* Issuer (iss is inherited from JWTPayload)
*/
iss: string;
/**
* Audience (aud is inherited from JWTPayload)
*/
aud?: string | string[];
/**
* Subject (sub is inherited from JWTPayload)
* Note: In SET, the subject is typically in the event, not the JWT subject
*/
sub?: string;
/**
* Additional custom claims
*/
[key: string]: unknown;
}
/**
* Options for creating a Security Event Token
*/
interface SecEventOptions {
/**
* Issuer of the token
*/
issuer: string;
/**
* Audience for the token
*/
audience?: string | string[];
/**
* Token ID (defaults to UUID)
*/
jti?: string;
/**
* Issued at timestamp (defaults to current time)
*/
iat?: number;
/**
* Transaction ID
*/
txn?: string;
/**
* Additional custom claims
*/
additionalClaims?: Record<string, unknown>;
}
/**
* Signed Security Event Token
*/
interface SignedSecEvent {
/**
* The JWT string representation
*/
jwt: string;
/**
* The decoded payload
*/
payload: SecEventPayload;
}
/**
* SEC Event validation options
*/
interface ValidationOptions {
/**
* Expected issuer
*/
issuer?: string | string[];
/**
* Expected audience
*/
audience?: string | string[];
/**
* Clock tolerance in seconds
*/
clockTolerance?: number;
/**
* Current time (defaults to Date.now())
*/
currentDate?: Date;
/**
* Required claims
*/
requiredClaims?: string[];
/**
* Maximum token age in seconds
*/
maxTokenAge?: number;
}
/**
* Validation result
*/
interface ValidationResult {
/**
* Whether validation succeeded
*/
valid: boolean;
/**
* Decoded payload if valid
*/
payload?: SecEventPayload;
/**
* Error message if invalid
*/
error?: string;
/**
* Detailed validation errors
*/
errors?: string[];
}
/**
* ID Generator interface
*/
interface IdGenerator {
/**
* Generate a unique ID
*/
generate(): string;
}
/**
* Signing key configuration
*/
interface SigningKey {
/**
* Key ID
*/
kid?: string;
/**
* Algorithm
*/
alg: string;
/**
* The actual key (can be string, Buffer, or KeyLike)
*/
key: unknown;
}
/**
* Security Event Token Builder
* Provides a fluent API for constructing SETs
*/
/**
* Builder configuration options
*/
interface BuilderOptions {
/**
* Default issuer for all tokens
*/
defaultIssuer?: string;
/**
* Default audience for all tokens
*/
defaultAudience?: string | string[];
/**
* ID generator for JTI values
*/
idGenerator?: IdGenerator;
/**
* Default signing key
*/
signingKey?: SigningKey;
}
/**
* Security Event Token Builder
*/
declare class SecEventBuilder {
private options;
private issuer?;
private audience?;
private jti?;
private iat?;
private txn?;
private events;
private additionalClaims;
private signingKey?;
constructor(options?: BuilderOptions);
/**
* Set the issuer
*/
withIssuer(issuer: string): this;
/**
* Set the audience
*/
withAudience(audience: string | string[]): this;
/**
* Set the token ID
*/
withJti(jti: string): this;
/**
* Set the issued at timestamp
*/
withIat(iat: number): this;
/**
* Set the transaction ID
*/
withTxn(txn: string): this;
/**
* Add an event to the token
*/
withEvent(event: SecurityEvent): this;
/**
* Add multiple events to the token
*/
withEvents(...events: SecurityEvent[]): this;
/**
* Add a custom claim
*/
withClaim(key: string, value: unknown): this;
/**
* Add multiple custom claims
*/
withClaims(claims: Record<string, unknown>): this;
/**
* Set the signing key
*/
withSigningKey(key: SigningKey): this;
/**
* Build the payload without signing
*/
buildPayload(): SecEventPayload;
/**
* Build and sign the token
*/
sign(signingKey?: SigningKey): Promise<SignedSecEvent>;
/**
* Reset the builder
*/
reset(): this;
/**
* Create a new builder with the same configuration
*/
clone(): SecEventBuilder;
}
/**
* Factory function for creating a builder
*/
declare function createBuilder(options?: BuilderOptions): SecEventBuilder;
/**
* Security Event Token Parser and Validator
* Parses and validates SETs according to RFC 8417
*/
/**
* Parser configuration
*/
interface ParserOptions {
/**
* JWKS URL for key verification
*/
jwksUrl?: string;
/**
* Static verification keys
*/
verificationKeys?: SigningKey[];
/**
* Default validation options
*/
defaultValidationOptions?: ValidationOptions;
}
/**
* Security Event Token Parser
*/
declare class SecEventParser {
private options;
private jwks?;
constructor(options?: ParserOptions);
/**
* Parse a JWT without verification
*/
decode(jwt: string): SecEventPayload;
/**
* Parse and verify a signed SET
*/
verify(jwt: string, verificationKey?: SigningKey | SigningKey[], options?: ValidationOptions): Promise<ValidationResult>;
/**
* Validate SET payload structure
*/
private validatePayloadStructure;
/**
* Validate SET-specific requirements
*/
private validateSecEvent;
/**
* Check if an event URI is valid
*/
private isValidEventUri;
/**
* Extract events from a decoded payload
*/
extractEvents(payload: SecEventPayload): SecurityEvent;
/**
* Extract a specific event type from a payload
*/
extractEvent<T = unknown>(payload: SecEventPayload, eventType: string): T | undefined;
/**
* Check if a payload contains a specific event type
*/
hasEvent(payload: SecEventPayload, eventType: string): boolean;
/**
* Get all event types in a payload
*/
getEventTypes(payload: SecEventPayload): string[];
}
/**
* Factory function for creating a parser
*/
declare function createParser(options?: ParserOptions): SecEventParser;
/**
* ID Generators for Security Event Tokens
*/
/**
* UUID v4 ID generator
*/
declare class UuidGenerator implements IdGenerator {
generate(): string;
}
/**
* Timestamp-based ID generator
*/
declare class TimestampGenerator implements IdGenerator {
private counter;
generate(): string;
}
/**
* Prefixed ID generator
*/
declare class PrefixedGenerator implements IdGenerator {
private prefix;
private baseGenerator;
constructor(prefix: string, baseGenerator?: IdGenerator);
generate(): string;
}
/**
* Custom function-based ID generator
*/
declare class CustomGenerator implements IdGenerator {
private generatorFn;
constructor(generatorFn: () => string);
generate(): string;
}
/**
* Default ID generator
*/
declare const defaultIdGenerator: UuidGenerator;
/**
* Security Event Token Signing utilities
* Provides various signing strategies and key management
*/
/**
* Supported algorithms
*/
declare enum Algorithm {
RS256 = "RS256",
RS384 = "RS384",
RS512 = "RS512",
PS256 = "PS256",
PS384 = "PS384",
PS512 = "PS512",
ES256 = "ES256",
ES384 = "ES384",
ES512 = "ES512",
HS256 = "HS256",
HS384 = "HS384",
HS512 = "HS512"
}
/**
* Key manager for handling signing keys
*/
declare class KeyManager {
private keys;
/**
* Add a key to the manager
*/
addKey(kid: string, key: SigningKey): void;
/**
* Get a key by ID
*/
getKey(kid: string): SigningKey | undefined;
/**
* Get all keys
*/
getAllKeys(): SigningKey[];
/**
* Remove a key
*/
removeKey(kid: string): boolean;
/**
* Clear all keys
*/
clear(): void;
/**
* Get the default key (first key added)
*/
getDefaultKey(): SigningKey | undefined;
}
/**
* Signing utilities
*/
declare class SigningUtils {
/**
* Generate a new key pair
*/
static generateKeyPair(alg?: Algorithm, options?: {
modulusLength?: number;
extractable?: boolean;
}): Promise<{
publicKey: KeyLike;
privateKey: KeyLike;
}>;
/**
* Import a JWK
*/
static importJWK(jwk: Record<string, unknown>, alg: Algorithm): Promise<KeyLike>;
/**
* Import a PKCS8 private key
*/
static importPrivateKey(pkcs8: string, alg: Algorithm, options?: {
extractable?: boolean;
}): Promise<KeyLike>;
/**
* Import a SPKI public key
*/
static importPublicKey(spki: string, alg: Algorithm, options?: {
extractable?: boolean;
}): Promise<KeyLike>;
/**
* Create a signing key from various formats
*/
static createSigningKey(key: string | KeyLike | Record<string, unknown>, alg: Algorithm, kid?: string): Promise<SigningKey>;
/**
* Create a symmetric signing key
*/
static createSymmetricKey(secret: string, alg?: Algorithm, kid?: string): SigningKey;
}
/**
* Default key manager instance
*/
declare const defaultKeyManager: KeyManager;
export { type AccountIdentifier, Algorithm, type AliasesIdentifier, type AssuranceLevelChangeEvent, type BuilderOptions, CAEP_EVENT_TYPES, type ComplexSubject, type CredentialChangeEvent, CustomGenerator, type DeviceComplianceChangeEvent, type DidIdentifier, EVENT_TYPES, type EmailIdentifier, type EventData, type EventTypeUri, Events, type IdGenerator, type IssuerSubjectIdentifier, KeyManager, type OpaqueIdentifier, type ParserOptions, type PhoneNumberIdentifier, PrefixedGenerator, RISC_EVENT_TYPES, SSF_EVENT_TYPES, SecEventBuilder, type SecEventOptions, SecEventParser, type SecEventPayload, type SecurityEvent, type SessionRevokedEvent, type SignedSecEvent, type SigningKey, SigningUtils, type StreamUpdatedEvent, type SubjectIdentifier, SubjectIdentifiers, TimestampGenerator, type TokenClaimsChangeEvent, type UriIdentifier, UuidGenerator, type ValidationOptions, type ValidationResult, type VerificationEvent, createBuilder, createParser, defaultIdGenerator, defaultKeyManager, isAccountIdentifier, isAliasesIdentifier, isDidIdentifier, isEmailIdentifier, isIssuerSubjectIdentifier, isOpaqueIdentifier, isPhoneNumberIdentifier, isUriIdentifier };