saml-test-server 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +134 -2
  3. package/assets/idp-private-key.pem +27 -0
  4. package/assets/idp-public-cert.pem +20 -0
  5. package/dist/cli.js +106 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.js +2170 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/types/app.d.ts +16 -0
  10. package/dist/types/cli-program.d.ts +10 -0
  11. package/dist/types/cli.d.ts +2 -0
  12. package/dist/types/default-config.d.ts +12 -0
  13. package/dist/types/index.d.ts +41 -0
  14. package/dist/types/log.d.ts +26 -0
  15. package/dist/types/options.d.ts +111 -0
  16. package/dist/types/profile-mapper.d.ts +10 -0
  17. package/dist/types/session.d.ts +36 -0
  18. package/dist/types/state.d.ts +49 -0
  19. package/dist/types/vendor/saml/encrypt.d.ts +16 -0
  20. package/dist/types/vendor/saml/index.d.ts +7 -0
  21. package/dist/types/vendor/saml/saml20.d.ts +47 -0
  22. package/dist/types/vendor/saml/sign.d.ts +30 -0
  23. package/dist/types/vendor/saml/utils.d.ts +18 -0
  24. package/dist/types/vendor/samlp/auth.d.ts +87 -0
  25. package/dist/types/vendor/samlp/constants.d.ts +30 -0
  26. package/dist/types/vendor/samlp/encoders.d.ts +5 -0
  27. package/dist/types/vendor/samlp/index.d.ts +14 -0
  28. package/dist/types/vendor/samlp/logout.d.ts +66 -0
  29. package/dist/types/vendor/samlp/metadata.d.ts +22 -0
  30. package/dist/types/vendor/samlp/profile-mapper.d.ts +38 -0
  31. package/dist/types/vendor/samlp/session-participants.d.ts +22 -0
  32. package/dist/types/vendor/samlp/signers.d.ts +43 -0
  33. package/dist/types/vendor/samlp/templates.d.ts +52 -0
  34. package/dist/types/vendor/samlp/trim-xml.d.ts +2 -0
  35. package/dist/types/vendor/samlp/utils.d.ts +55 -0
  36. package/dist/types/vendor/samlp/xml.d.ts +7 -0
  37. package/dist/types/views/AutoPostForm.d.ts +12 -0
  38. package/dist/types/views/ErrorPage.d.ts +8 -0
  39. package/dist/types/views/Layout.d.ts +4 -0
  40. package/dist/types/views/SettingsPage.d.ts +5 -0
  41. package/dist/types/views/UserPage.d.ts +20 -0
  42. package/dist/types/views/index.d.ts +5 -0
  43. package/package.json +58 -4
@@ -0,0 +1,26 @@
1
+ import type { ResolvedOptions } from './options.js';
2
+ export declare const UNDEFINED_VALUE = "None";
3
+ export declare const LOG_LEVELS: readonly ["silent", "error", "info"];
4
+ /**
5
+ * - `silent`: no output at all
6
+ * - `error`: only unexpected errors (HTTP 5xx), on stderr
7
+ * - `info`: banners, requests and SAML messages (default)
8
+ */
9
+ export type LogLevel = (typeof LOG_LEVELS)[number];
10
+ export declare function isLogLevel(value: unknown): value is LogLevel;
11
+ export interface Logger {
12
+ /** Informational output (shown at level `info`). */
13
+ log(message?: string, ...rest: unknown[]): void;
14
+ /** Errors (shown at levels `error` and `info`). */
15
+ error(message?: string, ...rest: unknown[]): void;
16
+ }
17
+ export declare function createLogger(level?: LogLevel): Logger;
18
+ /** Pretty prints XML with syntax coloring (used to log SAML responses). */
19
+ export declare function prettyPrintXml(xml: string, indent?: number): string;
20
+ /** Short, single line representation of an option value for logs. */
21
+ export declare function formatOptionValue(key: string, value: unknown): string;
22
+ export declare function describeRecord(record: Record<string, unknown>, indent: number, format?: typeof formatOptionValue): string;
23
+ /** Startup banner describing the effective configuration. */
24
+ export declare function configBanner(options: ResolvedOptions): string;
25
+ /** Banner printed once the server listens. */
26
+ export declare function listeningBanner(baseUrl: string, sloEnabled: boolean): string;
@@ -0,0 +1,111 @@
1
+ import { type IdpConfig } from './default-config.js';
2
+ import { type LogLevel } from './log.js';
3
+ /** Root of the package (works from `src/` and from the built `dist/`). */
4
+ export declare const PACKAGE_ROOT: string;
5
+ /** Special `configFile` value that selects the built-in default user + metadata. */
6
+ export declare const DEFAULT_CONFIG_FILE = "saml-idp/config.js";
7
+ export declare const KEY_CERT_HELP_TEXT = "To generate a key/cert pair for the IdP, run the following command:\n\n openssl req -x509 -new -newkey rsa:2048 -nodes \\\n -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' \\\n -keyout idp-private-key.pem \\\n -out idp-public-cert.pem -days 7300";
8
+ declare const CRYPT_TYPES: {
9
+ readonly certificate: RegExp;
10
+ readonly 'private key': RegExp;
11
+ readonly 'public key': RegExp;
12
+ };
13
+ type CryptType = keyof typeof CRYPT_TYPES;
14
+ /** Thrown when the options are invalid. The message is meant to be shown to the user. */
15
+ export declare class OptionsError extends Error {
16
+ constructor(message: string);
17
+ }
18
+ /** A PEM string / Buffer, or the path of a PEM file. */
19
+ export type PemOrPath = string | Buffer;
20
+ /** Options of the IdP. They match the CLI flags of the original `saml-idp`. */
21
+ export interface IdpOptions {
22
+ /** IdP web server listener host. Default: `localhost`. */
23
+ host?: string;
24
+ /** IdP web server listener port (`0` picks a free port). Default: `7000`. */
25
+ port?: number;
26
+ /** IdP signature public key certificate (PEM or path). Defaults to the bundled test certificate. */
27
+ cert?: PemOrPath;
28
+ /** IdP signature private key (PEM or path). Defaults to the bundled test key. */
29
+ key?: PemOrPath;
30
+ /** IdP issuer URI. Default: `urn:example:idp`. */
31
+ issuer?: string;
32
+ /** SP Assertion Consumer Service URL. */
33
+ acsUrl: string;
34
+ /** SP single logout URL. Enables SLO when set. */
35
+ sloUrl?: string;
36
+ /** SP audience URI. */
37
+ audience: string;
38
+ /** SP issuer / entity URI. Defaults to the audience. */
39
+ serviceProviderId?: string;
40
+ /** Default SAML RelayState. */
41
+ relayState?: string;
42
+ /** Ignore the ACS URL of the AuthnRequest. */
43
+ disableRequestAcsUrl?: boolean;
44
+ /** Encrypt the assertion with the SP public key. */
45
+ encryptAssertion?: boolean;
46
+ /** SP certificate (PEM or path) used for assertion encryption. */
47
+ encryptionCert?: PemOrPath;
48
+ /** SP RSA public key (PEM or path). Derived from `encryptionCert` when omitted. */
49
+ encryptionPublicKey?: PemOrPath;
50
+ /** Web server TLS private key (PEM or path). */
51
+ httpsPrivateKey?: PemOrPath;
52
+ /** Web server TLS certificate (PEM or path). */
53
+ httpsCert?: PemOrPath;
54
+ /** Enable the HTTPS listener (requires `httpsPrivateKey` and `httpsCert`). */
55
+ https?: boolean;
56
+ /** Sign the SAML Response (the assertion is always signed). Default: `true`. */
57
+ signResponse?: boolean;
58
+ /**
59
+ * Path to a module (`.js`, `.mjs`, `.json`) that exports `{ user, metadata }`.
60
+ * Ignored when `config` is given.
61
+ */
62
+ configFile?: string;
63
+ /** Inline user profile and attribute metadata (overrides `configFile`). */
64
+ config?: Partial<IdpConfig>;
65
+ /** Create a new session for every authn request. Default: `false`. */
66
+ rollSession?: boolean;
67
+ /** Authentication Context Class Reference. */
68
+ authnContextClassRef?: string;
69
+ /** Authentication Context Declaration (XML or path to an XML file). */
70
+ authnContextDecl?: string;
71
+ /**
72
+ * Verbosity of the logs: `silent` (nothing), `error` (unexpected errors only) or `info`.
73
+ * Defaults to the `SAML_IDP_LOG_LEVEL` environment variable, then to `info`.
74
+ */
75
+ logLevel?: LogLevel;
76
+ /** @deprecated Use `logLevel: 'silent'`. */
77
+ silent?: boolean;
78
+ }
79
+ export interface ResolvedOptions {
80
+ host: string;
81
+ port: number;
82
+ cert: string;
83
+ key: string;
84
+ issuer: string;
85
+ acsUrl: string;
86
+ sloUrl?: string;
87
+ audience: string;
88
+ serviceProviderId: string;
89
+ relayState?: string;
90
+ disableRequestAcsUrl: boolean;
91
+ encryptAssertion: boolean;
92
+ encryptionCert?: string;
93
+ encryptionPublicKey?: string;
94
+ httpsPrivateKey?: string;
95
+ httpsCert?: string;
96
+ https: boolean;
97
+ signResponse: boolean;
98
+ rollSession: boolean;
99
+ authnContextClassRef?: string;
100
+ authnContextDecl?: string;
101
+ logLevel: LogLevel;
102
+ config: IdpConfig;
103
+ }
104
+ export declare function matchesCertType(value: string, type: CryptType): boolean;
105
+ /** Resolves a file path option, the same way the original `saml-idp` did. */
106
+ export declare function resolveFilePath(filePath: string): string | undefined;
107
+ /** Accepts PEM content or a file path and returns the PEM content. */
108
+ export declare function coerceCertFile(value: PemOrPath, type: CryptType, description: string, helpText?: string): string;
109
+ /** Validates the options, applies defaults and reads certificates / config files. */
110
+ export declare function resolveOptions(options: IdpOptions): Promise<ResolvedOptions>;
111
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { ClaimMetadata, ProfileMapper } from './vendor/samlp/index.js';
2
+ type User = Record<string, string | undefined>;
3
+ /**
4
+ * Maps the flat user profile to SAML attributes using the attribute metadata:
5
+ * each metadata entry is an attribute; multi-value attributes are comma separated.
6
+ *
7
+ * `getMetadata` is called on every use so edits to the metadata at runtime are honored.
8
+ */
9
+ export declare function createSimpleProfileMapper(getMetadata: () => ClaimMetadata[]): ProfileMapper<User>;
10
+ export {};
@@ -0,0 +1,36 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ import type { LogoutStore } from './vendor/samlp/index.js';
3
+ export declare const SESSION_COOKIE = "idp_sid";
4
+ /** Server-side session of the browser (identified by a signed cookie). */
5
+ export declare class Session {
6
+ private readonly store;
7
+ id: string;
8
+ /** Arbitrary per-session data. */
9
+ data: Record<string, any>;
10
+ destroyed: boolean;
11
+ constructor(id: string, store: SessionStore);
12
+ /** Destroys the session. The cookie is cleared at the end of the request. */
13
+ destroy(): void;
14
+ /** Replaces the session id (and drops the data), like express-session's `regenerate()`. */
15
+ regenerate(): void;
16
+ }
17
+ /** In-memory session store with a sliding 1 hour TTL. */
18
+ export declare class SessionStore {
19
+ private readonly entries;
20
+ create(): Session;
21
+ get(id: string): Session | undefined;
22
+ set(session: Session): void;
23
+ delete(id: string): void;
24
+ clear(): void;
25
+ }
26
+ export interface SessionVariables {
27
+ session: Session;
28
+ }
29
+ /** Hono middleware that attaches a `session` to the context (cookie: `idp_sid`). */
30
+ export declare function sessionMiddleware(store: SessionStore, secret: string): MiddlewareHandler<{
31
+ Variables: SessionVariables;
32
+ }>;
33
+ /** Same session index derivation as the original saml-idp. */
34
+ export declare function getSessionIndex(session: Session): string;
35
+ /** Keeps the state of in-flight Single Logout transactions in the session. */
36
+ export declare function createLogoutStore(session: Session): LogoutStore;
@@ -0,0 +1,49 @@
1
+ import type { UserProfile } from './default-config.js';
2
+ import type { ResolvedOptions } from './options.js';
3
+ import type { ClaimMetadata } from './vendor/samlp/index.js';
4
+ import type { DigestAlgorithm, SignatureAlgorithm } from './vendor/saml/sign.js';
5
+ export declare const IDP_PATHS: {
6
+ readonly SSO: "/saml/sso";
7
+ readonly SLO: "/saml/slo";
8
+ readonly METADATA: "/metadata";
9
+ readonly SIGN_IN: "/signin";
10
+ readonly SIGN_OUT: "/signout";
11
+ readonly SETTINGS: "/settings";
12
+ };
13
+ /** IdP settings. They can be changed at runtime from the settings page (or by tests). */
14
+ export interface IdpSettings {
15
+ issuer: string;
16
+ serviceProviderId: string;
17
+ cert: string;
18
+ key: string;
19
+ audience: string;
20
+ recipient: string;
21
+ destination: string;
22
+ acsUrl: string;
23
+ sloUrl?: string;
24
+ RelayState?: string;
25
+ allowRequestAcsUrl: boolean;
26
+ digestAlgorithm: DigestAlgorithm;
27
+ signatureAlgorithm: SignatureAlgorithm;
28
+ signResponse: boolean;
29
+ encryptAssertion: boolean;
30
+ encryptionCert?: string;
31
+ encryptionPublicKey?: string;
32
+ encryptionAlgorithm: string;
33
+ keyEncryptionAlgorithm: string;
34
+ lifetimeInSeconds: number;
35
+ authnContextClassRef?: string;
36
+ authnContextDecl?: string;
37
+ includeAttributeNameFormat: boolean;
38
+ }
39
+ /** Mutable state shared by all the requests served by an IdP instance. */
40
+ export interface IdpState {
41
+ settings: IdpSettings;
42
+ /** The user that is "signed in". Updated by the sign-in form. */
43
+ user: UserProfile;
44
+ /** SAML attributes (claims) the IdP can issue. */
45
+ metadata: ClaimMetadata[];
46
+ /** Restores settings, user and metadata to the values the IdP was started with. */
47
+ reset(): void;
48
+ }
49
+ export declare function createState(options: ResolvedOptions): IdpState;
@@ -0,0 +1,16 @@
1
+ import { type PemInput } from './utils.js';
2
+ export interface EncryptXmlOptions {
3
+ /** Service Provider certificate (PEM). */
4
+ encryptionCert: PemInput;
5
+ /**
6
+ * Service Provider RSA public key (PEM). Derived from `encryptionCert`
7
+ * when omitted.
8
+ */
9
+ encryptionPublicKey?: PemInput;
10
+ encryptionAlgorithm?: string;
11
+ keyEncryptionAlgorithm?: string;
12
+ }
13
+ export declare const DEFAULT_ENCRYPTION_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";
14
+ export declare const DEFAULT_KEY_ENCRYPTION_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p";
15
+ /** Encrypts an XML fragment for the given Service Provider (XML Encryption). */
16
+ export declare function encryptXml(xml: string, options: EncryptXmlOptions): Promise<string>;
@@ -0,0 +1,7 @@
1
+ export { createAssertion, buildAssertionDocument, SAML_ASSERTION_NS } from './saml20.js';
2
+ export type { Saml20Options, Attributes, AttributeValue } from './saml20.js';
3
+ export { signXml, SIGNATURE_ALGORITHMS, DIGEST_ALGORITHMS } from './sign.js';
4
+ export type { SignatureAlgorithm, DigestAlgorithm } from './sign.js';
5
+ export { encryptXml } from './encrypt.js';
6
+ export { escapeXml, pemToCert, removeWhitespace, fixPemFormatting } from './utils.js';
7
+ export type { PemInput } from './utils.js';
@@ -0,0 +1,47 @@
1
+ import { DOMParser } from '@xmldom/xmldom';
2
+ import { type DigestAlgorithm, type SignatureAlgorithm } from './sign.js';
3
+ import { type PemInput } from './utils.js';
4
+ /** DOM document type produced by `@xmldom/xmldom`. */
5
+ export type XmlDocument = ReturnType<DOMParser['parseFromString']>;
6
+ export declare const SAML_ASSERTION_NS = "urn:oasis:names:tc:SAML:2.0:assertion";
7
+ export type AttributeValue = string | number | boolean | undefined;
8
+ export type Attributes = Record<string, AttributeValue | AttributeValue[]>;
9
+ export interface Saml20Options {
10
+ uid?: string;
11
+ issuer?: string;
12
+ lifetimeInSeconds?: number;
13
+ audiences?: string | string[];
14
+ recipient?: string;
15
+ inResponseTo?: string;
16
+ attributes?: Attributes;
17
+ includeAttributeNameFormat?: boolean;
18
+ typedAttributes?: boolean;
19
+ sessionIndex?: string;
20
+ nameIdentifier?: string;
21
+ nameIdentifierFormat?: string;
22
+ nameIdentifierNameQualifier?: string;
23
+ nameIdentifierSPNameQualifier?: string;
24
+ nameIdentifierSPProvidedID?: string;
25
+ authnContextClassRef?: string;
26
+ /** Lets the caller mutate the assertion DOM before it is signed. */
27
+ transformAssertion?: (doc: XmlDocument) => void;
28
+ signAssertion?: boolean;
29
+ key?: PemInput;
30
+ cert?: PemInput;
31
+ signatureAlgorithm?: SignatureAlgorithm;
32
+ digestAlgorithm?: DigestAlgorithm;
33
+ signatureNamespacePrefix?: string;
34
+ encryptionCert?: PemInput;
35
+ encryptionPublicKey?: PemInput;
36
+ encryptionAlgorithm?: string;
37
+ keyEncryptionAlgorithm?: string;
38
+ }
39
+ /** Builds the assertion DOM (unsigned, unencrypted). */
40
+ export declare function buildAssertionDocument(options: Saml20Options): XmlDocument;
41
+ /**
42
+ * Creates a SAML 2.0 assertion.
43
+ *
44
+ * The assertion is signed unless `signAssertion === false` and is wrapped in a
45
+ * `<saml:EncryptedAssertion>` when `encryptionCert` is provided.
46
+ */
47
+ export declare function createAssertion(options: Saml20Options): Promise<string>;
@@ -0,0 +1,30 @@
1
+ import { type PemInput } from './utils.js';
2
+ export declare const SIGNATURE_ALGORITHMS: {
3
+ readonly 'rsa-sha256': "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
4
+ readonly 'rsa-sha1': "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
5
+ };
6
+ export declare const DIGEST_ALGORITHMS: {
7
+ readonly sha256: "http://www.w3.org/2001/04/xmlenc#sha256";
8
+ readonly sha1: "http://www.w3.org/2000/09/xmldsig#sha1";
9
+ };
10
+ export type SignatureAlgorithm = keyof typeof SIGNATURE_ALGORITHMS;
11
+ export type DigestAlgorithm = keyof typeof DIGEST_ALGORITHMS;
12
+ export declare const ENVELOPED_SIGNATURE = "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
13
+ export declare const EXC_C14N = "http://www.w3.org/2001/10/xml-exc-c14n#";
14
+ export interface SignXmlOptions {
15
+ key: PemInput;
16
+ cert: PemInput;
17
+ signatureAlgorithm?: SignatureAlgorithm;
18
+ digestAlgorithm?: DigestAlgorithm;
19
+ /** Namespace prefix of the generated `<Signature>` element (default: none). */
20
+ signatureNamespacePrefix?: string;
21
+ /** XPath of the element that the signature is placed after. */
22
+ xpathToNodeBeforeSignature: string;
23
+ /** XPath of the element to sign. */
24
+ xpathToSign: string;
25
+ signatureIdAttribute?: string;
26
+ }
27
+ /** Signs an XML string with an enveloped, exclusive-c14n XML signature. */
28
+ export declare function signXml(xml: string, options: SignXmlOptions): string;
29
+ /** Serializes without signing (strategy used when `signAssertion` is false). */
30
+ export declare function unsigned(xml: string): string;
@@ -0,0 +1,18 @@
1
+ export type PemInput = string | Buffer;
2
+ /** Strips the PEM armor from a certificate and returns the base64 body on a single line. */
3
+ export declare function pemToCert(pem: PemInput): string | null;
4
+ /** Returns a random alphanumeric identifier of the given length. */
5
+ export declare function uid(len: number): string;
6
+ export declare function removeWhitespace(xml: string): string;
7
+ /**
8
+ * Standardizes PEM content to match the spec (best effort).
9
+ * The original value is returned unmodified if the content is not PEM.
10
+ */
11
+ export declare function fixPemFormatting(pem: PemInput): PemInput;
12
+ /**
13
+ * Formats a date as an `xs:dateTime` in UTC with millisecond precision
14
+ * (`YYYY-MM-DDTHH:mm:ss.SSSZ`).
15
+ */
16
+ export declare function formatInstant(date: Date): string;
17
+ /** Escapes a string for use in XML text or double-quoted attribute values. */
18
+ export declare function escapeXml(value: unknown): string;
@@ -0,0 +1,87 @@
1
+ import { type Saml20Options } from '../saml/saml20.js';
2
+ import type { DigestAlgorithm, SignatureAlgorithm } from '../saml/sign.js';
3
+ import { removeHeaders } from './encoders.js';
4
+ import { type ProfileMapper } from './profile-mapper.js';
5
+ import { type ParseRequestOptions, type SamlHttpInput } from './utils.js';
6
+ import { type XmlDocument } from './xml.js';
7
+ /** Error that carries the HTTP status the caller should answer with. */
8
+ export declare class SamlpError extends Error {
9
+ status: number;
10
+ context?: unknown;
11
+ constructor(message: string, status?: number, context?: unknown);
12
+ }
13
+ export interface ResponseOptions extends Omit<Saml20Options, 'attributes' | 'audiences' | 'nameIdentifier'> {
14
+ issuer: string;
15
+ cert: string | Buffer;
16
+ key: string | Buffer;
17
+ audience?: string;
18
+ destination?: string;
19
+ signResponse?: boolean;
20
+ signAssertion?: boolean;
21
+ signatureAlgorithm?: SignatureAlgorithm;
22
+ digestAlgorithm?: DigestAlgorithm;
23
+ signatureNamespacePrefix?: string;
24
+ profileMapper?: ProfileMapper;
25
+ nameIdentifierProbes?: string[];
26
+ samlStatusCode?: string;
27
+ samlStatusMessage?: string;
28
+ }
29
+ export interface AuthOptions extends ResponseOptions, ParseRequestOptions {
30
+ RelayState?: string;
31
+ /** Resolves where the SAMLResponse is posted to. */
32
+ getPostURL: (audience: string | undefined, authnRequest: XmlDocument | undefined) => string | undefined | Promise<string | undefined>;
33
+ }
34
+ export interface PostForm {
35
+ /** Form action (Assertion Consumer Service URL). */
36
+ url: string;
37
+ /** `SAMLResponse` or `SAMLRequest`. */
38
+ field: 'SAMLResponse' | 'SAMLRequest';
39
+ /** Base64 value of the SAML message. */
40
+ value: string;
41
+ relayState: string;
42
+ }
43
+ export interface AuthResult extends PostForm {
44
+ /** The XML of the (signed) SAML Response. */
45
+ xml: string;
46
+ /** Options that were effective for this response. */
47
+ options: AuthOptions & {
48
+ postUrl: string;
49
+ audience?: string;
50
+ };
51
+ }
52
+ export declare function buildSamlResponse(options: ResponseOptions & {
53
+ samlAssertion?: string;
54
+ inResponseTo?: string;
55
+ }): string;
56
+ /** Maps `user` to claims, builds + signs (+ encrypts) the assertion and wraps it in a Response. */
57
+ export declare function createSamlResponse(samlConfig: ResponseOptions, user: unknown): Promise<string>;
58
+ /**
59
+ * Handles an SP-initiated or IdP-initiated SSO request for an authenticated user:
60
+ * parses the optional AuthnRequest, then mints a SAML Response for `user`.
61
+ */
62
+ export declare function authenticate(input: SamlHttpInput, options: AuthOptions, user: unknown): Promise<AuthResult>;
63
+ export interface AuthnRequestData {
64
+ id?: string;
65
+ issuer?: string;
66
+ subject?: string;
67
+ destination?: string;
68
+ assertionConsumerServiceURL?: string;
69
+ forceAuthn?: string;
70
+ requestedAuthnContext?: {
71
+ authnContextClassRef: string;
72
+ };
73
+ }
74
+ /** Parses an incoming `AuthnRequest`. Returns `undefined` when the request carries none. */
75
+ export declare function parseRequest(input: SamlHttpInput, options?: ParseRequestOptions): Promise<AuthnRequestData | undefined>;
76
+ export interface ErrorResponseOptions extends ResponseOptions {
77
+ RelayState?: string;
78
+ error?: {
79
+ code?: string;
80
+ description?: string;
81
+ };
82
+ postUrl: string;
83
+ inResponseTo?: string;
84
+ }
85
+ /** Builds a (signed) error Response to be posted back to the SP. */
86
+ export declare function createErrorResponse(options: ErrorResponseOptions): PostForm;
87
+ export { removeHeaders };
@@ -0,0 +1,30 @@
1
+ export declare const BINDINGS: {
2
+ readonly HTTP_POST: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";
3
+ readonly HTTP_REDIRECT: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";
4
+ };
5
+ export declare const STATUS: {
6
+ readonly SUCCESS: "urn:oasis:names:tc:SAML:2.0:status:Success";
7
+ readonly PARTIAL_LOGOUT: "urn:oasis:names:tc:SAML:2.0:status:PartialLogout";
8
+ readonly RESPONDER: "urn:oasis:names:tc:SAML:2.0:status:Responder";
9
+ };
10
+ export type ElementType = 'LOGOUT_REQUEST' | 'LOGOUT_RESPONSE' | 'RESPONSE' | 'AUTHN_REQUEST';
11
+ export interface ElementDescriptor {
12
+ PROP: 'SAMLRequest' | 'SAMLResponse';
13
+ SIGNATURE_VALIDATION_PATH?: string;
14
+ SIGNATURE_LOCATION_PATH?: string;
15
+ ISSUER_PATH?: string;
16
+ SESSION_INDEX_PATH?: string;
17
+ NAME_ID?: string;
18
+ AUTHN_CONTEXT_CLASS_REF_PATH?: string;
19
+ }
20
+ export declare const ELEMENTS: Record<ElementType, ElementDescriptor>;
21
+ export declare const ALGORITHMS: {
22
+ readonly SIGNATURE: {
23
+ readonly 'rsa-sha256': "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
24
+ readonly 'rsa-sha1': "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
25
+ };
26
+ readonly DIGEST: {
27
+ readonly sha256: "http://www.w3.org/2001/04/xmlenc#sha256";
28
+ readonly sha1: "http://www.w3.org/2000/09/xmldsig#sha1";
29
+ };
30
+ };
@@ -0,0 +1,5 @@
1
+ /** Returns the base64 body of the first PEM block (no armor, no line breaks). */
2
+ export declare function removeHeaders(cert: string | Buffer): string | null;
3
+ /** SHA-1 thumbprint (upper-case hex) of a base64 DER certificate. */
4
+ export declare function calculateThumbprint(base64Cert: string): string;
5
+ export declare function thumbprint(pem: string | Buffer): string;
@@ -0,0 +1,14 @@
1
+ export { authenticate, buildSamlResponse, createErrorResponse, createSamlResponse, parseRequest, SamlpError, } from './auth.js';
2
+ export type { AuthnRequestData, AuthOptions, AuthResult, ErrorResponseOptions, PostForm, ResponseOptions, } from './auth.js';
3
+ export { logout, createLogoutError } from './logout.js';
4
+ export type { LogoutErrorOptions, LogoutOptions, LogoutResult, LogoutState, LogoutStore } from './logout.js';
5
+ export { buildMetadata } from './metadata.js';
6
+ export type { MetadataOptions } from './metadata.js';
7
+ export { SessionParticipants } from './session-participants.js';
8
+ export type { SessionParticipant } from './session-participants.js';
9
+ export { passportProfileMapper } from './profile-mapper.js';
10
+ export type { ClaimMetadata, NameIdentifier, ProfileMapper } from './profile-mapper.js';
11
+ export { BINDINGS, STATUS } from './constants.js';
12
+ export { thumbprint, removeHeaders } from './encoders.js';
13
+ export type { SamlHttpInput, ParseRequestOptions } from './utils.js';
14
+ export type { Attributes, AttributeValue } from '../saml/saml20.js';
@@ -0,0 +1,66 @@
1
+ import type { DigestAlgorithm, SignatureAlgorithm } from '../saml/sign.js';
2
+ import { type PostForm } from './auth.js';
3
+ import { SessionParticipants } from './session-participants.js';
4
+ import { type SamlHttpInput } from './utils.js';
5
+ export type LogoutState = Record<string, any>;
6
+ /** Persists the state of an in-flight logout transaction (keyed by an opaque handle). */
7
+ export interface LogoutStore {
8
+ /** Stores the state and returns its handle (used as `RelayState`). */
9
+ save(state: LogoutState): string;
10
+ load(handle: string | undefined, options?: {
11
+ destroy?: boolean;
12
+ }): LogoutState | undefined;
13
+ update(handle: string, state: LogoutState): void;
14
+ }
15
+ export interface LogoutOptions {
16
+ issuer: string;
17
+ cert: string | Buffer;
18
+ key: string | Buffer;
19
+ digestAlgorithm?: DigestAlgorithm;
20
+ signatureAlgorithm?: SignatureAlgorithm;
21
+ sessionParticipants: SessionParticipants;
22
+ store: LogoutStore;
23
+ /** Called once all participants are logged out. */
24
+ clearIdPSession?: () => void | Promise<void>;
25
+ /** Fallback SP logout URL. */
26
+ destination?: string;
27
+ protocolBinding?: string;
28
+ /** Deflate + detached signature (HTTP-Redirect binding). */
29
+ deflate?: boolean;
30
+ samlStatusMessage?: string;
31
+ }
32
+ export type LogoutResult = ({
33
+ type: 'post';
34
+ } & PostForm) | {
35
+ type: 'redirect';
36
+ url: string;
37
+ }
38
+ /** IdP-initiated flow finished: reply with an empty 200. */
39
+ | {
40
+ type: 'done';
41
+ };
42
+ /**
43
+ * Handles the three legs of the SAML Single Logout profile:
44
+ * - SP-initiated: an incoming `SAMLRequest` (LogoutRequest)
45
+ * - in-flight: an incoming `SAMLResponse` (LogoutResponse) from a participant
46
+ * - IdP-initiated: neither, starts the logout of all session participants
47
+ */
48
+ export declare function logout(input: SamlHttpInput, options: LogoutOptions): Promise<LogoutResult>;
49
+ export interface LogoutErrorOptions {
50
+ issuer: string;
51
+ cert: string | Buffer;
52
+ key: string | Buffer;
53
+ destination?: string;
54
+ inResponseTo?: string;
55
+ relayState?: string;
56
+ error?: {
57
+ code?: string;
58
+ description?: string;
59
+ };
60
+ digestAlgorithm?: DigestAlgorithm;
61
+ signatureAlgorithm?: SignatureAlgorithm;
62
+ protocolBinding?: string;
63
+ deflate?: boolean;
64
+ }
65
+ /** Builds a signed `LogoutResponse` carrying an error status. */
66
+ export declare function createLogoutError(input: SamlHttpInput, options: LogoutErrorOptions): Promise<LogoutResult>;
@@ -0,0 +1,22 @@
1
+ import { type ProfileMapper } from './profile-mapper.js';
2
+ export interface MetadataOptions {
3
+ issuer: string;
4
+ cert: string | Buffer;
5
+ /** Defaults to the Passport profile mapper. */
6
+ profileMapper?: ProfileMapper;
7
+ /** Path of the HTTP-Redirect SingleSignOnService (appended to the origin). */
8
+ redirectEndpointPath?: string;
9
+ /** Path of the HTTP-POST SingleSignOnService (appended to the origin). */
10
+ postEndpointPath?: string;
11
+ /** Paths of the SingleLogoutService endpoints. */
12
+ logoutEndpointPaths?: {
13
+ redirect?: string;
14
+ post?: string;
15
+ };
16
+ }
17
+ /**
18
+ * Builds the IdP SAML metadata document.
19
+ *
20
+ * @param origin scheme + host the IdP is reachable at, e.g. `https://idp.example.com`
21
+ */
22
+ export declare function buildMetadata(options: MetadataOptions, origin: string): string;
@@ -0,0 +1,38 @@
1
+ import type { Attributes } from '../saml/saml20.js';
2
+ export interface ClaimMetadata {
3
+ id: string;
4
+ optional?: boolean;
5
+ displayName: string;
6
+ description?: string;
7
+ multiValue?: boolean;
8
+ options?: string[];
9
+ }
10
+ export interface NameIdentifier {
11
+ nameIdentifier?: string;
12
+ nameIdentifierFormat?: string;
13
+ nameIdentifierNameQualifier?: string;
14
+ nameIdentifierSPNameQualifier?: string;
15
+ nameIdentifierSPProvidedID?: string;
16
+ }
17
+ /** Maps an application user profile to SAML claims and a NameID. */
18
+ export interface ProfileMapper<TUser = any> {
19
+ /** Claims advertised in the IdP metadata. */
20
+ metadata: ClaimMetadata[];
21
+ getClaims(user: TUser): Attributes | undefined;
22
+ getNameIdentifier(user: TUser): NameIdentifier;
23
+ }
24
+ interface PassportUser {
25
+ id?: string;
26
+ emails?: Array<{
27
+ value?: string;
28
+ }>;
29
+ displayName?: string;
30
+ name?: {
31
+ givenName?: string;
32
+ familyName?: string;
33
+ };
34
+ [key: string]: unknown;
35
+ }
36
+ /** Default mapper: maps a Passport.js user profile to WS-Federation style claims. */
37
+ export declare const passportProfileMapper: ProfileMapper<PassportUser>;
38
+ export {};
@@ -0,0 +1,22 @@
1
+ export interface SessionParticipant {
2
+ serviceProviderId?: string;
3
+ sessionIndex?: string;
4
+ nameId?: string;
5
+ nameIdFormat?: string;
6
+ serviceProviderLogoutURL?: string;
7
+ /** Certificate used to validate signed messages from the participant. */
8
+ cert?: string | Buffer;
9
+ thumbprint?: string;
10
+ }
11
+ /** In-memory list of the Service Providers that take part in a user's session. */
12
+ export declare class SessionParticipants {
13
+ private participants;
14
+ constructor(sessions?: SessionParticipant[]);
15
+ /** Finds a participant by issuer (optional), session index and NameID. */
16
+ get(issuer: string | undefined, sessionIndices: string | string[] | undefined, nameId: string | undefined): SessionParticipant | undefined;
17
+ hasElements(): boolean;
18
+ /** Returns the first participant without removing it. */
19
+ getFirst(): SessionParticipant | undefined;
20
+ /** Removes and returns the matching participant (if any). */
21
+ remove(issuer: string | undefined, sessionIndex: string | undefined, nameId: string | undefined): SessionParticipant | null;
22
+ }