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 @@
1
+ {"version":3,"file":"index.js","sources":["../src/log.ts","../src/default-config.ts","../src/options.ts","../src/profile-mapper.ts","../src/session.ts","../src/state.ts","../src/vendor/saml/utils.ts","../src/vendor/saml/encrypt.ts","../src/vendor/saml/sign.ts","../src/vendor/saml/saml20.ts","../src/vendor/samlp/constants.ts","../src/vendor/samlp/encoders.ts","../src/vendor/samlp/profile-mapper.ts","../src/vendor/samlp/xml.ts","../src/vendor/samlp/signers.ts","../src/vendor/samlp/templates.ts","../src/vendor/samlp/utils.ts","../src/vendor/samlp/auth.ts","../src/vendor/samlp/session-participants.ts","../src/vendor/samlp/trim-xml.ts","../src/vendor/samlp/logout.ts","../src/vendor/samlp/metadata.ts","../src/views/AutoPostForm.tsx","../src/views/Layout.tsx","../src/views/ErrorPage.tsx","../src/views/SettingsPage.tsx","../src/views/UserPage.tsx","../src/app.tsx","../src/index.ts"],"sourcesContent":["import pc from 'picocolors';\nimport xmlFormat from 'xml-formatter';\nimport type { ResolvedOptions } from './options.js';\n\nexport const UNDEFINED_VALUE = 'None';\n\nconst CERT_OPTIONS = [\n 'cert',\n 'key',\n 'encryptionCert',\n 'encryptionPublicKey',\n 'httpsPrivateKey',\n 'httpsCert',\n];\n\nexport const LOG_LEVELS = ['silent', 'error', 'info'] as const;\n\n/**\n * - `silent`: no output at all\n * - `error`: only unexpected errors (HTTP 5xx), on stderr\n * - `info`: banners, requests and SAML messages (default)\n */\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\nexport function isLogLevel(value: unknown): value is LogLevel {\n return (LOG_LEVELS as readonly unknown[]).includes(value);\n}\n\nexport interface Logger {\n /** Informational output (shown at level `info`). */\n log(message?: string, ...rest: unknown[]): void;\n /** Errors (shown at levels `error` and `info`). */\n error(message?: string, ...rest: unknown[]): void;\n}\n\nexport function createLogger(level: LogLevel = 'info'): Logger {\n const noop = () => {};\n return {\n log: level === 'info' ? (...args) => console.log(...args) : noop,\n error: level === 'silent' ? noop : (...args) => console.error(...args),\n };\n}\n\n/** Indents every line of `text` by `indent` spaces. */\nfunction indentLines(text: string, indent: number): string {\n const pad = ' '.repeat(indent);\n return text.replace(/(^|\\n)/g, `$1${pad}`);\n}\n\n/** Pretty prints XML with syntax coloring (used to log SAML responses). */\nexport function prettyPrintXml(xml: string, indent = 0): string {\n // This works well, because we format the xml before applying the replacements\n const pretty = xmlFormat(xml, { indentation: ' ', collapseContent: true })\n // Matches `<{prefix}:{name} .*?>`\n .replace(\n /<(\\/)?((?:[\\w]+)(?::))?([\\w]+)(.*?)>/g,\n (_m, slash = '', prefix = '', name, rest) => `<${pc.green(`${slash}${prefix}${pc.bold(name)}`)}${rest}>`,\n )\n // Matches ` {attribute}=\"{value}\"`\n .replace(/ ([\\w:]+)=\"(.+?)\"/g, (_m, attr, value) => ` ${pc.white(attr)}=${pc.cyan(`\"${value}\"`)}`);\n return indent ? indentLines(pretty, indent) : pretty;\n}\n\n/** Short, single line representation of an option value for logs. */\nexport function formatOptionValue(key: string, value: unknown): string {\n if (typeof value === 'string' && !CERT_OPTIONS.includes(key)) {\n return value;\n }\n if (CERT_OPTIONS.includes(key) && value) {\n return (\n value\n .toString()\n .replace(/-----.+?-----|\\n/g, '')\n .substring(0, 80) + pc.white('…')\n );\n }\n if (!value && value !== false) {\n return UNDEFINED_VALUE;\n }\n if (typeof value === 'function') {\n return `${value}`.split('\\n')[0].slice(0, -2);\n }\n return JSON.stringify(value);\n}\n\nexport function describeRecord(record: Record<string, unknown>, indent: number, format = formatOptionValue): string {\n return Object.entries(record)\n .map(([key, value]) => `${' '.repeat(indent)}${key}: ${pc.cyan(format(key, value))}`)\n .join('\\n');\n}\n\n/** Startup banner describing the effective configuration. */\nexport function configBanner(options: ResolvedOptions): string {\n const v = (value: unknown) => pc.cyan(value === false ? 'false' : String(value || UNDEFINED_VALUE));\n return [\n `Listener Port:`,\n ` ${pc.cyan(`${options.host}:${options.port}`)}`,\n `HTTPS Enabled:`,\n ` ${pc.cyan(String(options.https))}`,\n ``,\n pc.bold(`[${pc.yellow('Identity Provider')}]`),\n ``,\n `Issuer URI:`,\n ` ${v(options.issuer)}`,\n `Sign Response Message:`,\n ` ${v(options.signResponse)}`,\n `Encrypt Assertion:`,\n ` ${v(options.encryptAssertion)}`,\n `Authentication Context Class Reference:`,\n ` ${v(options.authnContextClassRef)}`,\n `Authentication Context Declaration:`,\n ` ${v(options.authnContextDecl)}`,\n `Default RelayState:`,\n ` ${v(options.relayState)}`,\n ``,\n pc.bold(`[${pc.yellow('Service Provider')}]`),\n ``,\n `Issuer URI:`,\n ` ${v(options.serviceProviderId)}`,\n `Audience URI:`,\n ` ${v(options.audience)}`,\n `ACS URL:`,\n ` ${v(options.acsUrl)}`,\n `SLO URL:`,\n ` ${v(options.sloUrl)}`,\n `Trust ACS URL in Request:`,\n ` ${pc.cyan(String(!options.disableRequestAcsUrl))}`,\n ``,\n ].join('\\n');\n}\n\n/** Banner printed once the server listens. */\nexport function listeningBanner(baseUrl: string, sloEnabled: boolean): string {\n const lines = [\n `IdP Metadata URL:`,\n ` ${pc.cyan(`${baseUrl}/metadata`)}`,\n ``,\n `SSO Bindings:`,\n ` urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST`,\n ` => ${pc.cyan(`${baseUrl}/saml/sso`)}`,\n ` urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect`,\n ` => ${pc.cyan(`${baseUrl}/saml/sso`)}`,\n ];\n if (sloEnabled) {\n lines.push(\n ``,\n `SLO Bindings:`,\n ` urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST`,\n ` => ${pc.cyan(`${baseUrl}/saml/slo`)}`,\n ` urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect`,\n ` => ${pc.cyan(`${baseUrl}/saml/slo`)}`,\n );\n }\n lines.push(``, `IdP server ready at`, ` ${pc.cyan(baseUrl)}`);\n return lines.join('\\n');\n}\n","// Ported from saml-idp/config.js (MIT, see ../LICENSE).\nimport type { ClaimMetadata } from './vendor/samlp/index.js';\n\n/** Profile of the user that is signed in. Values are rendered as SAML attributes. */\nexport type UserProfile = Record<string, string> & {\n userName: string;\n nameIdFormat: string;\n};\n\nexport interface IdpConfig {\n user: UserProfile;\n metadata: ClaimMetadata[];\n}\n\n/** Returns a fresh copy of the default user profile and attribute metadata. */\nexport function createDefaultConfig(): IdpConfig {\n return {\n user: {\n userName: 'saml.jackson@example.com',\n nameIdFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',\n firstName: 'Saml',\n lastName: 'Jackson',\n displayName: 'saml jackson',\n email: 'saml.jackson@example.com',\n mobilePhone: '+1-415-555-5141',\n groups: 'Simple IdP Users, West Coast Users, Cloud Users',\n },\n metadata: [\n {\n id: 'firstName',\n optional: false,\n displayName: 'First Name',\n description: 'The given name of the user',\n multiValue: false,\n },\n {\n id: 'lastName',\n optional: false,\n displayName: 'Last Name',\n description: 'The surname of the user',\n multiValue: false,\n },\n {\n id: 'displayName',\n optional: true,\n displayName: 'Display Name',\n description: 'The display name of the user',\n multiValue: false,\n },\n {\n id: 'email',\n optional: false,\n displayName: 'E-Mail Address',\n description: 'The e-mail address of the user',\n multiValue: false,\n },\n {\n id: 'mobilePhone',\n optional: true,\n displayName: 'Mobile Phone',\n description: 'The mobile phone of the user',\n multiValue: false,\n },\n {\n id: 'groups',\n optional: true,\n displayName: 'Groups',\n description: 'Group memberships of the user',\n multiValue: true,\n },\n {\n id: 'userType',\n optional: true,\n displayName: 'User Type',\n description: 'The type of user',\n options: ['Admin', 'Editor', 'Commenter'],\n },\n ],\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { isAbsolute, resolve } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { createDefaultConfig, type IdpConfig } from './default-config.js';\nimport { isLogLevel, LOG_LEVELS, type LogLevel } from './log.js';\n\n/** Root of the package (works from `src/` and from the built `dist/`). */\nexport const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url));\n\nconst DEFAULT_CERT_PATH = resolve(PACKAGE_ROOT, 'assets/idp-public-cert.pem');\nconst DEFAULT_KEY_PATH = resolve(PACKAGE_ROOT, 'assets/idp-private-key.pem');\n/** Special `configFile` value that selects the built-in default user + metadata. */\nexport const DEFAULT_CONFIG_FILE = 'saml-idp/config.js';\n\nexport 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`;\n\nconst CRYPT_TYPES = {\n certificate: /-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----/,\n // PKCS#1 (RSA PRIVATE KEY) and PKCS#8 (PRIVATE KEY)\n 'private key': /-----BEGIN (?:RSA )?PRIVATE KEY-----[^-]*-----END (?:RSA )?PRIVATE KEY-----/,\n 'public key': /-----BEGIN (?:RSA )?PUBLIC KEY-----[^-]*-----END (?:RSA )?PUBLIC KEY-----/,\n} as const;\n\ntype CryptType = keyof typeof CRYPT_TYPES;\n\n/** Thrown when the options are invalid. The message is meant to be shown to the user. */\nexport class OptionsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'OptionsError';\n }\n}\n\n/** A PEM string / Buffer, or the path of a PEM file. */\nexport type PemOrPath = string | Buffer;\n\n/** Options of the IdP. They match the CLI flags of the original `saml-idp`. */\nexport interface IdpOptions {\n /** IdP web server listener host. Default: `localhost`. */\n host?: string;\n /** IdP web server listener port (`0` picks a free port). Default: `7000`. */\n port?: number;\n /** IdP signature public key certificate (PEM or path). Defaults to the bundled test certificate. */\n cert?: PemOrPath;\n /** IdP signature private key (PEM or path). Defaults to the bundled test key. */\n key?: PemOrPath;\n /** IdP issuer URI. Default: `urn:example:idp`. */\n issuer?: string;\n /** SP Assertion Consumer Service URL. */\n acsUrl: string;\n /** SP single logout URL. Enables SLO when set. */\n sloUrl?: string;\n /** SP audience URI. */\n audience: string;\n /** SP issuer / entity URI. Defaults to the audience. */\n serviceProviderId?: string;\n /** Default SAML RelayState. */\n relayState?: string;\n /** Ignore the ACS URL of the AuthnRequest. */\n disableRequestAcsUrl?: boolean;\n /** Encrypt the assertion with the SP public key. */\n encryptAssertion?: boolean;\n /** SP certificate (PEM or path) used for assertion encryption. */\n encryptionCert?: PemOrPath;\n /** SP RSA public key (PEM or path). Derived from `encryptionCert` when omitted. */\n encryptionPublicKey?: PemOrPath;\n /** Web server TLS private key (PEM or path). */\n httpsPrivateKey?: PemOrPath;\n /** Web server TLS certificate (PEM or path). */\n httpsCert?: PemOrPath;\n /** Enable the HTTPS listener (requires `httpsPrivateKey` and `httpsCert`). */\n https?: boolean;\n /** Sign the SAML Response (the assertion is always signed). Default: `true`. */\n signResponse?: boolean;\n /**\n * Path to a module (`.js`, `.mjs`, `.json`) that exports `{ user, metadata }`.\n * Ignored when `config` is given.\n */\n configFile?: string;\n /** Inline user profile and attribute metadata (overrides `configFile`). */\n config?: Partial<IdpConfig>;\n /** Create a new session for every authn request. Default: `false`. */\n rollSession?: boolean;\n /** Authentication Context Class Reference. */\n authnContextClassRef?: string;\n /** Authentication Context Declaration (XML or path to an XML file). */\n authnContextDecl?: string;\n /**\n * Verbosity of the logs: `silent` (nothing), `error` (unexpected errors only) or `info`.\n * Defaults to the `SAML_IDP_LOG_LEVEL` environment variable, then to `info`.\n */\n logLevel?: LogLevel;\n /** @deprecated Use `logLevel: 'silent'`. */\n silent?: boolean;\n}\n\nexport interface ResolvedOptions {\n host: string;\n port: number;\n cert: string;\n key: string;\n issuer: string;\n acsUrl: string;\n sloUrl?: string;\n audience: string;\n serviceProviderId: string;\n relayState?: string;\n disableRequestAcsUrl: boolean;\n encryptAssertion: boolean;\n encryptionCert?: string;\n encryptionPublicKey?: string;\n httpsPrivateKey?: string;\n httpsCert?: string;\n https: boolean;\n signResponse: boolean;\n rollSession: boolean;\n authnContextClassRef?: string;\n authnContextDecl?: string;\n logLevel: LogLevel;\n config: IdpConfig;\n}\n\nexport function matchesCertType(value: string, type: CryptType): boolean {\n return CRYPT_TYPES[type].test(value);\n}\n\n/** Resolves a file path option, the same way the original `saml-idp` did. */\nexport function resolveFilePath(filePath: string): string | undefined {\n if (filePath.startsWith('saml-idp/')) {\n // Allows file path options to files included in this package\n const resolved = resolve(PACKAGE_ROOT, filePath.replace(/^saml-idp\\//, ''));\n return existsSync(resolved) ? resolved : undefined;\n }\n if (isAbsolute(filePath) && existsSync(filePath)) return filePath;\n if (filePath.startsWith('~/')) {\n const expanded = resolve(process.env.HOME ?? homedir(), filePath.slice(2));\n return existsSync(expanded) ? expanded : undefined;\n }\n return [process.cwd(), PACKAGE_ROOT]\n .map((base) => resolve(base, filePath))\n .find((candidate) => existsSync(candidate));\n}\n\n/** Accepts PEM content or a file path and returns the PEM content. */\nexport function coerceCertFile(\n value: PemOrPath,\n type: CryptType,\n description: string,\n helpText?: string,\n): string {\n const asString = value.toString();\n if (matchesCertType(asString, type)) {\n return asString;\n }\n const filePath = resolveFilePath(asString);\n if (filePath) {\n const content = readFileSync(filePath, 'utf8');\n if (matchesCertType(content, type)) return content;\n throw new OptionsError(`Invalid ${description}: \"${filePath}\" does not contain a valid ${type}`);\n }\n throw new OptionsError(\n `Invalid / missing ${description} - not a valid crypt key/cert or file path` +\n (helpText ? '\\n' + helpText : ''),\n );\n}\n\nasync function loadConfigFile(configFile: string): Promise<Partial<IdpConfig>> {\n if (configFile === DEFAULT_CONFIG_FILE) return {};\n\n const filePath = resolveFilePath(configFile);\n if (!filePath) {\n throw new OptionsError(`SAML attribute config file path \"${configFile}\" is not a valid path.`);\n }\n try {\n if (filePath.endsWith('.json')) {\n return JSON.parse(readFileSync(filePath, 'utf8'));\n }\n const mod = await import(/* @vite-ignore */ pathToFileURL(filePath).href);\n return (mod.default?.user || mod.default?.metadata ? mod.default : mod) as Partial<IdpConfig>;\n } catch (error) {\n throw new OptionsError(\n `Encountered an exception while loading SAML attribute config file \"${filePath}\".\\n${error}`,\n );\n }\n}\n\nfunction coerceAuthnContextDecl(value: string): string {\n if (value.trimStart().startsWith('<')) return value;\n const filePath = resolveFilePath(value);\n if (!filePath) {\n throw new OptionsError(`Authentication Context Declaration file \"${value}\" was not found.`);\n }\n return readFileSync(filePath, 'utf8');\n}\n\n/** Validates the options, applies defaults and reads certificates / config files. */\nexport async function resolveOptions(options: IdpOptions): Promise<ResolvedOptions> {\n if (!options.acsUrl) throw new OptionsError('Missing required option: acsUrl');\n if (!options.audience) throw new OptionsError('Missing required option: audience');\n\n const encryptAssertion = options.encryptAssertion ?? false;\n if (encryptAssertion && options.encryptionCert === undefined) {\n throw new OptionsError('encryptionCert argument is required for assertion encryption');\n }\n\n const logLevel = options.logLevel ?? (options.silent ? 'silent' : process.env.SAML_IDP_LOG_LEVEL ?? 'info');\n if (!isLogLevel(logLevel)) {\n throw new OptionsError(`Invalid logLevel \"${logLevel}\", expected one of: ${LOG_LEVELS.join(', ')}`);\n }\n\n const https = options.https ?? false;\n if (https && (options.httpsPrivateKey === undefined || options.httpsCert === undefined)) {\n throw new OptionsError('httpsPrivateKey and httpsCert arguments are required to enable HTTPS');\n }\n\n const fileConfig = options.config ? {} : await loadConfigFile(options.configFile ?? DEFAULT_CONFIG_FILE);\n const inlineConfig = options.config ?? fileConfig;\n const defaults = createDefaultConfig();\n const config: IdpConfig = {\n // the user is merged with the defaults so that a partial profile still has a NameID\n user: { ...defaults.user, ...(inlineConfig.user ?? {}) },\n metadata: inlineConfig.metadata ?? defaults.metadata,\n };\n\n return {\n host: options.host ?? 'localhost',\n port: options.port ?? 7000,\n cert: coerceCertFile(\n options.cert ?? DEFAULT_CERT_PATH,\n 'certificate',\n 'IdP Signature PublicKey Certificate',\n KEY_CERT_HELP_TEXT,\n ),\n key: coerceCertFile(\n options.key ?? DEFAULT_KEY_PATH,\n 'private key',\n 'IdP Signature PrivateKey Certificate',\n KEY_CERT_HELP_TEXT,\n ),\n issuer: options.issuer ?? 'urn:example:idp',\n acsUrl: options.acsUrl,\n sloUrl: options.sloUrl,\n audience: options.audience,\n serviceProviderId: options.serviceProviderId || options.audience,\n relayState: options.relayState,\n disableRequestAcsUrl: options.disableRequestAcsUrl ?? false,\n encryptAssertion,\n encryptionCert:\n options.encryptionCert !== undefined\n ? coerceCertFile(options.encryptionCert, 'certificate', 'Encryption cert')\n : undefined,\n encryptionPublicKey:\n options.encryptionPublicKey !== undefined\n ? coerceCertFile(options.encryptionPublicKey, 'public key', 'Encryption public key')\n : undefined,\n httpsPrivateKey:\n options.httpsPrivateKey !== undefined\n ? coerceCertFile(options.httpsPrivateKey, 'private key', 'Web Server TLS/SSL Private Key')\n : undefined,\n httpsCert:\n options.httpsCert !== undefined\n ? coerceCertFile(options.httpsCert, 'certificate', 'Web Server TLS/SSL Certificate')\n : undefined,\n https,\n signResponse: options.signResponse ?? true,\n rollSession: options.rollSession ?? false,\n authnContextClassRef:\n options.authnContextClassRef ?? 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',\n authnContextDecl: options.authnContextDecl ? coerceAuthnContextDecl(options.authnContextDecl) : undefined,\n logLevel,\n config,\n };\n}\n","// Ported from saml-idp/lib/simpleProfileMapper.js (MIT, see ../LICENSE).\nimport type { Attributes, ClaimMetadata, ProfileMapper } from './vendor/samlp/index.js';\n\ntype User = Record<string, string | undefined>;\n\n/**\n * Maps the flat user profile to SAML attributes using the attribute metadata:\n * each metadata entry is an attribute; multi-value attributes are comma separated.\n *\n * `getMetadata` is called on every use so edits to the metadata at runtime are honored.\n */\nexport function createSimpleProfileMapper(getMetadata: () => ClaimMetadata[]): ProfileMapper<User> {\n return {\n get metadata() {\n return getMetadata();\n },\n\n getClaims(user) {\n const claims: Attributes = {};\n for (const entry of getMetadata()) {\n const value = user[entry.id];\n claims[entry.id] = entry.multiValue\n ? (value ?? '')\n .split(',')\n .map((v) => v.trim())\n .filter((v) => v !== '')\n : value;\n }\n return Object.keys(claims).length ? claims : undefined;\n },\n\n getNameIdentifier(user) {\n return {\n nameIdentifier: user.userName,\n nameIdentifierFormat: user.nameIdFormat,\n nameIdentifierNameQualifier: user.nameIdNameQualifier || undefined,\n nameIdentifierSPNameQualifier: user.nameIdSPNameQualifier || undefined,\n nameIdentifierSPProvidedID: user.nameIdSPProvidedID || undefined,\n };\n },\n };\n}\n","import { randomBytes } from 'node:crypto';\nimport type { MiddlewareHandler } from 'hono';\nimport { deleteCookie, getSignedCookie, setSignedCookie } from 'hono/cookie';\nimport type { LogoutStore } from './vendor/samlp/index.js';\n\nexport const SESSION_COOKIE = 'idp_sid';\nconst MAX_AGE_SECONDS = 60 * 60;\n\n/** Server-side session of the browser (identified by a signed cookie). */\nexport class Session {\n id: string;\n /** Arbitrary per-session data. */\n data: Record<string, any> = {};\n destroyed = false;\n\n constructor(\n id: string,\n private readonly store: SessionStore,\n ) {\n this.id = id;\n }\n\n /** Destroys the session. The cookie is cleared at the end of the request. */\n destroy(): void {\n this.destroyed = true;\n this.store.delete(this.id);\n }\n\n /** Replaces the session id (and drops the data), like express-session's `regenerate()`. */\n regenerate(): void {\n this.store.delete(this.id);\n this.id = randomBytes(24).toString('hex');\n this.data = {};\n this.destroyed = false;\n this.store.set(this);\n }\n}\n\ninterface Entry {\n session: Session;\n expires: number;\n}\n\n/** In-memory session store with a sliding 1 hour TTL. */\nexport class SessionStore {\n private readonly entries = new Map<string, Entry>();\n\n create(): Session {\n const session = new Session(randomBytes(24).toString('hex'), this);\n this.set(session);\n return session;\n }\n\n get(id: string): Session | undefined {\n const entry = this.entries.get(id);\n if (!entry) return undefined;\n if (entry.expires < Date.now()) {\n this.entries.delete(id);\n return undefined;\n }\n return entry.session;\n }\n\n set(session: Session): void {\n this.entries.set(session.id, { session, expires: Date.now() + MAX_AGE_SECONDS * 1000 });\n }\n\n delete(id: string): void {\n this.entries.delete(id);\n }\n\n clear(): void {\n this.entries.clear();\n }\n}\n\nexport interface SessionVariables {\n session: Session;\n}\n\n/** Hono middleware that attaches a `session` to the context (cookie: `idp_sid`). */\nexport function sessionMiddleware(\n store: SessionStore,\n secret: string,\n): MiddlewareHandler<{ Variables: SessionVariables }> {\n return async (c, next) => {\n const cookieId = await getSignedCookie(c, secret, SESSION_COOKIE);\n const session = (cookieId ? store.get(cookieId) : undefined) ?? store.create();\n c.set('session', session);\n\n await next();\n\n if (session.destroyed) {\n deleteCookie(c, SESSION_COOKIE, { path: '/' });\n return;\n }\n store.set(session); // sliding expiration\n await setSignedCookie(c, SESSION_COOKIE, session.id, secret, {\n path: '/',\n httpOnly: true,\n sameSite: 'Lax',\n maxAge: MAX_AGE_SECONDS,\n });\n };\n}\n\n/** Same session index derivation as the original saml-idp. */\nexport function getSessionIndex(session: Session): string {\n let hash = 0;\n for (let i = 0; i < session.id.length; i++) {\n hash = (hash << 5) - hash + session.id.charCodeAt(i);\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString();\n}\n\n/** Keeps the state of in-flight Single Logout transactions in the session. */\nexport function createLogoutStore(session: Session): LogoutStore {\n const bag = (): Record<string, any> => (session.data._logoutState ??= {});\n return {\n save(state) {\n const handle = randomBytes(16).toString('hex');\n bag()[handle] = state;\n return handle;\n },\n load(handle, options) {\n if (!handle) return undefined;\n const state = bag()[handle];\n if (state && options?.destroy) delete bag()[handle];\n return state;\n },\n update(handle, state) {\n bag()[handle] = state;\n },\n };\n}\n","import type { UserProfile } from './default-config.js';\nimport type { ResolvedOptions } from './options.js';\nimport type { ClaimMetadata } from './vendor/samlp/index.js';\nimport type { DigestAlgorithm, SignatureAlgorithm } from './vendor/saml/sign.js';\n\nexport const IDP_PATHS = {\n SSO: '/saml/sso',\n SLO: '/saml/slo',\n METADATA: '/metadata',\n SIGN_IN: '/signin',\n SIGN_OUT: '/signout',\n SETTINGS: '/settings',\n} as const;\n\n/** IdP settings. They can be changed at runtime from the settings page (or by tests). */\nexport interface IdpSettings {\n issuer: string;\n serviceProviderId: string;\n cert: string;\n key: string;\n audience: string;\n recipient: string;\n destination: string;\n acsUrl: string;\n sloUrl?: string;\n RelayState?: string;\n allowRequestAcsUrl: boolean;\n digestAlgorithm: DigestAlgorithm;\n signatureAlgorithm: SignatureAlgorithm;\n signResponse: boolean;\n encryptAssertion: boolean;\n encryptionCert?: string;\n encryptionPublicKey?: string;\n encryptionAlgorithm: string;\n keyEncryptionAlgorithm: string;\n lifetimeInSeconds: number;\n authnContextClassRef?: string;\n authnContextDecl?: string;\n includeAttributeNameFormat: boolean;\n}\n\n/** Mutable state shared by all the requests served by an IdP instance. */\nexport interface IdpState {\n settings: IdpSettings;\n /** The user that is \"signed in\". Updated by the sign-in form. */\n user: UserProfile;\n /** SAML attributes (claims) the IdP can issue. */\n metadata: ClaimMetadata[];\n /** Restores settings, user and metadata to the values the IdP was started with. */\n reset(): void;\n}\n\nfunction initialSettings(options: ResolvedOptions): IdpSettings {\n return {\n issuer: options.issuer,\n serviceProviderId: options.serviceProviderId,\n cert: options.cert,\n key: options.key,\n audience: options.audience,\n recipient: options.acsUrl,\n destination: options.acsUrl,\n acsUrl: options.acsUrl,\n sloUrl: options.sloUrl,\n RelayState: options.relayState,\n allowRequestAcsUrl: !options.disableRequestAcsUrl,\n digestAlgorithm: 'sha256',\n signatureAlgorithm: 'rsa-sha256',\n signResponse: options.signResponse,\n encryptAssertion: options.encryptAssertion,\n encryptionCert: options.encryptionCert,\n encryptionPublicKey: options.encryptionPublicKey,\n encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',\n keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',\n lifetimeInSeconds: 3600,\n authnContextClassRef: options.authnContextClassRef,\n authnContextDecl: options.authnContextDecl,\n includeAttributeNameFormat: true,\n };\n}\n\nexport function createState(options: ResolvedOptions): IdpState {\n const snapshot = () => ({\n settings: initialSettings(options),\n user: structuredClone(options.config.user),\n metadata: structuredClone(options.config.metadata),\n });\n\n const state: IdpState = {\n ...snapshot(),\n reset() {\n Object.assign(state, snapshot());\n },\n };\n return state;\n}\n","// Ported from mcguinness/node-saml (MIT, see ./LICENSE).\nimport { randomInt } from 'node:crypto';\n\nexport type PemInput = string | Buffer;\n\n/** Strips the PEM armor from a certificate and returns the base64 body on a single line. */\nexport function pemToCert(pem: PemInput): string | null {\n const cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/.exec(pem.toString());\n if (cert) {\n return cert[1].replace(/[\\r\\n]/g, '');\n }\n return null;\n}\n\nconst UID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\n/** Returns a random alphanumeric identifier of the given length. */\nexport function uid(len: number): string {\n let out = '';\n for (let i = 0; i < len; i++) {\n out += UID_CHARS[randomInt(0, UID_CHARS.length)];\n }\n return out;\n}\n\nexport function removeWhitespace(xml: string): string {\n return xml\n .replace(/\\r\\n/g, '')\n .replace(/\\n/g, '')\n .replace(/>(\\s*)</g, '><') // unindent\n .trim();\n}\n\n/**\n * Standardizes PEM content to match the spec (best effort).\n * The original value is returned unmodified if the content is not PEM.\n */\nexport function fixPemFormatting(pem: PemInput): PemInput {\n const entries = pem\n .toString()\n .matchAll(/([-]{5}[^-\\r\\n]+[-]{5})([^-]*)([-]{5}[^-\\r\\n]+[-]{5})/g);\n let fixed = '';\n for (const parts of entries) {\n fixed += `${parts[1]}\\n${parts[2].replace(/[\\r\\n]/g, '')}\\n${parts[3]}\\n`;\n }\n if (fixed.length === 0) {\n return pem;\n }\n return Buffer.from(fixed);\n}\n\n/**\n * Formats a date as an `xs:dateTime` in UTC with millisecond precision\n * (`YYYY-MM-DDTHH:mm:ss.SSSZ`).\n */\nexport function formatInstant(date: Date): string {\n return date.toISOString();\n}\n\n/** Escapes a string for use in XML text or double-quoted attribute values. */\nexport function escapeXml(value: unknown): string {\n return String(value ?? '')\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n","// Ported from mcguinness/node-saml (MIT, see ./LICENSE).\nimport { X509Certificate } from 'node:crypto';\nimport * as xmlenc from 'xml-encryption';\nimport { fixPemFormatting, removeWhitespace, type PemInput } from './utils.js';\n\nexport interface EncryptXmlOptions {\n /** Service Provider certificate (PEM). */\n encryptionCert: PemInput;\n /**\n * Service Provider RSA public key (PEM). Derived from `encryptionCert`\n * when omitted.\n */\n encryptionPublicKey?: PemInput;\n encryptionAlgorithm?: string;\n keyEncryptionAlgorithm?: string;\n}\n\nexport const DEFAULT_ENCRYPTION_ALGORITHM = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';\nexport const DEFAULT_KEY_ENCRYPTION_ALGORITHM = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';\n\nfunction derivePublicKey(cert: PemInput): string {\n return new X509Certificate(cert).publicKey.export({ type: 'spki', format: 'pem' }).toString();\n}\n\nfunction encryptOnce(xml: string, options: xmlenc.EncryptOptions): Promise<string> {\n return new Promise((resolve, reject) => {\n xmlenc.encrypt(xml, options, (err, encrypted) => (err ? reject(err) : resolve(encrypted)));\n });\n}\n\n/** Encrypts an XML fragment for the given Service Provider (XML Encryption). */\nexport async function encryptXml(xml: string, options: EncryptXmlOptions): Promise<string> {\n const encryptOptions: xmlenc.EncryptOptions = {\n rsa_pub: options.encryptionPublicKey ?? derivePublicKey(options.encryptionCert),\n pem: options.encryptionCert,\n encryptionAlgorithm: options.encryptionAlgorithm ?? DEFAULT_ENCRYPTION_ALGORITHM,\n keyEncryptionAlgorithm: options.keyEncryptionAlgorithm ?? DEFAULT_KEY_ENCRYPTION_ALGORITHM,\n };\n\n try {\n return removeWhitespace(await encryptOnce(xml, encryptOptions));\n } catch {\n // Attempt to fix PEM formatting issues and retry\n const retried = await encryptOnce(xml, {\n ...encryptOptions,\n rsa_pub: fixPemFormatting(encryptOptions.rsa_pub),\n pem: fixPemFormatting(encryptOptions.pem),\n });\n return removeWhitespace(retried);\n }\n}\n","// Ported from mcguinness/node-saml (MIT, see ./LICENSE).\nimport { SignedXml } from 'xml-crypto';\nimport { fixPemFormatting, pemToCert, removeWhitespace, type PemInput } from './utils.js';\n\nexport const SIGNATURE_ALGORITHMS = {\n 'rsa-sha256': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',\n 'rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',\n} as const;\n\nexport const DIGEST_ALGORITHMS = {\n sha256: 'http://www.w3.org/2001/04/xmlenc#sha256',\n sha1: 'http://www.w3.org/2000/09/xmldsig#sha1',\n} as const;\n\nexport type SignatureAlgorithm = keyof typeof SIGNATURE_ALGORITHMS;\nexport type DigestAlgorithm = keyof typeof DIGEST_ALGORITHMS;\n\nexport const ENVELOPED_SIGNATURE = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature';\nexport const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';\n\nexport interface SignXmlOptions {\n key: PemInput;\n cert: PemInput;\n signatureAlgorithm?: SignatureAlgorithm;\n digestAlgorithm?: DigestAlgorithm;\n /** Namespace prefix of the generated `<Signature>` element (default: none). */\n signatureNamespacePrefix?: string;\n /** XPath of the element that the signature is placed after. */\n xpathToNodeBeforeSignature: string;\n /** XPath of the element to sign. */\n xpathToSign: string;\n signatureIdAttribute?: string;\n}\n\n/** Signs an XML string with an enveloped, exclusive-c14n XML signature. */\nexport function signXml(xml: string, options: SignXmlOptions): string {\n if (!options.key) throw new Error('Expect a private key in pem format');\n if (!options.cert) throw new Error('Expect a public key cert in pem format');\n\n const cert = pemToCert(options.cert);\n if (!cert) throw new Error('Expect a public key cert in pem format');\n\n const sign = (key: PemInput) => {\n const sig = new SignedXml({\n privateKey: key,\n signatureAlgorithm: SIGNATURE_ALGORITHMS[options.signatureAlgorithm ?? 'rsa-sha256'],\n canonicalizationAlgorithm: EXC_C14N,\n idAttribute: options.signatureIdAttribute,\n getKeyInfoContent: ({ prefix } = {}) => {\n const p = prefix ? `${prefix}:` : '';\n return `<${p}X509Data><${p}X509Certificate>${cert}</${p}X509Certificate></${p}X509Data>`;\n },\n });\n sig.addReference({\n xpath: options.xpathToSign,\n transforms: [ENVELOPED_SIGNATURE, EXC_C14N],\n digestAlgorithm: DIGEST_ALGORITHMS[options.digestAlgorithm ?? 'sha256'],\n });\n sig.computeSignature(xml, {\n location: { reference: options.xpathToNodeBeforeSignature, action: 'after' },\n prefix: options.signatureNamespacePrefix ?? '',\n });\n return sig.getSignedXml();\n };\n\n try {\n return sign(options.key);\n } catch {\n return sign(fixPemFormatting(options.key));\n }\n}\n\n/** Serializes without signing (strategy used when `signAssertion` is false). */\nexport function unsigned(xml: string): string {\n return removeWhitespace(xml);\n}\n","// Ported from mcguinness/node-saml (MIT, see ./LICENSE).\n// Builds, signs and optionally encrypts a SAML 2.0 Assertion.\nimport { DOMParser, XMLSerializer } from '@xmldom/xmldom';\nimport { name as isXmlName } from 'xml-name-validator';\nimport { encryptXml, type EncryptXmlOptions } from './encrypt.js';\nimport { signXml, unsigned, type DigestAlgorithm, type SignatureAlgorithm } from './sign.js';\nimport { formatInstant, removeWhitespace, uid, type PemInput } from './utils.js';\n\n/** DOM document type produced by `@xmldom/xmldom`. */\nexport type XmlDocument = ReturnType<DOMParser['parseFromString']>;\n\nexport const SAML_ASSERTION_NS = 'urn:oasis:names:tc:SAML:2.0:assertion';\n\nconst ASSERTION_TEMPLATE = `<saml:Assertion xmlns:saml=\"${SAML_ASSERTION_NS}\" Version=\"2.0\" ID=\"\" IssueInstant=\"\">\n <saml:Issuer></saml:Issuer>\n <saml:Subject>\n <saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\" />\n <saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">\n <saml:SubjectConfirmationData />\n </saml:SubjectConfirmation>\n </saml:Subject>\n <saml:Conditions />\n <saml:AuthnStatement AuthnInstant=\"\">\n <saml:AuthnContext>\n <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef>\n </saml:AuthnContext>\n </saml:AuthnStatement>\n</saml:Assertion>`;\n\nexport type AttributeValue = string | number | boolean | undefined;\nexport type Attributes = Record<string, AttributeValue | AttributeValue[]>;\n\nexport interface Saml20Options {\n uid?: string;\n issuer?: string;\n lifetimeInSeconds?: number;\n audiences?: string | string[];\n recipient?: string;\n inResponseTo?: string;\n attributes?: Attributes;\n includeAttributeNameFormat?: boolean;\n typedAttributes?: boolean;\n sessionIndex?: string;\n nameIdentifier?: string;\n nameIdentifierFormat?: string;\n nameIdentifierNameQualifier?: string;\n nameIdentifierSPNameQualifier?: string;\n nameIdentifierSPProvidedID?: string;\n authnContextClassRef?: string;\n /** Lets the caller mutate the assertion DOM before it is signed. */\n transformAssertion?: (doc: XmlDocument) => void;\n\n // XML Dsig\n signAssertion?: boolean;\n key?: PemInput;\n cert?: PemInput;\n signatureAlgorithm?: SignatureAlgorithm;\n digestAlgorithm?: DigestAlgorithm;\n signatureNamespacePrefix?: string;\n\n // XML Encryption\n encryptionCert?: PemInput;\n encryptionPublicKey?: PemInput;\n encryptionAlgorithm?: string;\n keyEncryptionAlgorithm?: string;\n}\n\nfunction getAttributeType(value: unknown): string {\n switch (typeof value) {\n case 'string':\n return 'xs:string';\n case 'boolean':\n return 'xs:boolean';\n case 'number':\n return 'xs:double';\n default:\n return 'xs:anyType';\n }\n}\n\nconst URI_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\S+$/;\n\nfunction getNameFormat(name: string): string {\n if (URI_RE.test(name)) {\n return 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';\n }\n // Check that the name is a valid xs:Name -> https://www.w3.org/TR/xmlschema-2/#Name\n if (isXmlName(name)) {\n return 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic';\n }\n return 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified';\n}\n\n/** Builds the assertion DOM (unsigned, unencrypted). */\nexport function buildAssertionDocument(options: Saml20Options): XmlDocument {\n const doc = new DOMParser().parseFromString(ASSERTION_TEMPLATE, 'text/xml');\n const root = doc.documentElement!;\n\n root.setAttribute('ID', `_${options.uid || uid(32)}`);\n if (options.issuer) {\n root.getElementsByTagName('saml:Issuer')[0].textContent = options.issuer;\n }\n\n const now = new Date();\n root.setAttribute('IssueInstant', formatInstant(now));\n\n const conditions = root.getElementsByTagName('saml:Conditions')[0];\n const confirmationData = root.getElementsByTagName('saml:SubjectConfirmationData')[0];\n\n if (options.lifetimeInSeconds) {\n const notOnOrAfter = formatInstant(new Date(now.getTime() + options.lifetimeInSeconds * 1000));\n conditions.setAttribute('NotBefore', formatInstant(now));\n conditions.setAttribute('NotOnOrAfter', notOnOrAfter);\n confirmationData.setAttribute('NotOnOrAfter', notOnOrAfter);\n }\n\n if (options.audiences) {\n const audienceRestriction = doc.createElementNS(SAML_ASSERTION_NS, 'saml:AudienceRestriction');\n const audiences = Array.isArray(options.audiences) ? options.audiences : [options.audiences];\n for (const audience of audiences) {\n const element = doc.createElementNS(SAML_ASSERTION_NS, 'saml:Audience');\n element.textContent = audience;\n audienceRestriction.appendChild(element);\n }\n conditions.appendChild(audienceRestriction);\n }\n\n if (options.recipient) confirmationData.setAttribute('Recipient', options.recipient);\n if (options.inResponseTo) confirmationData.setAttribute('InResponseTo', options.inResponseTo);\n\n const typedAttributes = options.typedAttributes ?? true;\n const includeAttributeNameFormat = options.includeAttributeNameFormat ?? true;\n\n if (options.attributes) {\n const statement = doc.createElementNS(SAML_ASSERTION_NS, 'saml:AttributeStatement');\n statement.setAttribute('xmlns:xs', 'http://www.w3.org/2001/XMLSchema');\n statement.setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');\n root.appendChild(statement);\n\n for (const [prop, raw] of Object.entries(options.attributes)) {\n if (typeof raw === 'undefined') continue;\n\n const attributeElement = doc.createElementNS(SAML_ASSERTION_NS, 'saml:Attribute');\n attributeElement.setAttribute('Name', prop);\n if (includeAttributeNameFormat) {\n attributeElement.setAttribute('NameFormat', getNameFormat(prop));\n }\n\n const values = (Array.isArray(raw) ? raw : [raw]).filter((v) => typeof v !== 'undefined');\n for (const value of values) {\n const valueElement = doc.createElementNS(SAML_ASSERTION_NS, 'saml:AttributeValue');\n valueElement.setAttribute('xsi:type', typedAttributes ? getAttributeType(value) : 'xs:anyType');\n valueElement.textContent = String(value);\n attributeElement.appendChild(valueElement);\n }\n\n // saml:Attribute must have at least one saml:AttributeValue\n if (values.length > 0) {\n statement.appendChild(attributeElement);\n }\n }\n }\n\n const authnStatement = root.getElementsByTagName('saml:AuthnStatement')[0];\n authnStatement.setAttribute('AuthnInstant', formatInstant(now));\n if (options.sessionIndex) {\n authnStatement.setAttribute('SessionIndex', options.sessionIndex);\n }\n\n const nameID = root.getElementsByTagNameNS(SAML_ASSERTION_NS, 'NameID')[0];\n if (options.nameIdentifier) nameID.textContent = options.nameIdentifier;\n if (options.nameIdentifierFormat) nameID.setAttribute('Format', options.nameIdentifierFormat);\n if (options.nameIdentifierNameQualifier) {\n nameID.setAttribute('NameQualifier', options.nameIdentifierNameQualifier);\n }\n if (options.nameIdentifierSPNameQualifier) {\n nameID.setAttribute('SPNameQualifier', options.nameIdentifierSPNameQualifier);\n }\n if (options.nameIdentifierSPProvidedID) {\n nameID.setAttribute('SPProvidedID', options.nameIdentifierSPProvidedID);\n }\n\n if (options.authnContextClassRef) {\n root.getElementsByTagName('saml:AuthnContextClassRef')[0].textContent =\n options.authnContextClassRef;\n }\n\n if (typeof options.transformAssertion === 'function') {\n options.transformAssertion(doc);\n }\n\n return doc;\n}\n\n/**\n * Creates a SAML 2.0 assertion.\n *\n * The assertion is signed unless `signAssertion === false` and is wrapped in a\n * `<saml:EncryptedAssertion>` when `encryptionCert` is provided.\n */\nexport async function createAssertion(options: Saml20Options): Promise<string> {\n const doc = buildAssertionDocument(options);\n const xml = removeWhitespace(new XMLSerializer().serializeToString(doc));\n\n const signed =\n options.signAssertion === false\n ? unsigned(xml)\n : signXml(xml, {\n key: options.key as PemInput,\n cert: options.cert as PemInput,\n signatureAlgorithm: options.signatureAlgorithm,\n digestAlgorithm: options.digestAlgorithm,\n signatureNamespacePrefix: options.signatureNamespacePrefix,\n xpathToNodeBeforeSignature: \"//*[local-name(.)='Issuer']\",\n xpathToSign: \"//*[local-name(.)='Assertion']\",\n signatureIdAttribute: 'ID',\n });\n\n if (!options.encryptionCert) {\n return signed;\n }\n\n const encryptOptions: EncryptXmlOptions = {\n encryptionCert: options.encryptionCert,\n encryptionPublicKey: options.encryptionPublicKey,\n encryptionAlgorithm: options.encryptionAlgorithm,\n keyEncryptionAlgorithm: options.keyEncryptionAlgorithm,\n };\n const encrypted = await encryptXml(signed, encryptOptions);\n return removeWhitespace(\n `<saml:EncryptedAssertion xmlns:saml=\"${SAML_ASSERTION_NS}\">${encrypted}</saml:EncryptedAssertion>`,\n );\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE).\n\nexport const BINDINGS = {\n HTTP_POST: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',\n HTTP_REDIRECT: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',\n} as const;\n\nexport const STATUS = {\n SUCCESS: 'urn:oasis:names:tc:SAML:2.0:status:Success',\n PARTIAL_LOGOUT: 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout',\n RESPONDER: 'urn:oasis:names:tc:SAML:2.0:status:Responder',\n} as const;\n\nexport type ElementType = 'LOGOUT_REQUEST' | 'LOGOUT_RESPONSE' | 'RESPONSE' | 'AUTHN_REQUEST';\n\nexport interface ElementDescriptor {\n PROP: 'SAMLRequest' | 'SAMLResponse';\n SIGNATURE_VALIDATION_PATH?: string;\n SIGNATURE_LOCATION_PATH?: string;\n ISSUER_PATH?: string;\n SESSION_INDEX_PATH?: string;\n NAME_ID?: string;\n AUTHN_CONTEXT_CLASS_REF_PATH?: string;\n}\n\nexport const ELEMENTS: Record<ElementType, ElementDescriptor> = {\n LOGOUT_REQUEST: {\n PROP: 'SAMLRequest',\n SIGNATURE_VALIDATION_PATH:\n \"//*[local-name(.)='LogoutRequest']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']\",\n SIGNATURE_LOCATION_PATH:\n \"//*[local-name(.)='LogoutRequest' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']\",\n ISSUER_PATH:\n \"//*[local-name(.)='Issuer' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/text()\",\n SESSION_INDEX_PATH:\n \"//*[local-name(.)='SessionIndex' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']/text()\",\n NAME_ID:\n \"//*[local-name(.)='NameID' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/text()\",\n },\n LOGOUT_RESPONSE: {\n PROP: 'SAMLResponse',\n SIGNATURE_VALIDATION_PATH:\n \"//*[local-name(.)='LogoutResponse']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']\",\n SIGNATURE_LOCATION_PATH:\n \"//*[local-name(.)='LogoutResponse' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']\",\n },\n RESPONSE: {\n PROP: 'SAMLResponse',\n SIGNATURE_LOCATION_PATH:\n \"//*[local-name(.)='Response' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:protocol']\",\n },\n AUTHN_REQUEST: {\n PROP: 'SAMLRequest',\n SIGNATURE_VALIDATION_PATH:\n \"//*[local-name(.)='AuthnRequest']/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']\",\n AUTHN_CONTEXT_CLASS_REF_PATH: \"//*[local-name(.)='AuthnContextClassRef']/text()\",\n },\n};\n\nexport const ALGORITHMS = {\n SIGNATURE: {\n 'rsa-sha256': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',\n 'rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',\n },\n DIGEST: {\n sha256: 'http://www.w3.org/2001/04/xmlenc#sha256',\n sha1: 'http://www.w3.org/2000/09/xmldsig#sha1',\n },\n} as const;\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE).\nimport { createHash } from 'node:crypto';\n\n/** Returns the base64 body of the first PEM block (no armor, no line breaks). */\nexport function removeHeaders(cert: string | Buffer): string | null {\n const pem = /-----BEGIN (\\w*)-----([^-]*)-----END (\\w*)-----/g.exec(cert.toString());\n if (pem && pem.length > 0) {\n return pem[2].replace(/[\\r\\n]/g, '');\n }\n return null;\n}\n\n/** SHA-1 thumbprint (upper-case hex) of a base64 DER certificate. */\nexport function calculateThumbprint(base64Cert: string): string {\n return createHash('sha1')\n .update(Buffer.from(base64Cert.replace(/\\s/g, ''), 'base64'))\n .digest('hex')\n .toUpperCase();\n}\n\nexport function thumbprint(pem: string | Buffer): string {\n const cert = removeHeaders(pem);\n if (!cert) throw new Error('Invalid certificate');\n return calculateThumbprint(cert);\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE): claims/PassportProfileMapper.\nimport type { Attributes } from '../saml/saml20.js';\n\nexport interface ClaimMetadata {\n id: string;\n optional?: boolean;\n displayName: string;\n description?: string;\n multiValue?: boolean;\n options?: string[];\n}\n\nexport interface NameIdentifier {\n nameIdentifier?: string;\n nameIdentifierFormat?: string;\n nameIdentifierNameQualifier?: string;\n nameIdentifierSPNameQualifier?: string;\n nameIdentifierSPProvidedID?: string;\n}\n\n/** Maps an application user profile to SAML claims and a NameID. */\nexport interface ProfileMapper<TUser = any> {\n /** Claims advertised in the IdP metadata. */\n metadata: ClaimMetadata[];\n getClaims(user: TUser): Attributes | undefined;\n getNameIdentifier(user: TUser): NameIdentifier;\n}\n\nconst fm = {\n nameIdentifier: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier',\n email: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',\n name: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',\n givenname: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',\n surname: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',\n};\n\ninterface PassportUser {\n id?: string;\n emails?: Array<{ value?: string }>;\n displayName?: string;\n name?: { givenName?: string; familyName?: string };\n [key: string]: unknown;\n}\n\nconst DONT_REMAP = ['emails', 'displayName', 'name', 'id', '_json'];\n\n/** Default mapper: maps a Passport.js user profile to WS-Federation style claims. */\nexport const passportProfileMapper: ProfileMapper<PassportUser> = {\n metadata: [\n { id: fm.email, optional: true, displayName: 'E-Mail Address', description: 'The e-mail address of the user' },\n { id: fm.givenname, optional: true, displayName: 'Given Name', description: 'The given name of the user' },\n { id: fm.name, optional: true, displayName: 'Name', description: 'The unique name of the user' },\n { id: fm.surname, optional: true, displayName: 'Surname', description: 'The surname of the user' },\n { id: fm.nameIdentifier, optional: true, displayName: 'Name ID', description: 'The SAML name identifier of the user' },\n ],\n\n getClaims(pu) {\n const claims: Attributes = {};\n claims[fm.nameIdentifier] = pu.id;\n claims[fm.email] = pu.emails?.[0]?.value;\n claims[fm.name] = pu.displayName;\n claims[fm.givenname] = pu.name?.givenName;\n claims[fm.surname] = pu.name?.familyName;\n\n for (const key of Object.keys(pu)) {\n if (!DONT_REMAP.includes(key)) {\n claims['http://schemas.passportjs.com/' + key] = pu[key] as Attributes[string];\n }\n }\n return claims;\n },\n\n getNameIdentifier(pu) {\n const claims = this.getClaims(pu) ?? {};\n return {\n nameIdentifier: (claims[fm.nameIdentifier] || claims[fm.name] || claims[fm.email]) as\n | string\n | undefined,\n };\n },\n};\n","import { DOMParser } from '@xmldom/xmldom';\nimport xpath from 'xpath';\n\n/** DOM node/document types produced by `@xmldom/xmldom`. */\nexport type XmlDocument = ReturnType<DOMParser['parseFromString']>;\nexport type XmlNode = NonNullable<XmlDocument['documentElement']>;\n\nexport function parseXml(xml: string): XmlDocument {\n return new DOMParser({\n onError: (level, msg) => {\n if (level === 'fatalError') throw new Error(msg);\n },\n }).parseFromString(xml, 'text/xml');\n}\n\n/** Evaluates an XPath expression and always returns an array of nodes. */\nexport function selectNodes(expression: string, node: unknown): XmlNode[] {\n const result = xpath.select(expression, node as Node);\n return Array.isArray(result) ? (result as unknown as XmlNode[]) : [];\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE).\nimport { createSign, createVerify } from 'node:crypto';\nimport { SignedXml } from 'xml-crypto';\nimport { signXml as signXmlDsig } from '../saml/sign.js';\nimport { ALGORITHMS, ELEMENTS } from './constants.js';\nimport { calculateThumbprint } from './encoders.js';\nimport { selectNodes, type XmlDocument } from './xml.js';\n\ntype SignatureAlgorithmName = keyof typeof ALGORITHMS.SIGNATURE;\ntype DigestAlgorithmName = keyof typeof ALGORITHMS.DIGEST;\n\nconst DEFAULT_SIG_ALG: SignatureAlgorithmName = 'rsa-sha256';\nconst DEFAULT_DIGEST_ALG: DigestAlgorithmName = 'sha256';\nconst DSIG_NS = 'http://www.w3.org/2000/09/xmldsig#';\n\nexport interface SigningOptions {\n key: string | Buffer;\n cert: string | Buffer;\n signatureAlgorithm?: SignatureAlgorithmName;\n digestAlgorithm?: DigestAlgorithmName;\n /** XPath of the element to sign (default: `<LogoutRequest>`). */\n reference?: string;\n}\n\nexport interface ValidationOptions {\n signingCert?: string | Buffer;\n thumbprints?: string | string[];\n}\n\nexport function getSigAlg(options: { signatureAlgorithm?: SignatureAlgorithmName }): string {\n return ALGORITHMS.SIGNATURE[options.signatureAlgorithm ?? DEFAULT_SIG_ALG];\n}\n\n/** Adds an enveloped signature (placed after `<Issuer>`) to the root element. */\nexport function signXml(options: SigningOptions, xml: string): string {\n return signXmlDsig(xml, {\n key: options.key,\n cert: options.cert,\n signatureAlgorithm: options.signatureAlgorithm ?? DEFAULT_SIG_ALG,\n digestAlgorithm: options.digestAlgorithm ?? DEFAULT_DIGEST_ALG,\n xpathToSign: options.reference ?? (ELEMENTS.LOGOUT_REQUEST.SIGNATURE_LOCATION_PATH as string),\n xpathToNodeBeforeSignature: \"//*[local-name(.)='Issuer']\",\n signatureIdAttribute: 'ID',\n });\n}\n\nexport function certToPEM(cert: string | Buffer): string {\n const value = cert.toString();\n if (/-----BEGIN CERTIFICATE-----/.test(value)) {\n return value;\n }\n const body = value.replace(/\\s/g, '').match(/.{1,64}/g)?.join('\\n') ?? '';\n return `-----BEGIN CERTIFICATE-----\\n${body}\\n-----END CERTIFICATE-----\\n`;\n}\n\n/**\n * Validates the enveloped signature of the root element of `xml`.\n *\n * - If thumbprints are configured, the certificate embedded in `<KeyInfo>` is\n * used and its thumbprint must match one of them.\n * - Otherwise the configured `signingCert` is the only trusted key.\n *\n * @returns a list of validation errors (empty when the signature is valid)\n */\nexport function validateXmlEmbeddedSignature(\n xml: XmlDocument,\n xmlString: string,\n options: ValidationOptions,\n): string[] {\n const signature = selectNodes(\n \"/*/*[local-name(.)='Signature' and namespace-uri(.)='\" + DSIG_NS + \"']\",\n xml,\n )[0];\n if (!signature) {\n return ['Signature is missing'];\n }\n\n const thumbprints = options.thumbprints\n ? Array.isArray(options.thumbprints)\n ? options.thumbprints\n : [options.thumbprints]\n : [];\n\n let calculatedThumbprint = '';\n\n const sig = new SignedXml({\n publicCert: options.signingCert ? certToPEM(options.signingCert) : undefined,\n getCertFromKeyInfo: (keyInfo) => {\n if (thumbprints.length === 0 || !keyInfo) return null;\n const certNode = selectNodes(\".//*[local-name(.)='X509Certificate']\", keyInfo)[0];\n const base64 = certNode?.textContent?.replace(/\\s/g, '');\n if (!base64) return null;\n calculatedThumbprint = calculateThumbprint(base64);\n return certToPEM(base64);\n },\n });\n\n if (!options.signingCert && thumbprints.length === 0) {\n return ['options.signingCert or options.thumbprints must be specified to validate the signature'];\n }\n\n try {\n sig.loadSignature(signature as unknown as Node);\n if (!sig.checkSignature(xmlString)) {\n return sig.getSignedReferences().length === 0 ? ['Invalid signature'] : ['Invalid signature reference'];\n }\n } catch (e) {\n return [e instanceof Error ? e.message : String(e)];\n }\n\n if (thumbprints.length > 0 && !options.signingCert) {\n const valid = thumbprints.some((t) => calculatedThumbprint === t.replace(/:/g, '').toUpperCase());\n if (!valid) {\n return [\n `Invalid thumbprint (configured: ${thumbprints.join(', ').toUpperCase()}. calculated: ${calculatedThumbprint})`,\n ];\n }\n }\n\n return [];\n}\n\n/** Computes a detached signature (HTTP-Redirect binding) over `content`. */\nexport function sign(\n options: { key: string | Buffer; signatureAlgorithm?: SignatureAlgorithmName },\n content: string,\n): string {\n const signer = createSign((options.signatureAlgorithm ?? DEFAULT_SIG_ALG).toUpperCase());\n signer.update(content);\n return signer.sign(options.key, 'base64');\n}\n\n/** Verifies a detached signature. `signatureAlgorithm` is the `SigAlg` URI. */\nexport function isValidContentAndSignature(\n content: string,\n signature: string,\n options: { signingCert: string | Buffer; signatureAlgorithm: string },\n): boolean {\n const verifier = createVerify(options.signatureAlgorithm.split('#')[1].toUpperCase());\n verifier.update(content);\n return verifier.verify(certToPEM(options.signingCert), signature, 'base64');\n}\n","// Replaces the EJS templates of mcguinness/node-samlp (MIT, see ./LICENSE)\n// with typed template-literal functions. All interpolated values are escaped.\nimport { escapeXml as esc } from '../saml/utils.js';\n\nexport interface SamlResponseData {\n id: string;\n instant: string;\n destination?: string;\n inResponseTo?: string;\n issuer: string;\n samlStatusCode: string;\n samlStatusMessage?: string;\n /** Raw (already serialized) assertion XML. */\n assertion?: string;\n}\n\nexport function samlResponse(data: SamlResponseData): string {\n return (\n `<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"${esc(data.id)}\"` +\n (data.inResponseTo ? ` InResponseTo=\"${esc(data.inResponseTo)}\"` : '') +\n ` Version=\"2.0\" IssueInstant=\"${esc(data.instant)}\"` +\n (data.destination ? ` Destination=\"${esc(data.destination)}\"` : '') +\n `>` +\n `<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">${esc(data.issuer)}</saml:Issuer>` +\n `<samlp:Status><samlp:StatusCode Value=\"${esc(data.samlStatusCode)}\"/>` +\n (data.samlStatusMessage\n ? `<samlp:StatusMessage Value=\"${esc(data.samlStatusMessage)}\"/>`\n : '') +\n `</samlp:Status>` +\n (data.assertion ?? '') +\n `</samlp:Response>`\n );\n}\n\nexport interface LogoutResponseData {\n id: string;\n instant: string;\n destination?: string;\n inResponseTo?: string;\n issuer: string;\n samlStatusCode: string;\n samlStatusMessage?: string;\n}\n\nexport function logoutResponse(data: LogoutResponseData): string {\n return (\n `<samlp:LogoutResponse xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"${esc(data.id)}\"` +\n (data.inResponseTo ? ` InResponseTo=\"${esc(data.inResponseTo)}\"` : '') +\n ` Version=\"2.0\" IssueInstant=\"${esc(data.instant)}\"` +\n (data.destination ? ` Destination=\"${esc(data.destination)}\"` : '') +\n `>` +\n `<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">${esc(data.issuer)}</saml:Issuer>` +\n `<samlp:Status><samlp:StatusCode Value=\"${esc(data.samlStatusCode)}\"/>` +\n (data.samlStatusMessage\n ? `<samlp:StatusMessage>${esc(data.samlStatusMessage)}</samlp:StatusMessage>`\n : '') +\n `</samlp:Status></samlp:LogoutResponse>`\n );\n}\n\nexport interface LogoutRequestData {\n ID: string;\n IssueInstant: string;\n Issuer: string;\n Destination?: string;\n NameID: {\n value?: string;\n Format?: string;\n NameQualifier?: string;\n SPNameQualifier?: string;\n SPProvidedID?: string;\n };\n SessionIndex?: string;\n}\n\nexport function logoutRequest(data: LogoutRequestData): string {\n const nameId = data.NameID;\n return (\n `<samlp:LogoutRequest ID=\"_${esc(data.ID)}\" Version=\"2.0\" IssueInstant=\"${esc(data.IssueInstant)}\"` +\n (data.Destination ? ` Destination=\"${esc(data.Destination)}\"` : '') +\n ` Consent=\"urn:oasis:names:tc:SAML:2.0:consent:unspecified\"` +\n ` xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">` +\n `<Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">${esc(data.Issuer)}</Issuer>` +\n `<NameID xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\"` +\n (nameId.NameQualifier ? ` NameQualifier=\"${esc(nameId.NameQualifier)}\"` : '') +\n (nameId.SPNameQualifier ? ` SPNameQualifier=\"${esc(nameId.SPNameQualifier)}\"` : '') +\n (nameId.Format ? ` Format=\"${esc(nameId.Format)}\"` : '') +\n (nameId.SPProvidedID ? ` SPProvidedID=\"${esc(nameId.SPProvidedID)}\"` : '') +\n `>${esc(nameId.value)}</NameID>` +\n (data.SessionIndex ? `<samlp:SessionIndex>${esc(data.SessionIndex)}</samlp:SessionIndex>` : '') +\n `</samlp:LogoutRequest>`\n );\n}\n\nexport interface MetadataData {\n issuer: string;\n pem: string;\n redirectEndpoint: string;\n postEndpoint: string;\n logoutEndpoints: { redirect?: string; post?: string };\n claimTypes: Array<{ id: string; displayName: string }>;\n}\n\nexport function metadata(data: MetadataData): string {\n const lines = [\n `<EntityDescriptor entityID=\"${esc(data.issuer)}\" xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\">`,\n ` <IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">`,\n ` <KeyDescriptor use=\"signing\">`,\n ` <KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">`,\n ` <X509Data>`,\n ` <X509Certificate>${esc(data.pem)}</X509Certificate>`,\n ` </X509Data>`,\n ` </KeyInfo>`,\n ` </KeyDescriptor>`,\n ];\n if (data.logoutEndpoints.redirect) {\n lines.push(\n ` <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"${esc(data.logoutEndpoints.redirect)}\"/>`,\n );\n }\n if (data.logoutEndpoints.post) {\n lines.push(\n ` <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"${esc(data.logoutEndpoints.post)}\"/>`,\n );\n }\n lines.push(\n ` <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>`,\n ` <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>`,\n ` <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>`,\n ` <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"${esc(data.redirectEndpoint)}\"/>`,\n ` <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"${esc(data.postEndpoint)}\"/>`,\n );\n for (const ct of data.claimTypes) {\n lines.push(\n ` <Attribute Name=\"${esc(ct.id)}\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"${esc(ct.displayName)}\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\"/>`,\n );\n }\n lines.push(` </IDPSSODescriptor>`, `</EntityDescriptor>`);\n return lines.join('\\n');\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE).\nimport { randomBytes } from 'node:crypto';\nimport { inflateRaw } from 'node:zlib';\nimport { promisify } from 'node:util';\nimport qs from 'node:querystring';\nimport { ELEMENTS, type ElementType } from './constants.js';\nimport * as signers from './signers.js';\nimport { parseXml, selectNodes, type XmlDocument } from './xml.js';\n\nconst inflateRawAsync = promisify(inflateRaw);\n\nconst SIGNATURE_ALGORITHM_NAMES: Record<string, string> = {\n 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'rsa-sha256',\n 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'rsa-sha1',\n};\n\n/** Minimal, framework-agnostic view of an incoming HTTP request. */\nexport interface SamlHttpInput {\n query: Record<string, string | undefined>;\n body: Record<string, string | undefined>;\n}\n\nexport interface SigningCredentials {\n cert?: string | Buffer;\n thumbprints?: string | string[];\n}\n\nexport interface ParseRequestOptions {\n signingCert?: string | Buffer;\n thumbprints?: string | string[];\n relayState?: string;\n signaturePath?: string;\n /**\n * Resolves the credentials used to validate the signature of the incoming\n * message from its issuer (and for logout requests, session index / NameID).\n */\n getCredentials?: (\n issuer: string | undefined,\n sessionIndices: string[] | undefined,\n nameId: string | undefined,\n ) => SigningCredentials | undefined | Promise<SigningCredentials | undefined>;\n}\n\ninterface ValidateOptions {\n signingCert?: string | Buffer;\n thumbprints?: string | string[];\n deflate?: boolean;\n relayState?: string;\n signaturePath?: string;\n}\n\n/** Generates a random 20 character hexadecimal identifier. */\nexport function generateUniqueID(): string {\n return randomBytes(10).toString('hex');\n}\n\n/**\n * Formats the given date in `xs:DateTime` format as per Core SAML \"1.3.3 Time Values\":\n * UTC, no time zone component, millisecond precision.\n */\nexport function formatXmlDateTime(date: Date): string {\n return date.toISOString();\n}\n\nexport function generateInstant(): string {\n return formatXmlDateTime(new Date());\n}\n\nexport function appendQueryString(initialUrl: string, query: Record<string, string>): string {\n const parsed = new URL(initialUrl);\n for (const [key, value] of Object.entries(query)) {\n parsed.searchParams.set(key, value);\n }\n return parsed.toString();\n}\n\nfunction isXmlBuffer(input: Buffer): boolean {\n return input[0] === 60; // '<'\n}\n\n/**\n * Decodes (base64, then optionally inflates) and parses a SAML request, and\n * validates its signature when a signing credential is available.\n *\n * @returns the parsed DOM, or `undefined` when there is no SAML message\n */\nexport async function parseSamlRequest(\n input: SamlHttpInput,\n samlRequest: string | undefined,\n type: ElementType,\n options: ParseRequestOptions = {},\n): Promise<XmlDocument | undefined> {\n if (!samlRequest) return undefined;\n\n const raw = Buffer.from(samlRequest, 'base64');\n let xmlString: string;\n if (isXmlBuffer(raw)) {\n // content is just encoded, not zipped\n xmlString = raw.toString();\n } else {\n xmlString = (await inflateRawAsync(raw)).toString();\n }\n\n let xml: XmlDocument;\n try {\n xml = parseXml(xmlString);\n } catch (e) {\n throw new Error(e instanceof Error ? e.message : String(e));\n }\n if (!xml || !xml.documentElement) {\n throw new Error('Invalid SAML Request');\n }\n\n const credentials = await configureSigningCredentials(xml, type, options);\n if (!credentials) return xml;\n\n validateSignature(input, type, xml, xmlString, {\n signingCert: credentials.cert,\n thumbprints: credentials.thumbprints,\n deflate: !!input.query.Signature,\n relayState: options.relayState,\n signaturePath: options.signaturePath,\n });\n return xml;\n}\n\n/**\n * Determines whether the message signature must be validated and with which\n * credentials. Returns `undefined` when no validation is needed.\n */\nasync function configureSigningCredentials(\n xml: XmlDocument,\n type: ElementType,\n options: ParseRequestOptions,\n): Promise<SigningCredentials | undefined> {\n if (options.signingCert || (options.thumbprints && options.thumbprints.length > 0)) {\n return { cert: options.signingCert, thumbprints: options.thumbprints };\n }\n if (!options.getCredentials) return undefined;\n\n const descriptor = ELEMENTS[type];\n const issuer = descriptor.ISSUER_PATH\n ? selectNodes(descriptor.ISSUER_PATH, xml)[0]?.textContent ?? undefined\n : undefined;\n\n let sessionIndices: string[] | undefined;\n if (descriptor.SESSION_INDEX_PATH) {\n const nodes = selectNodes(descriptor.SESSION_INDEX_PATH, xml);\n if (nodes.length > 0) sessionIndices = nodes.map((n) => n.textContent ?? '');\n }\n\n let nameId: string | undefined;\n if (descriptor.NAME_ID) {\n nameId = selectNodes(descriptor.NAME_ID, xml)[0]?.textContent ?? undefined;\n }\n\n if (!issuer && !(sessionIndices && sessionIndices.length) && !nameId) {\n return undefined;\n }\n\n let credentials: SigningCredentials | undefined;\n try {\n credentials = await options.getCredentials(issuer, sessionIndices, nameId);\n } catch {\n return undefined;\n }\n if (!credentials) return undefined;\n return credentials;\n}\n\n/**\n * Validates the signature of a SAML request/response received through the\n * HTTP-POST binding (embedded XML signature) or HTTP-Redirect binding\n * (detached `Signature` / `SigAlg` query parameters).\n *\n * @throws when the message is signed with an invalid signature, or unsigned\n * while a signing certificate is configured.\n */\nexport function validateSignature(\n input: SamlHttpInput,\n elementType: ElementType,\n xml: XmlDocument,\n xmlString: string,\n options: ValidateOptions,\n): void {\n const descriptor = ELEMENTS[elementType];\n const prop = descriptor.PROP;\n\n const isRequestSigned = !options.deflate\n ? selectNodes((options.signaturePath ?? descriptor.SIGNATURE_VALIDATION_PATH) as string, xml).length > 0\n : !!input.query.Signature;\n\n if (isRequestSigned) {\n if (input.body[prop] || !options.deflate) {\n // HTTP-POST or HTTP-Redirect without deflate encoding\n const errors = signers.validateXmlEmbeddedSignature(xml, xmlString, options);\n if (errors.length > 0) {\n throw new Error('Signature check errors: ' + errors.join('; '));\n }\n } else {\n // HTTP-Redirect with deflate encoding\n const signedContent: Record<string, string | undefined> = {\n [prop]: input.query[prop],\n RelayState: input.query.RelayState || options.relayState,\n SigAlg: input.query.SigAlg,\n };\n\n if (!signedContent.RelayState) {\n delete signedContent.RelayState;\n }\n if (!signedContent.SigAlg) {\n throw new Error('Signature Algorithm is missing');\n }\n if (!SIGNATURE_ALGORITHM_NAMES[signedContent.SigAlg]) {\n throw new Error(\n 'Invalid signature algorithm. Supported algorithms are http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 and http://www.w3.org/2000/09/xmldsig#rsa-sha1',\n );\n }\n if (!options.signingCert) {\n throw new Error('options.signingCert must be specified to validate a detached signature');\n }\n\n const valid = signers.isValidContentAndSignature(\n qs.stringify(signedContent as Record<string, string>),\n input.query.Signature as string,\n { signingCert: options.signingCert, signatureAlgorithm: signedContent.SigAlg },\n );\n if (!valid) {\n throw new Error(\n 'Signature check errors: The signature provided (' +\n input.query.Signature +\n ') does not match the one calculated',\n );\n }\n }\n } else if (options.signingCert) {\n throw new Error(prop + ' message MUST be signed when using an asynchronous binding (POST or Redirect)');\n }\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE): lib/samlp.js.\n// The Express middlewares were rewritten as framework-agnostic async functions.\nimport { createAssertion, type Saml20Options } from '../saml/saml20.js';\nimport type { DigestAlgorithm, SignatureAlgorithm } from '../saml/sign.js';\nimport { ELEMENTS, STATUS } from './constants.js';\nimport { removeHeaders } from './encoders.js';\nimport { passportProfileMapper, type ProfileMapper } from './profile-mapper.js';\nimport { signXml } from './signers.js';\nimport * as templates from './templates.js';\nimport {\n generateInstant,\n generateUniqueID,\n parseSamlRequest,\n type ParseRequestOptions,\n type SamlHttpInput,\n} from './utils.js';\nimport { selectNodes, type XmlDocument } from './xml.js';\n\nconst ISSUER_XPATH =\n \"//*[local-name(.)='Issuer' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/text()\";\n\n/** Error that carries the HTTP status the caller should answer with. */\nexport class SamlpError extends Error {\n status: number;\n context?: unknown;\n constructor(message: string, status = 500, context?: unknown) {\n super(message);\n this.name = 'SamlpError';\n this.status = status;\n this.context = context;\n }\n}\n\nexport interface ResponseOptions\n extends Omit<Saml20Options, 'attributes' | 'audiences' | 'nameIdentifier'> {\n issuer: string;\n cert: string | Buffer;\n key: string | Buffer;\n audience?: string;\n destination?: string;\n signResponse?: boolean;\n signAssertion?: boolean;\n signatureAlgorithm?: SignatureAlgorithm;\n digestAlgorithm?: DigestAlgorithm;\n signatureNamespacePrefix?: string;\n profileMapper?: ProfileMapper;\n nameIdentifierProbes?: string[];\n samlStatusCode?: string;\n samlStatusMessage?: string;\n}\n\nexport interface AuthOptions extends ResponseOptions, ParseRequestOptions {\n RelayState?: string;\n /** Resolves where the SAMLResponse is posted to. */\n getPostURL: (\n audience: string | undefined,\n authnRequest: XmlDocument | undefined,\n ) => string | undefined | Promise<string | undefined>;\n}\n\nexport interface PostForm {\n /** Form action (Assertion Consumer Service URL). */\n url: string;\n /** `SAMLResponse` or `SAMLRequest`. */\n field: 'SAMLResponse' | 'SAMLRequest';\n /** Base64 value of the SAML message. */\n value: string;\n relayState: string;\n}\n\nexport interface AuthResult extends PostForm {\n /** The XML of the (signed) SAML Response. */\n xml: string;\n /** Options that were effective for this response. */\n options: AuthOptions & { postUrl: string; audience?: string };\n}\n\nexport function buildSamlResponse(\n options: ResponseOptions & { samlAssertion?: string; inResponseTo?: string },\n): string {\n let response = templates.samlResponse({\n id: '_' + generateUniqueID(),\n instant: generateInstant(),\n destination: options.destination || options.audience,\n inResponseTo: options.inResponseTo,\n issuer: options.issuer,\n samlStatusCode: options.samlStatusCode ?? STATUS.SUCCESS,\n samlStatusMessage: options.samlStatusMessage,\n assertion: options.samlAssertion || '',\n });\n\n if (options.signResponse) {\n response = signXml(\n {\n key: options.key,\n cert: options.cert,\n signatureAlgorithm: options.signatureAlgorithm,\n digestAlgorithm: options.digestAlgorithm,\n reference: ELEMENTS.RESPONSE.SIGNATURE_LOCATION_PATH,\n },\n response,\n );\n }\n\n return response;\n}\n\nfunction nameIdentifierNotFoundErrorMessage(options: ResponseOptions): string {\n const probes = Array.isArray(options.nameIdentifierProbes)\n ? options.nameIdentifierProbes.join(', ')\n : '';\n return 'No attribute was found to generate the nameIdentifier. We tried with: ' + probes;\n}\n\n/** Maps `user` to claims, builds + signs (+ encrypts) the assertion and wraps it in a Response. */\nexport async function createSamlResponse(\n samlConfig: ResponseOptions,\n user: unknown,\n): Promise<string> {\n const options = {\n ...samlConfig,\n profileMapper: (samlConfig.profileMapper ?? passportProfileMapper) as ProfileMapper,\n signAssertion: typeof samlConfig.signAssertion !== 'boolean' || samlConfig.signAssertion,\n signatureNamespacePrefix:\n typeof samlConfig.signatureNamespacePrefix === 'string' ? samlConfig.signatureNamespacePrefix : '',\n };\n const mapper = options.profileMapper;\n const claims = mapper.getClaims(user);\n const ni = mapper.getNameIdentifier(user);\n\n if (!ni || !ni.nameIdentifier) {\n throw new SamlpError(nameIdentifierNotFoundErrorMessage(options), 500, { user });\n }\n\n const samlAssertion = await createAssertion({\n signatureAlgorithm: options.signatureAlgorithm,\n digestAlgorithm: options.digestAlgorithm,\n signAssertion: options.signAssertion,\n cert: options.cert,\n key: options.key,\n issuer: options.issuer,\n lifetimeInSeconds: options.lifetimeInSeconds || 3600,\n audiences: options.audience,\n attributes: claims || undefined,\n nameIdentifier: ni.nameIdentifier,\n nameIdentifierFormat: ni.nameIdentifierFormat || options.nameIdentifierFormat,\n nameIdentifierNameQualifier: ni.nameIdentifierNameQualifier,\n nameIdentifierSPNameQualifier: ni.nameIdentifierSPNameQualifier,\n nameIdentifierSPProvidedID: ni.nameIdentifierSPProvidedID,\n recipient: options.recipient,\n inResponseTo: options.inResponseTo,\n authnContextClassRef: options.authnContextClassRef,\n encryptionPublicKey: options.encryptionPublicKey,\n encryptionCert: options.encryptionCert,\n encryptionAlgorithm: options.encryptionAlgorithm,\n keyEncryptionAlgorithm: options.keyEncryptionAlgorithm,\n sessionIndex: options.sessionIndex,\n typedAttributes: options.typedAttributes,\n includeAttributeNameFormat: options.includeAttributeNameFormat,\n signatureNamespacePrefix: options.signatureNamespacePrefix,\n transformAssertion: options.transformAssertion,\n });\n\n return buildSamlResponse({\n ...options,\n samlAssertion,\n samlStatusCode: options.samlStatusCode || STATUS.SUCCESS,\n });\n}\n\n/**\n * Handles an SP-initiated or IdP-initiated SSO request for an authenticated user:\n * parses the optional AuthnRequest, then mints a SAML Response for `user`.\n */\nexport async function authenticate(\n input: SamlHttpInput,\n options: AuthOptions,\n user: unknown,\n): Promise<AuthResult> {\n const opts = {\n ...options,\n signatureAlgorithm: options.signatureAlgorithm || 'rsa-sha256',\n digestAlgorithm: options.digestAlgorithm || 'sha256',\n } as AuthOptions & { postUrl: string };\n\n if (typeof opts.getPostURL !== 'function') {\n throw new Error('getPostURL is required');\n }\n\n const samlRequest = input.query.SAMLRequest || input.body.SAMLRequest;\n const samlRequestDom = await parseSamlRequest(input, samlRequest, 'AUTHN_REQUEST', {\n ...opts,\n relayState: opts.RelayState || input.query.RelayState,\n });\n\n let audience = opts.audience;\n if (samlRequestDom) {\n if (!audience) {\n audience = selectNodes(ISSUER_XPATH, samlRequestDom)[0]?.textContent ?? undefined;\n }\n const id = samlRequestDom.documentElement?.getAttribute('ID');\n if (id) opts.inResponseTo = opts.inResponseTo || id;\n }\n\n const postUrl = await opts.getPostURL(audience, samlRequestDom);\n if (!postUrl) throw new SamlpError('Unable to determine the Assertion Consumer Service URL', 401);\n\n if (!user) throw new SamlpError('Unauthorized', 401);\n\n opts.audience = audience;\n opts.postUrl = postUrl;\n\n const xml = await createSamlResponse(opts, user);\n\n return {\n url: postUrl,\n field: 'SAMLResponse',\n value: Buffer.from(xml).toString('base64'),\n relayState: opts.RelayState || input.query.RelayState || input.body.RelayState || '',\n xml,\n options: opts,\n };\n}\n\nexport interface AuthnRequestData {\n id?: string;\n issuer?: string;\n subject?: string;\n destination?: string;\n assertionConsumerServiceURL?: string;\n forceAuthn?: string;\n requestedAuthnContext?: { authnContextClassRef: string };\n}\n\n/** Parses an incoming `AuthnRequest`. Returns `undefined` when the request carries none. */\nexport async function parseRequest(\n input: SamlHttpInput,\n options: ParseRequestOptions = {},\n): Promise<AuthnRequestData | undefined> {\n const samlRequest = input.query.SAMLRequest || input.body.SAMLRequest;\n if (!samlRequest) return undefined;\n\n const dom = await parseSamlRequest(input, samlRequest, 'AUTHN_REQUEST', options);\n if (!dom) return undefined;\n\n const data: AuthnRequestData = {};\n const root = dom.documentElement!;\n\n const issuer = selectNodes(ISSUER_XPATH, dom);\n if (issuer.length > 0) data.issuer = issuer[0].textContent ?? undefined;\n\n const subject = selectNodes(\n \"//*[local-name(.)='Subject' and namespace-uri(.)='urn:oasis:names:tc:SAML:2.0:assertion']/*[local-name(.)='NameID']\",\n dom,\n );\n if (subject.length > 0) data.subject = subject[0].textContent ?? undefined;\n\n const acs = root.getAttribute('AssertionConsumerServiceURL');\n if (acs) data.assertionConsumerServiceURL = acs;\n\n const destination = root.getAttribute('Destination');\n if (destination) data.destination = destination;\n\n const forceAuthn = root.getAttribute('ForceAuthn');\n if (forceAuthn) data.forceAuthn = forceAuthn;\n\n const id = root.getAttribute('ID');\n if (id) data.id = id;\n\n const classRefs = selectNodes(ELEMENTS.AUTHN_REQUEST.AUTHN_CONTEXT_CLASS_REF_PATH as string, dom);\n if (classRefs.length === 1) {\n data.requestedAuthnContext = { authnContextClassRef: classRefs[0].textContent ?? '' };\n }\n\n return data;\n}\n\nexport interface ErrorResponseOptions extends ResponseOptions {\n RelayState?: string;\n error?: { code?: string; description?: string };\n postUrl: string;\n inResponseTo?: string;\n}\n\n/** Builds a (signed) error Response to be posted back to the SP. */\nexport function createErrorResponse(options: ErrorResponseOptions): PostForm {\n const error = options.error ?? {};\n const xml = buildSamlResponse({\n ...options,\n samlStatusCode: error.code || STATUS.RESPONDER,\n samlStatusMessage: error.description,\n });\n return {\n url: options.postUrl,\n field: 'SAMLResponse',\n value: Buffer.from(xml).toString('base64'),\n relayState: options.RelayState ?? '',\n };\n}\n\nexport { removeHeaders };\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE).\n\nexport interface SessionParticipant {\n serviceProviderId?: string;\n sessionIndex?: string;\n nameId?: string;\n nameIdFormat?: string;\n serviceProviderLogoutURL?: string;\n /** Certificate used to validate signed messages from the participant. */\n cert?: string | Buffer;\n thumbprint?: string;\n}\n\nfunction matchingIndex(issuer?: string, sessionIndex?: string | string[], nameId?: string) {\n return (session: SessionParticipant) => {\n // If we had the issuer in session and it is provided, they should match\n if (session.serviceProviderId && issuer && session.serviceProviderId !== issuer) {\n return false;\n }\n\n const hasMatchingSessionIndex = Array.isArray(sessionIndex)\n ? sessionIndex.some((si) => si === session.sessionIndex)\n : sessionIndex === session.sessionIndex;\n\n // SessionIndex and NameID should match\n return hasMatchingSessionIndex && session.nameId === nameId;\n };\n}\n\n/** In-memory list of the Service Providers that take part in a user's session. */\nexport class SessionParticipants {\n private participants: SessionParticipant[];\n\n constructor(sessions: SessionParticipant[] = []) {\n this.participants = sessions;\n }\n\n /** Finds a participant by issuer (optional), session index and NameID. */\n get(\n issuer: string | undefined,\n sessionIndices: string | string[] | undefined,\n nameId: string | undefined,\n ): SessionParticipant | undefined {\n return this.participants.find(matchingIndex(issuer, sessionIndices, nameId));\n }\n\n hasElements(): boolean {\n return this.participants.length > 0;\n }\n\n /** Returns the first participant without removing it. */\n getFirst(): SessionParticipant | undefined {\n return this.participants[0];\n }\n\n /** Removes and returns the matching participant (if any). */\n remove(\n issuer: string | undefined,\n sessionIndex: string | undefined,\n nameId: string | undefined,\n ): SessionParticipant | null {\n if (this.participants.length === 0 || !issuer) return null;\n const index = this.participants.findIndex(matchingIndex(issuer, sessionIndex, nameId));\n if (index === -1) return null;\n return this.participants.splice(index, 1)[0];\n }\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE).\nimport { DOMParser, XMLSerializer } from '@xmldom/xmldom';\n\nconst whitespace = /^\\s+$/;\n\ntype XmlNode = ReturnType<DOMParser['parseFromString']>['firstChild'];\n\nfunction removeEmptyNodes(node: NonNullable<XmlNode>): void {\n for (let i = 0; i < node.childNodes.length; i++) {\n const current = node.childNodes[i];\n if (current.nodeType === 3 && whitespace.test(current.nodeValue ?? '')) {\n node.removeChild(current);\n i--;\n } else if (current.nodeType === 1) {\n removeEmptyNodes(current);\n }\n }\n}\n\n/** Removes whitespace-only text nodes and re-serializes the document. */\nexport function trimXml(xml: string): string {\n const dom = new DOMParser().parseFromString(xml, 'text/xml');\n removeEmptyNodes(dom as NonNullable<XmlNode>);\n return new XMLSerializer().serializeToString(dom);\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE): lib/logout.js.\n// The Express middleware was rewritten as a framework-agnostic async function that\n// returns what the HTTP layer must do (POST form, redirect, or \"done\").\nimport qs from 'node:querystring';\nimport { deflateRaw, inflateRaw } from 'node:zlib';\nimport { promisify } from 'node:util';\nimport type { DigestAlgorithm, SignatureAlgorithm } from '../saml/sign.js';\nimport { SamlpError, type PostForm } from './auth.js';\nimport { BINDINGS, ELEMENTS, STATUS, type ElementType } from './constants.js';\nimport { SessionParticipants } from './session-participants.js';\nimport * as signers from './signers.js';\nimport * as templates from './templates.js';\nimport { trimXml } from './trim-xml.js';\nimport {\n appendQueryString,\n generateInstant,\n generateUniqueID,\n parseSamlRequest,\n validateSignature,\n type SamlHttpInput,\n} from './utils.js';\nimport { parseXml, selectNodes, type XmlDocument } from './xml.js';\n\nconst deflateRawAsync = promisify(deflateRaw);\nconst inflateRawAsync = promisify(inflateRaw);\n\nexport type LogoutState = Record<string, any>;\n\n/** Persists the state of an in-flight logout transaction (keyed by an opaque handle). */\nexport interface LogoutStore {\n /** Stores the state and returns its handle (used as `RelayState`). */\n save(state: LogoutState): string;\n load(handle: string | undefined, options?: { destroy?: boolean }): LogoutState | undefined;\n update(handle: string, state: LogoutState): void;\n}\n\nexport interface LogoutOptions {\n issuer: string;\n cert: string | Buffer;\n key: string | Buffer;\n digestAlgorithm?: DigestAlgorithm;\n signatureAlgorithm?: SignatureAlgorithm;\n sessionParticipants: SessionParticipants;\n store: LogoutStore;\n /** Called once all participants are logged out. */\n clearIdPSession?: () => void | Promise<void>;\n /** Fallback SP logout URL. */\n destination?: string;\n protocolBinding?: string;\n /** Deflate + detached signature (HTTP-Redirect binding). */\n deflate?: boolean;\n samlStatusMessage?: string;\n}\n\nexport type LogoutResult =\n | ({ type: 'post' } & PostForm)\n | { type: 'redirect'; url: string }\n /** IdP-initiated flow finished: reply with an empty 200. */\n | { type: 'done' };\n\ninterface SendContext {\n destination?: string;\n relayState?: string;\n reference?: string;\n}\n\ninterface LogoutRequestData {\n id?: string;\n issuer?: string;\n sessionIndices?: string[];\n nameId?: string;\n nameIdFormat?: string;\n destination?: string;\n signature?: string;\n}\n\n/** Parses an SP-initiated `LogoutRequest`. */\nasync function parseIncomingLogoutRequest(\n input: SamlHttpInput,\n samlRequest: string,\n options: Parameters<typeof parseSamlRequest>[3],\n): Promise<LogoutRequestData> {\n const type: ElementType = 'LOGOUT_REQUEST';\n const dom = (await parseSamlRequest(input, samlRequest, type, options)) as XmlDocument;\n const descriptor = ELEMENTS[type];\n const data: LogoutRequestData = {};\n\n const issuer = selectNodes(descriptor.ISSUER_PATH as string, dom);\n if (issuer.length > 0) data.issuer = issuer[0].textContent ?? undefined;\n\n const sessionIndexNodes = selectNodes(\"//*[local-name(.)='SessionIndex']/text()\", dom);\n if (sessionIndexNodes.length > 0) data.sessionIndices = sessionIndexNodes.map((n) => n.textContent ?? '');\n\n const nameId = selectNodes(\"//*[local-name(.)='NameID']\", dom);\n if (nameId.length > 0) {\n data.nameId = nameId[0].textContent ?? undefined;\n data.nameIdFormat = nameId[0].getAttribute('Format') ?? undefined;\n }\n\n const root = dom.documentElement!;\n const destination = root.getAttribute('Destination');\n if (destination) data.destination = destination;\n const id = root.getAttribute('ID');\n if (id) data.id = id;\n\n const signature = selectNodes(descriptor.SIGNATURE_VALIDATION_PATH as string, dom);\n if (signature.length > 0) data.signature = signature[0].textContent ?? undefined;\n\n return data;\n}\n\nasync function prepareAndSendToken(\n elementType: 'LOGOUT_REQUEST' | 'LOGOUT_RESPONSE',\n token: string,\n options: LogoutOptions,\n ctx: SendContext,\n): Promise<LogoutResult> {\n const type = ELEMENTS[elementType].PROP;\n const params: Record<string, string> = { [type]: '', RelayState: ctx.relayState || '' };\n\n const send = (): LogoutResult => {\n if (options.protocolBinding !== BINDINGS.HTTP_REDIRECT) {\n // HTTP-POST\n return {\n type: 'post',\n url: ctx.destination as string,\n field: type,\n value: params[type],\n relayState: params.RelayState ?? '',\n };\n }\n // HTTP-Redirect\n try {\n return { type: 'redirect', url: appendQueryString(ctx.destination as string, params) };\n } catch {\n throw new Error('The logout URL may be missing or misconfigured');\n }\n };\n\n // canonical request\n const canonical = trimXml(token);\n const signingOptions: signers.SigningOptions = {\n key: options.key,\n cert: options.cert,\n signatureAlgorithm: options.signatureAlgorithm,\n digestAlgorithm: options.digestAlgorithm,\n reference: ctx.reference,\n };\n\n if (options.protocolBinding !== BINDINGS.HTTP_REDIRECT || !options.deflate) {\n // HTTP-POST or HTTP-Redirect without deflate encoding\n params[type] = Buffer.from(signers.signXml(signingOptions, canonical)).toString('base64');\n return send();\n }\n\n // Default: HTTP-Redirect with deflate encoding\n // (http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf - section 3.4.4.1)\n params[type] = (await deflateRawAsync(Buffer.from(canonical))).toString('base64');\n\n // The signature covers SAMLRequest|SAMLResponse, RelayState (if present) and SigAlg\n if (params.RelayState === '') delete params.RelayState;\n\n params.SigAlg = signers.getSigAlg(options);\n params.Signature = signers.sign(options, qs.stringify(params));\n return send();\n}\n\nasync function prepareAndSendLogoutRequest(\n options: LogoutOptions,\n transactionId: string,\n): Promise<LogoutResult> {\n const { sessionParticipants } = options;\n\n // Finished if there are no more session participants - finish logout\n if (!sessionParticipants.hasElements()) {\n return finalize(options, transactionId);\n }\n\n const participant = sessionParticipants.getFirst()!;\n\n // Store the sessionIndex in session so when the logout response comes back we can validate it\n const relayState = options.store.save({\n transactionId,\n sessionIndex: participant.sessionIndex,\n issuer: participant.serviceProviderId,\n nameId: participant.nameId,\n });\n\n const logoutRequest = templates.logoutRequest({\n ID: generateUniqueID(),\n IssueInstant: generateInstant(),\n Issuer: options.issuer, // IdP identifier\n NameID: { value: participant.nameId, Format: participant.nameIdFormat },\n SessionIndex: participant.sessionIndex,\n Destination: participant.serviceProviderLogoutURL,\n });\n\n return prepareAndSendToken('LOGOUT_REQUEST', logoutRequest, options, {\n destination: participant.serviceProviderLogoutURL,\n relayState,\n });\n}\n\nasync function finalize(options: LogoutOptions, transactionId: string): Promise<LogoutResult> {\n const transaction = options.store.load(transactionId, { destroy: true });\n let isPartialLogout = !!transaction && transaction.global_status === 'failed';\n\n try {\n await options.clearIdPSession?.();\n } catch {\n // If there was an issue cleaning the session, reply with partial logout\n isPartialLogout = true;\n }\n\n // No data - It was an IdP initiated flow\n if (!transaction || !transaction.parsedRequest) {\n return { type: 'done' };\n }\n\n const data = transaction.parsedRequest;\n const destination: string | undefined = data.serviceProviderLogoutURL || options.destination;\n\n // IdP is the Issuer for this LogoutResponse\n const logoutResponse = templates.logoutResponse({\n id: '_' + generateUniqueID(),\n instant: generateInstant(),\n inResponseTo: data.id,\n destination,\n issuer: options.issuer,\n samlStatusCode: isPartialLogout ? STATUS.PARTIAL_LOGOUT : STATUS.SUCCESS,\n samlStatusMessage: options.samlStatusMessage,\n });\n\n return prepareAndSendToken('LOGOUT_RESPONSE', logoutResponse, options, {\n destination,\n // We stored the relay state of the initial request\n relayState: transaction.relayState,\n reference: ELEMENTS.LOGOUT_RESPONSE.SIGNATURE_LOCATION_PATH,\n });\n}\n\ninterface ParsedLogoutResponse {\n status?: string;\n subCode?: string;\n message?: string;\n detail?: string;\n issuer?: string;\n}\n\nasync function validateSamlResponse(\n input: SamlHttpInput,\n options: LogoutOptions,\n cert: string | Buffer | undefined,\n): Promise<ParsedLogoutResponse> {\n const samlResponse = (input.query.SAMLResponse || input.body.SAMLResponse) as string;\n const raw = Buffer.from(samlResponse, 'base64');\n const xmlString =\n input.body.SAMLResponse || !options.deflate\n ? raw.toString() // HTTP-POST or HTTP-Redirect without deflate encoding\n : (await inflateRawAsync(raw)).toString(); // Default: HTTP-Redirect with deflate encoding\n\n const xml = parseXml(xmlString);\n const PROTOCOL_NS = 'urn:oasis:names:tc:SAML:2.0:protocol';\n const parsed: ParsedLogoutResponse = {};\n\n const statusCodes = xml.getElementsByTagNameNS(PROTOCOL_NS, 'StatusCode');\n if (statusCodes[0]) {\n parsed.status = statusCodes[0].getAttribute('Value') ?? undefined;\n if (statusCodes[1]) parsed.subCode = statusCodes[1].getAttribute('Value') ?? undefined;\n }\n const message = xml.getElementsByTagNameNS(PROTOCOL_NS, 'StatusMessage')[0];\n if (message) parsed.message = message.textContent ?? undefined;\n const detail = xml.getElementsByTagNameNS(PROTOCOL_NS, 'StatusDetail')[0];\n if (detail) parsed.detail = detail.textContent ?? undefined;\n const issuer = xml.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'Issuer')[0];\n if (issuer) parsed.issuer = issuer.textContent ?? undefined;\n\n // If there's no certificate to check the signature, let it pass\n if (cert) {\n validateSignature(input, 'LOGOUT_RESPONSE', xml, xmlString, {\n signingCert: cert,\n deflate: options.deflate,\n });\n }\n return parsed;\n}\n\n/**\n * Handles the three legs of the SAML Single Logout profile:\n * - SP-initiated: an incoming `SAMLRequest` (LogoutRequest)\n * - in-flight: an incoming `SAMLResponse` (LogoutResponse) from a participant\n * - IdP-initiated: neither, starts the logout of all session participants\n */\nexport async function logout(input: SamlHttpInput, options: LogoutOptions): Promise<LogoutResult> {\n const relayStateParam = input.query.RelayState || input.body.RelayState;\n\n // SP Initiated flow.\n const samlRequest = input.query.SAMLRequest || input.body.SAMLRequest;\n if (samlRequest) {\n const requestData = await parseIncomingLogoutRequest(input, samlRequest, {\n getCredentials: (issuer, sessionIndices, nameId) => {\n const session = options.sessionParticipants.get(issuer, sessionIndices, nameId);\n if (!session) throw new Error('Invalid Session Participant');\n if (!session.cert) return undefined;\n return { cert: session.cert, thumbprints: session.thumbprint };\n },\n });\n\n if (!requestData.issuer) {\n throw new SamlpError('SAML Request with no issuer. Issuer is a mandatory element.', 400);\n }\n\n const session = options.sessionParticipants.get(\n requestData.issuer,\n requestData.sessionIndices,\n requestData.nameId,\n );\n if (!session && !options.destination) {\n throw new SamlpError('Invalid Session Participant', 400);\n }\n\n // We should store who requested the logout, so we can reply back with LogoutResponse\n const transactionId = options.store.save({\n parsedRequest: {\n id: requestData.id,\n serviceProviderLogoutURL: session?.serviceProviderLogoutURL || options.destination,\n },\n relayState: relayStateParam,\n });\n\n // We remove the session from the LogoutRequest originator. This session is already saved\n // in the store: we must not send a LogoutRequest to it, only a LogoutResponse once there\n // are no other session participants left.\n options.sessionParticipants.remove(session?.serviceProviderId, session?.sessionIndex, session?.nameId);\n\n return prepareAndSendLogoutRequest(options, transactionId);\n }\n\n // Logout flow in progress, incoming SAMLResponse from SP (SP or IdP initiated).\n if (input.query.SAMLResponse || input.body.SAMLResponse) {\n // Verify that the state sent to the SP matches the one returned\n const state = options.store.load(relayStateParam, { destroy: true });\n if (!state) throw new SamlpError('Invalid RelayState', 400);\n\n const participant = options.sessionParticipants.get(state.issuer, [state.sessionIndex], state.nameId);\n if (!participant) throw new SamlpError('Invalid Session Participant', 400);\n\n const logoutResponse = await validateSamlResponse(input, options, participant.cert);\n const transactionId: string = state.transactionId;\n\n if (logoutResponse.status !== STATUS.SUCCESS) {\n // Mark global status as partial logout if a logout does not succeed\n const globalState = options.store.load(transactionId) ?? {};\n globalState.global_status = 'failed';\n options.store.update(transactionId, globalState);\n }\n\n // The participant replied: remove it and continue with the next one (if any)\n options.sessionParticipants.remove(state.issuer, state.sessionIndex, state.nameId);\n return prepareAndSendLogoutRequest(options, transactionId);\n }\n\n // IdP initiated - Start flow. When complete the result is \"done\" (HTTP 200).\n const transactionId = options.store.save({});\n return prepareAndSendLogoutRequest(options, transactionId);\n}\n\nexport interface LogoutErrorOptions {\n issuer: string;\n cert: string | Buffer;\n key: string | Buffer;\n destination?: string;\n inResponseTo?: string;\n relayState?: string;\n error?: { code?: string; description?: string };\n digestAlgorithm?: DigestAlgorithm;\n signatureAlgorithm?: SignatureAlgorithm;\n protocolBinding?: string;\n deflate?: boolean;\n}\n\n/** Builds a signed `LogoutResponse` carrying an error status. */\nexport async function createLogoutError(\n input: SamlHttpInput,\n options: LogoutErrorOptions,\n): Promise<LogoutResult> {\n if (!options.destination) throw new Error('Destination not specified');\n const error = options.error ?? {};\n\n const logoutResponse = templates.logoutResponse({\n id: '_' + generateUniqueID(),\n instant: generateInstant(),\n inResponseTo: options.inResponseTo,\n destination: options.destination,\n issuer: options.issuer,\n samlStatusCode: error.code || STATUS.RESPONDER,\n samlStatusMessage: error.description,\n });\n\n return prepareAndSendToken(\n 'LOGOUT_RESPONSE',\n logoutResponse,\n {\n ...options,\n sessionParticipants: new SessionParticipants(),\n store: undefined as unknown as LogoutStore,\n },\n {\n destination: options.destination,\n relayState: options.relayState || input.query.RelayState || input.body.RelayState,\n reference: ELEMENTS.LOGOUT_RESPONSE.SIGNATURE_LOCATION_PATH,\n },\n );\n}\n","// Ported from mcguinness/node-samlp (MIT, see ./LICENSE): lib/metadata.js.\nimport { removeHeaders } from './encoders.js';\nimport { passportProfileMapper, type ProfileMapper } from './profile-mapper.js';\nimport * as templates from './templates.js';\n\nexport interface MetadataOptions {\n issuer: string;\n cert: string | Buffer;\n /** Defaults to the Passport profile mapper. */\n profileMapper?: ProfileMapper;\n /** Path of the HTTP-Redirect SingleSignOnService (appended to the origin). */\n redirectEndpointPath?: string;\n /** Path of the HTTP-POST SingleSignOnService (appended to the origin). */\n postEndpointPath?: string;\n /** Paths of the SingleLogoutService endpoints. */\n logoutEndpointPaths?: { redirect?: string; post?: string };\n}\n\n/**\n * Builds the IdP SAML metadata document.\n *\n * @param origin scheme + host the IdP is reachable at, e.g. `https://idp.example.com`\n */\nexport function buildMetadata(options: MetadataOptions, origin: string): string {\n if (!options.issuer) throw new Error('options.issuer is required');\n if (!options.cert) throw new Error('options.cert is required');\n\n const pem = removeHeaders(options.cert);\n if (!pem) throw new Error('options.cert must be a PEM certificate');\n\n const logoutPaths = options.logoutEndpointPaths ?? { redirect: '/logout' };\n const logoutEndpoints: { redirect?: string; post?: string } = {};\n if (logoutPaths.redirect) logoutEndpoints.redirect = origin + logoutPaths.redirect;\n if (logoutPaths.post) logoutEndpoints.post = origin + logoutPaths.post;\n\n return templates\n .metadata({\n claimTypes: (options.profileMapper ?? passportProfileMapper).metadata,\n pem,\n issuer: options.issuer,\n redirectEndpoint: origin + (options.redirectEndpointPath ?? ''),\n postEndpoint: origin + (options.postEndpointPath ?? ''),\n logoutEndpoints,\n })\n .replace(/\\n(?:\\s*\\n)+/g, '\\n');\n}\n","import { raw } from 'hono/html';\nimport type { FC } from 'hono/jsx';\n\nexport interface AutoPostFormProps {\n /** Form action (ACS / SLO URL of the Service Provider). */\n url: string;\n /** Name of the hidden field carrying the SAML message. */\n field: string;\n /** Base64 encoded SAML message. */\n value: string;\n relayState?: string;\n}\n\n/** Page that immediately POSTs a SAML message to the Service Provider (HTTP-POST binding). */\nexport const AutoPostForm: FC<AutoPostFormProps> = ({ url, field, value, relayState }) => (\n <>\n {raw('<!DOCTYPE html>')}\n <html>\n <head>\n <title>Keep Calm and Single Sign-On!</title>\n </head>\n <body>\n <form method=\"post\" name=\"hiddenform\" action={url}>\n <input type=\"hidden\" name={field} value={value} />\n <input type=\"hidden\" name=\"RelayState\" value={relayState ?? ''} />\n <noscript>\n <p>JavaScript is disabled. Click Submit to continue.</p>\n <input type=\"submit\" value=\"Submit\" />\n </noscript>\n </form>\n <script>{raw('window.setTimeout(function(){document.forms[0].submit();}, 0);')}</script>\n </body>\n </html>\n </>\n);\n","import { raw } from 'hono/html';\nimport type { FC, PropsWithChildren } from 'hono/jsx';\nimport { IDP_PATHS } from '../state.js';\n\nconst STYLES = `\n :root { color-scheme: light; }\n * { box-sizing: border-box; }\n body { margin: 0; font-family: \"Lato\", \"Helvetica Neue\", Helvetica, Arial, sans-serif; font-size: 14px; color: #2c3e50; background: #f5f7f9; }\n nav { background: #2d3f50; color: #ecf0f1; display: flex; align-items: center; justify-content: space-between; padding: 12px 24px; }\n nav a { color: #ecf0f1; text-decoration: none; margin-left: 20px; }\n nav a.brand { margin-left: 0; font-size: 16px; font-weight: 600; }\n main { max-width: 820px; margin: 24px auto; padding: 0 16px; }\n .panel { background: #fff; border: 1px solid #d9e0e6; border-radius: 4px; margin-bottom: 20px; }\n .panel > h3, .panel > summary { margin: 0; padding: 10px 16px; font-size: 15px; font-weight: 600; background: #eef2f5; border-bottom: 1px solid #d9e0e6; cursor: default; }\n .panel.primary > h3, .panel.primary > summary { background: #2d3f50; color: #ecf0f1; }\n .panel > summary { cursor: pointer; }\n .panel-body { padding: 16px; }\n .row { display: flex; align-items: flex-start; margin-bottom: 12px; gap: 12px; }\n .row > label, .row > .label { flex: 0 0 200px; padding-top: 7px; font-weight: 600; }\n .row > .field { flex: 1; min-width: 0; }\n .static { padding-top: 7px; overflow-wrap: anywhere; }\n input, select { width: 100%; padding: 6px 10px; border: 1px solid #c3ccd4; border-radius: 4px; font: inherit; }\n .hint { color: #7b8a97; font-size: 12px; margin: 4px 0 0; }\n .actions { display: flex; justify-content: flex-end; gap: 10px; }\n .btn { display: inline-block; padding: 7px 16px; border: 1px solid #c3ccd4; border-radius: 4px; background: #fff; color: #2c3e50; font: inherit; text-decoration: none; cursor: pointer; }\n .btn.primary { background: #2d3f50; border-color: #2d3f50; color: #fff; }\n pre { white-space: pre-wrap; word-break: break-word; }\n`;\n\nexport const Layout: FC<PropsWithChildren<{ issuer?: string }>> = ({ issuer, children }) => (\n <>\n {raw('<!DOCTYPE html>')}\n <html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>Simple Identity Provider</title>\n <style>{raw(STYLES)}</style>\n </head>\n <body>\n <nav>\n <a class=\"brand\" href={IDP_PATHS.METADATA}>\n Simple SAML Identity Provider{issuer ? ` (${issuer})` : ''}\n </a>\n <div>\n <a href={IDP_PATHS.METADATA}>IdP Metadata</a>\n <a href={IDP_PATHS.SIGN_OUT}>New Session</a>\n <a href={IDP_PATHS.SETTINGS}>Settings</a>\n </div>\n </nav>\n <main>{children}</main>\n </body>\n </html>\n </>\n);\n","import type { FC } from 'hono/jsx';\nimport { Layout } from './Layout.js';\n\nexport interface ErrorPageProps {\n message: string;\n status?: number;\n stack?: string;\n issuer?: string;\n}\n\nexport const ErrorPage: FC<ErrorPageProps> = ({ message, status, stack, issuer }) => (\n <Layout issuer={issuer}>\n <div id=\"container\">\n <h1>{message}</h1>\n {status ? <h2>{status}</h2> : null}\n {stack ? <pre>{stack}</pre> : null}\n <div class=\"actions\">\n <form action=\"/\" method=\"get\">\n <button id=\"btn-return\" class=\"btn primary\" type=\"submit\">\n Restart\n </button>\n </form>\n </div>\n </div>\n </Layout>\n);\n","import type { FC, PropsWithChildren } from 'hono/jsx';\nimport type { IdpSettings } from '../state.js';\nimport { Layout } from './Layout.js';\n\nconst AUTHN_CONTEXT_CLASSES = [\n 'InternetProtocol',\n 'InternetProtocolPassword',\n 'Kerberos',\n 'MobileOneFactorUnregistered',\n 'MobileTwoFactorUnregistered',\n 'MobileOneFactorContract',\n 'MobileTwoFactorContract',\n 'Password',\n 'PasswordProtectedTransport',\n 'PreviousSession',\n 'X509',\n 'PGP',\n 'SPKI',\n 'XMLDSig',\n 'Smartcard',\n 'SmartcardPKI',\n 'SoftwarePKI',\n 'Telephony',\n 'NomadTelephony',\n 'PersonalTelephony',\n 'AuthenticatedTelephony',\n 'SecureRemotePassword',\n 'TLSClient',\n 'TimeSyncToken',\n 'unspecified',\n].map((name) => ({ value: `urn:oasis:names:tc:SAML:2.0:ac:classes:${name}`, label: name }));\n\nconst Row: FC<PropsWithChildren<{ id: string; label: string }>> = ({ id, label, children }) => (\n <div class=\"row\">\n <label for={id}>{label}</label>\n <div class=\"field\">{children}</div>\n </div>\n);\n\nconst Select: FC<{\n id: string;\n name?: string;\n selected: unknown;\n options: Array<{ value: string; label: string }>;\n}> = ({ id, name, selected, options }) => (\n <select id={id} name={name ?? id}>\n {options.map((o) => (\n <option value={o.value} selected={o.value === String(selected)}>\n {o.label}\n </option>\n ))}\n </select>\n);\n\nconst YES_NO = [\n { value: 'true', label: 'Yes' },\n { value: 'false', label: 'No' },\n];\n\nexport const SettingsPage: FC<{ idp: IdpSettings }> = ({ idp }) => (\n <Layout issuer={idp.issuer}>\n <form id=\"settings\" method=\"post\">\n <div class=\"panel\">\n <h3>Identity Provider Settings</h3>\n <div class=\"panel-body\">\n <Row id=\"issuer\" label=\"Issuer\">\n <input id=\"issuer\" name=\"issuer\" type=\"text\" required value={idp.issuer} />\n </Row>\n <Row id=\"signatureAlgorithm\" label=\"Signature Algorithm\">\n <Select\n id=\"signatureAlgorithm\"\n selected={idp.signatureAlgorithm}\n options={[\n { value: 'rsa-sha1', label: 'RSA-SHA1' },\n { value: 'rsa-sha256', label: 'RSA-SHA256' },\n ]}\n />\n </Row>\n <Row id=\"digestAlgorithm\" label=\"Digest Algorithm\">\n <Select\n id=\"digestAlgorithm\"\n selected={idp.digestAlgorithm}\n options={[\n { value: 'sha1', label: 'SHA1' },\n { value: 'sha256', label: 'SHA256' },\n ]}\n />\n </Row>\n <Row id=\"signResponse\" label=\"Sign Response\">\n <Select id=\"signResponse\" selected={idp.signResponse} options={YES_NO} />\n </Row>\n <Row id=\"lifetimeInSeconds\" label=\"Assertion Lifetime Seconds\">\n <input id=\"lifetimeInSeconds\" name=\"lifetimeInSeconds\" type=\"number\" required value={idp.lifetimeInSeconds} />\n </Row>\n <Row id=\"authnContextClassRef\" label=\"Authentication Context Class\">\n <Select\n id=\"authnContextClassRef\"\n selected={idp.authnContextClassRef}\n options={AUTHN_CONTEXT_CLASSES}\n />\n </Row>\n <Row id=\"allowRequestAcsUrl\" label=\"Allow Dynamic Assertion Consumer Service URL\">\n <Select id=\"allowRequestAcsUrl\" selected={idp.allowRequestAcsUrl} options={YES_NO} />\n </Row>\n </div>\n </div>\n\n <div class=\"panel\">\n <h3>Service Provider Settings</h3>\n <div class=\"panel-body\">\n <Row id=\"serviceProviderId\" label=\"Issuer URI\">\n <input id=\"serviceProviderId\" name=\"serviceProviderId\" type=\"text\" required value={idp.serviceProviderId} />\n </Row>\n <Row id=\"sloUrl\" label=\"Single Logout URL\">\n <input id=\"sloUrl\" name=\"sloUrl\" type=\"url\" value={idp.sloUrl ?? ''} />\n </Row>\n <Row id=\"acsUrl\" label=\"Assertion Consumer Service URL\">\n <input id=\"acsUrl\" name=\"acsUrl\" type=\"url\" required value={idp.acsUrl} />\n </Row>\n <Row id=\"destination\" label=\"Destination\">\n <input id=\"destination\" name=\"destination\" type=\"url\" required value={idp.destination} />\n </Row>\n <Row id=\"recipient\" label=\"Recipient\">\n <input id=\"recipient\" name=\"recipient\" type=\"url\" required value={idp.recipient} />\n </Row>\n <Row id=\"audience\" label=\"Audience\">\n <input id=\"audience\" name=\"audience\" type=\"text\" required value={idp.audience} />\n </Row>\n <Row id=\"relayState\" label=\"Default Relay State\">\n <input id=\"relayState\" name=\"RelayState\" type=\"text\" value={idp.RelayState ?? ''} />\n </Row>\n </div>\n </div>\n\n <div class=\"actions\">\n <a href=\"/\" class=\"btn\">\n Cancel\n </a>\n <button type=\"submit\" class=\"btn primary\">\n Save\n </button>\n </div>\n </form>\n </Layout>\n);\n","import type { FC } from 'hono/jsx';\nimport type { UserProfile } from '../default-config.js';\nimport { IDP_PATHS, type IdpSettings } from '../state.js';\nimport type { ClaimMetadata } from '../vendor/samlp/index.js';\nimport { Layout } from './Layout.js';\n\nexport interface AuthnRequestInfo {\n relayState?: string;\n id?: string;\n issuer?: string;\n destination?: string;\n acsUrl?: string;\n forceAuthn?: boolean;\n}\n\nexport interface UserPageProps {\n user: UserProfile;\n sessionIndex?: string;\n metadata: ClaimMetadata[];\n authnRequest?: AuthnRequestInfo;\n idp: IdpSettings;\n}\n\nconst NAME_ID_FORMATS = [\n 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',\n 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',\n 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',\n 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',\n 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName',\n 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName',\n 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos',\n];\n\nconst StaticRow: FC<{ id: string; label: string; value?: string | boolean }> = ({ id, label, value }) => (\n <div class=\"row\">\n <span class=\"label\">{label}</span>\n <div class=\"field\">\n <p class=\"static\" id={id}>\n {String(value)}\n </p>\n </div>\n </div>\n);\n\nconst AuthnRequestPanel: FC<{ authnRequest: AuthnRequestInfo }> = ({ authnRequest }) => (\n <details class=\"panel primary\" id=\"authn-request\">\n <summary>SAML Authentication Request</summary>\n <div class=\"panel-body\">\n <StaticRow id=\"requestId\" label=\"Request ID\" value={authnRequest.id} />\n <StaticRow id=\"issuer\" label=\"Issuer\" value={authnRequest.issuer} />\n {authnRequest.destination ? (\n <StaticRow id=\"destination\" label=\"Destination\" value={authnRequest.destination} />\n ) : null}\n {authnRequest.acsUrl ? <StaticRow id=\"acsUrl\" label=\"ACS URL\" value={authnRequest.acsUrl} /> : null}\n {authnRequest.forceAuthn ? (\n <StaticRow id=\"forceAuthn\" label=\"ForceAuthn\" value={authnRequest.forceAuthn} />\n ) : null}\n {authnRequest.relayState ? (\n <StaticRow id=\"relayState\" label=\"Relay State\" value={authnRequest.relayState} />\n ) : null}\n </div>\n </details>\n);\n\nconst AttributeField: FC<{ entry: ClaimMetadata; value?: string }> = ({ entry, value }) => (\n <div class=\"row\">\n <label for={entry.id}>{entry.displayName}</label>\n <div class=\"field\">\n {entry.options ? (\n <select id={entry.id} name={entry.id}>\n {entry.options.map((option) => (\n <option value={option} selected={option === value}>\n {option}\n </option>\n ))}\n </select>\n ) : (\n <input id={entry.id} name={entry.id} type=\"text\" placeholder=\"Attribute value\" value={value ?? ''} />\n )}\n {entry.multiValue ? <p class=\"hint\">Multiple values are separated by commas</p> : null}\n </div>\n </div>\n);\n\nexport const UserPage: FC<UserPageProps> = ({ user, sessionIndex, metadata, authnRequest, idp }) => (\n <Layout issuer={idp.issuer}>\n {authnRequest?.id ? <AuthnRequestPanel authnRequest={authnRequest} /> : null}\n\n <form action={IDP_PATHS.SIGN_IN} method=\"post\">\n <div class=\"panel primary\">\n <h3>SAML Assertion</h3>\n <div class=\"panel-body\">\n <div class=\"row\">\n <label for=\"userName\">Subject NameID</label>\n <div class=\"field\">\n <input id=\"userName\" name=\"userName\" placeholder=\"user@example.com\" required value={user.userName} />\n </div>\n </div>\n <details>\n <summary>More Options</summary>\n <div class=\"row\">\n <label for=\"nameIdFormat\">Name ID Format</label>\n <div class=\"field\">\n <select id=\"nameIdFormat\" name=\"nameIdFormat\">\n {NAME_ID_FORMATS.map((format) => (\n <option value={format} selected={format === user.nameIdFormat}>\n {format}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div class=\"row\">\n <label for=\"nameIdNameQualifier\">Name Qualifier</label>\n <div class=\"field\">\n <input id=\"nameIdNameQualifier\" name=\"nameIdNameQualifier\" type=\"text\" value={user.nameIdNameQualifier ?? ''} />\n </div>\n </div>\n <div class=\"row\">\n <label for=\"nameIdSPNameQualifier\">Service Provider Name Qualifier</label>\n <div class=\"field\">\n <input id=\"nameIdSPNameQualifier\" name=\"nameIdSPNameQualifier\" type=\"text\" value={user.nameIdSPNameQualifier ?? ''} />\n </div>\n </div>\n <div class=\"row\">\n <label for=\"nameIdSPProvidedID\">Service Provider Provided ID</label>\n <div class=\"field\">\n <input id=\"nameIdSPProvidedID\" name=\"nameIdSPProvidedID\" type=\"text\" value={user.nameIdSPProvidedID ?? ''} />\n </div>\n </div>\n </details>\n </div>\n </div>\n\n <details class=\"panel\">\n <summary>Authentication Statement</summary>\n <div class=\"panel-body\">\n <StaticRow id=\"sessionIndex\" label=\"Session Index\" value={sessionIndex} />\n <StaticRow id=\"authnContextClassRef\" label=\"Authentication Context Class\" value={idp.authnContextClassRef} />\n </div>\n </details>\n\n <div class=\"panel\">\n <h3>Attribute Statements</h3>\n <div class=\"panel-body\">\n {metadata.map((entry) => (\n <AttributeField entry={entry} value={user[entry.id]} />\n ))}\n </div>\n </div>\n\n {authnRequest?.id ? (\n <input\n name=\"_authnRequest\"\n type=\"hidden\"\n value={Buffer.from(JSON.stringify(authnRequest)).toString('base64')}\n />\n ) : null}\n\n <div class=\"actions\">\n <a id=\"btn-sign-out\" class=\"btn\" href={IDP_PATHS.SIGN_OUT}>\n Sign out\n </a>\n <button id=\"btn-sign-in\" class=\"btn primary\" type=\"submit\">\n Sign in\n </button>\n </div>\n </form>\n </Layout>\n);\n","import { randomBytes } from 'node:crypto';\nimport { DOMParser } from '@xmldom/xmldom';\nimport { Hono, type Context } from 'hono';\nimport { logger as honoLogger } from 'hono/logger';\nimport { createLogger, describeRecord, formatOptionValue, prettyPrintXml, type Logger } from './log.js';\nimport { createSimpleProfileMapper } from './profile-mapper.js';\nimport {\n createLogoutStore,\n getSessionIndex,\n sessionMiddleware,\n SessionStore,\n type SessionVariables,\n} from './session.js';\nimport { IDP_PATHS, type IdpSettings, type IdpState } from './state.js';\nimport {\n authenticate,\n buildMetadata,\n logout,\n parseRequest,\n SamlpError,\n SessionParticipants,\n type AuthOptions,\n type LogoutResult,\n type PostForm,\n type SamlHttpInput,\n} from './vendor/samlp/index.js';\nimport { AutoPostForm, ErrorPage, SettingsPage, UserPage, type AuthnRequestInfo } from './views/index.js';\n\ntype AppEnv = { Variables: SessionVariables };\ntype AppContext = Context<AppEnv>;\n\nconst SSO_PATHS = ['/', '/idp', IDP_PATHS.SSO];\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport interface CreateAppOptions {\n state: IdpState;\n logger?: Logger;\n /** Create a new session for every authn request instead of reusing an existing session. */\n rollSession?: boolean;\n}\n\n/** Reads the query string and (form) body of the request in a framework independent shape. */\nasync function readInput(c: AppContext): Promise<SamlHttpInput> {\n const query = c.req.query();\n const body: Record<string, string | undefined> = {};\n if (c.req.method === 'POST') {\n const contentType = c.req.header('content-type') ?? '';\n if (/application\\/x-www-form-urlencoded|multipart\\/form-data/.test(contentType)) {\n const parsed = await c.req.parseBody();\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') body[key] = value;\n }\n }\n }\n return { query, body };\n}\n\nfunction errorPage(c: AppContext, state: IdpState, message: string, status = 500, error?: unknown) {\n const stack = error instanceof Error ? error.stack : undefined;\n return c.html(\n <ErrorPage message={message} status={status} stack={stack} issuer={state.settings.issuer} />,\n status as 400,\n );\n}\n\nfunction postFormResponse(c: AppContext, form: PostForm) {\n return c.html(<AutoPostForm url={form.url} field={form.field} value={form.value} relayState={form.relayState} />);\n}\n\nfunction toBoolean(value: string): boolean {\n return ['true', 'yes', '1', 'on'].includes(value.toLowerCase());\n}\n\n/** Fields of the settings page and how their value is parsed. */\nconst SETTINGS_FIELDS: Record<string, 'string' | 'boolean' | 'number' | 'optional'> = {\n issuer: 'string',\n signatureAlgorithm: 'string',\n digestAlgorithm: 'string',\n signResponse: 'boolean',\n lifetimeInSeconds: 'number',\n authnContextClassRef: 'string',\n allowRequestAcsUrl: 'boolean',\n serviceProviderId: 'string',\n sloUrl: 'optional',\n acsUrl: 'string',\n destination: 'string',\n recipient: 'string',\n audience: 'string',\n RelayState: 'optional',\n};\n\nfunction applySettings(settings: IdpSettings, body: Record<string, string | undefined>): void {\n for (const [key, kind] of Object.entries(SETTINGS_FIELDS)) {\n const value = body[key];\n if (value === undefined) continue;\n const target = settings as unknown as Record<string, unknown>;\n switch (kind) {\n case 'boolean':\n target[key] = toBoolean(value);\n break;\n case 'number': {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isNaN(parsed)) target[key] = parsed;\n break;\n }\n case 'optional':\n target[key] = value === '' ? undefined : value;\n break;\n default:\n target[key] = value;\n }\n }\n}\n\n/** Adds an `AuthnContextDecl` (parsed from XML) to the assertion's `AuthnContext`. */\nfunction authnContextDeclTransformer(decl: string | undefined, logger: Logger): AuthOptions['transformAssertion'] {\n if (!decl) return undefined;\n return (assertionDom) => {\n let declDoc;\n try {\n declDoc = new DOMParser().parseFromString(decl, 'text/xml');\n } catch (err) {\n logger.log('Unable to parse Authentication Context Declaration XML', err);\n return;\n }\n if (!declDoc?.documentElement) return;\n const declEl = assertionDom.createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml:AuthnContextDecl');\n declEl.appendChild(assertionDom.importNode(declDoc.documentElement, true));\n assertionDom.getElementsByTagName('saml:AuthnContext')[0].appendChild(declEl);\n };\n}\n\nfunction requestOrigin(c: AppContext): string {\n const url = new URL(c.req.url);\n const proto = c.req.header('x-forwarded-proto')?.split(',')[0].trim() ?? url.protocol.replace(':', '');\n const host = c.req.header('x-forwarded-host') ?? c.req.header('host') ?? url.host;\n return `${proto}://${host}`;\n}\n\n/** Creates the Hono application that serves the IdP. */\nexport function createApp({ state, logger = createLogger('info'), rollSession = false }: CreateAppOptions): Hono<AppEnv> {\n const app = new Hono<AppEnv>();\n\n /** Renders the error page and logs unexpected (5xx) errors. */\n const fail: typeof errorPage = (c, st, message, status = 500, error) => {\n if (status >= 500) logger.error(error instanceof Error ? error.stack ?? message : message);\n return errorPage(c, st, message, status, error);\n };\n const sessions = new SessionStore();\n const profileMapper = createSimpleProfileMapper(() => state.metadata);\n\n /** Everything the IdP does for a request: settings + derived callbacks. */\n const logoutCredentials = (c: AppContext) => {\n const settings = state.settings;\n const session = c.get('session');\n return {\n participant: {\n serviceProviderId: settings.serviceProviderId,\n sessionIndex: getSessionIndex(session),\n nameId: state.user.userName,\n nameIdFormat: state.user.nameIdFormat,\n serviceProviderLogoutURL: settings.sloUrl,\n },\n session,\n };\n };\n\n /**\n * Middleware\n */\n app.use(\n '*',\n honoLogger((message, ...rest) => logger.log(message, ...rest)),\n );\n app.use('*', sessionMiddleware(sessions, randomBytes(32).toString('hex')));\n\n // Optionally start a fresh session on every authn request\n app.use('*', async (c, next) => {\n if (rollSession && SSO_PATHS.includes(c.req.path) && ['GET', 'POST'].includes(c.req.method)) {\n c.get('session').regenerate();\n }\n await next();\n });\n\n /**\n * View handlers\n */\n const showUser = (c: AppContext, authnRequest?: AuthnRequestInfo) => {\n const session = c.get('session');\n return c.html(\n <UserPage\n user={state.user}\n sessionIndex={getSessionIndex(session)}\n metadata={state.metadata}\n authnRequest={authnRequest}\n idp={state.settings}\n />,\n );\n };\n\n /**\n * SSO: parse the (optional) AuthnRequest and show the sign-in form\n */\n const parseSamlRequest = async (c: AppContext) => {\n const input = await readInput(c);\n let data;\n try {\n data = await parseRequest(input);\n } catch (err) {\n return fail(c, state, 'SAML AuthnRequest Parse Error: ' + (err as Error).message, 400, err);\n }\n\n let authnRequest: AuthnRequestInfo | undefined;\n if (data) {\n authnRequest = {\n relayState: input.query.RelayState || input.body.RelayState,\n id: data.id,\n issuer: data.issuer,\n destination: data.destination,\n acsUrl: data.assertionConsumerServiceURL,\n forceAuthn: data.forceAuthn === 'true',\n };\n logger.log('Received AuthnRequest => \\n', authnRequest);\n }\n return showUser(c, authnRequest);\n };\n app.on(['GET', 'POST'], SSO_PATHS, parseSamlRequest);\n\n /**\n * SLO\n */\n const handleLogout = async (c: AppContext) => {\n const settings = state.settings;\n if (!settings.sloUrl) {\n return fail(c, state, 'SAML Single Logout Service URL not defined for Service Provider', 400);\n }\n const { participant, session } = logoutCredentials(c);\n logger.log('Processing SAML SLO request for participant => \\n', participant);\n\n const input = await readInput(c);\n let result: LogoutResult;\n try {\n result = await logout(input, {\n issuer: settings.issuer,\n cert: settings.cert,\n key: settings.key,\n digestAlgorithm: settings.digestAlgorithm,\n signatureAlgorithm: settings.signatureAlgorithm,\n sessionParticipants: new SessionParticipants([participant]),\n store: createLogoutStore(session),\n clearIdPSession: () => {\n logger.log('Destroying session ' + session.id + ' for participant', participant);\n session.destroy();\n },\n });\n } catch (err) {\n const status = err instanceof SamlpError ? err.status : 500;\n return fail(c, state, (err as Error).message, status, err);\n }\n\n switch (result.type) {\n case 'post':\n return postFormResponse(c, result);\n case 'redirect':\n return c.redirect(result.url);\n default:\n return c.text('OK', 200);\n }\n };\n app.get(IDP_PATHS.SLO, handleLogout);\n app.post(IDP_PATHS.SLO, handleLogout);\n\n /**\n * Sign in: mint the SAML Response and POST it to the Service Provider\n */\n app.post(IDP_PATHS.SIGN_IN, async (c) => {\n const settings = state.settings;\n const session = c.get('session');\n const input = await readInput(c);\n\n const authOptions: AuthOptions & Record<string, unknown> = {\n ...settings,\n profileMapper,\n postEndpointPath: IDP_PATHS.SSO,\n redirectEndpointPath: IDP_PATHS.SSO,\n getPostURL: () => settings.acsUrl,\n transformAssertion: authnContextDeclTransformer(settings.authnContextDecl, logger),\n };\n\n let authnRequest: AuthnRequestInfo | undefined;\n for (const [key, value] of Object.entries(input.body)) {\n if (value === undefined) continue;\n if (key === '_authnRequest') {\n try {\n authnRequest = JSON.parse(Buffer.from(value, 'base64').toString('utf8'));\n } catch {\n return fail(c, state, 'Invalid _authnRequest', 400);\n }\n if (!authnRequest) continue;\n\n // Apply AuthnRequest params\n authOptions.inResponseTo = authnRequest.id;\n if (settings.allowRequestAcsUrl && authnRequest.acsUrl) {\n authOptions.acsUrl = authnRequest.acsUrl;\n authOptions.recipient = authnRequest.acsUrl;\n authOptions.destination = authnRequest.acsUrl;\n authOptions.forceAuthn = authnRequest.forceAuthn;\n authOptions.getPostURL = () => authnRequest!.acsUrl;\n }\n if (authnRequest.relayState) {\n authOptions.RelayState = authnRequest.relayState;\n }\n } else if (!UNSAFE_KEYS.has(key)) {\n state.user[key] = value;\n }\n }\n\n if (!authOptions.encryptAssertion) {\n delete authOptions.encryptionCert;\n delete authOptions.encryptionPublicKey;\n }\n\n // Set Session Index\n authOptions.sessionIndex = getSessionIndex(session);\n\n // Keep calm and Single Sign On\n logger.log(\n [\n 'Generating SAML Response using =>',\n ' User =>',\n describeRecord(state.user, 4, (_k, v) => String(v)),\n ' SAMLP Options =>',\n describeRecord(authOptions, 4),\n '',\n ].join('\\n'),\n );\n\n try {\n // the SAML Response is created for the signed-in user, not for a request\n const result = await authenticate({ query: {}, body: {} }, authOptions, state.user);\n logger.log(\n [\n `Sending SAML Response to ${result.url} =>`,\n ` RelayState =>`,\n ` ${result.relayState || 'None'}`,\n ` SAMLResponse =>`,\n prettyPrintXml(result.xml, 4),\n '',\n ].join('\\n'),\n );\n return postFormResponse(c, result);\n } catch (err) {\n const status = err instanceof SamlpError ? err.status : 500;\n return fail(c, state, (err as Error).message, status, err);\n }\n });\n\n /**\n * Metadata\n */\n app.get(IDP_PATHS.METADATA, (c) => {\n const settings = state.settings;\n const xml = buildMetadata(\n {\n issuer: settings.issuer,\n cert: settings.cert,\n profileMapper,\n postEndpointPath: IDP_PATHS.SSO,\n redirectEndpointPath: IDP_PATHS.SSO,\n logoutEndpointPaths: settings.sloUrl ? { redirect: IDP_PATHS.SLO, post: IDP_PATHS.SLO } : {},\n },\n requestOrigin(c),\n );\n return c.body(xml, 200, { 'Content-Type': 'application/xml' });\n });\n\n app.post(IDP_PATHS.METADATA, async (c) => {\n const { body } = await readInput(c);\n if (body.attributeName && body.displayName) {\n const attribute = {\n id: body.attributeName,\n optional: true,\n displayName: body.displayName,\n description: body.description || '',\n multiValue: body.valueType === 'multi',\n };\n const index = state.metadata.findIndex((entry) => entry.id === attribute.id);\n if (index === -1) state.metadata.push(attribute);\n else state.metadata[index] = attribute;\n\n logger.log('Updated SAML Attribute Metadata => \\n', state.metadata);\n return c.body(null, 200);\n }\n return c.body(null, 400);\n });\n\n /**\n * Sign out\n */\n app.get(IDP_PATHS.SIGN_OUT, (c) => {\n const settings = state.settings;\n if (settings.sloUrl) {\n logger.log(\n `Initiating SAML SLO request for user: ${state.user.userName} with sessionIndex: ${getSessionIndex(c.get('session'))}`,\n );\n return c.redirect(IDP_PATHS.SLO);\n }\n logger.log('SAML SLO is not enabled for SP, destroying IDP session');\n c.get('session').destroy();\n return c.redirect(c.req.header('referer') || '/');\n });\n\n /**\n * Settings\n */\n app.get(IDP_PATHS.SETTINGS, (c) => c.html(<SettingsPage idp={state.settings} />));\n\n app.post(IDP_PATHS.SETTINGS, async (c) => {\n const { body } = await readInput(c);\n applySettings(state.settings, body);\n logger.log('Updated IdP Configuration => \\n', {\n ...state.settings,\n cert: formatOptionValue('cert', state.settings.cert),\n key: formatOptionValue('key', state.settings.key),\n });\n return c.redirect('/');\n });\n\n /**\n * Errors\n */\n app.notFound((c) => {\n const err = new Error('Route Not Found');\n return fail(c as AppContext, state, err.message, 404, err);\n });\n\n app.onError((err, c) => {\n const status = (err as { status?: number }).status ?? 500;\n return fail(c as AppContext, state, err.message, status, err);\n });\n\n return app;\n}\n","import { createServer as createHttpsServer } from 'node:https';\nimport type { AddressInfo } from 'node:net';\nimport { hostname as osHostname } from 'node:os';\nimport type { Server } from 'node:http';\nimport { serve } from '@hono/node-server';\nimport type { Hono } from 'hono';\nimport { createApp } from './app.js';\nimport { configBanner, createLogger, listeningBanner, type Logger } from './log.js';\nimport { resolveOptions, type IdpOptions, type ResolvedOptions } from './options.js';\nimport type { SessionVariables } from './session.js';\nimport { createState, type IdpState } from './state.js';\nimport pc from 'picocolors';\n\nexport { OptionsError } from './options.js';\nexport type { IdpOptions, ResolvedOptions, PemOrPath } from './options.js';\nexport type { LogLevel } from './log.js';\nexport { createDefaultConfig } from './default-config.js';\nexport type { IdpConfig, UserProfile } from './default-config.js';\nexport { IDP_PATHS } from './state.js';\nexport type { IdpSettings, IdpState } from './state.js';\nexport type { ClaimMetadata } from './vendor/samlp/index.js';\n\nconst WILDCARD_ADDRESSES = ['0.0.0.0', '::'];\n\nexport interface Idp {\n /** The Hono application. Use `app.request()` / `app.fetch` to call it without a network port. */\n app: Hono<{ Variables: SessionVariables }>;\n /** Mutable state (settings, user, attribute metadata). */\n state: IdpState;\n /** The effective options after defaults were applied. */\n options: ResolvedOptions;\n logger: Logger;\n}\n\nexport interface RunningIdp extends Idp {\n /** Base URL of the IdP, e.g. `http://localhost:7000`. */\n url: string;\n /** The IdP metadata URL. */\n metadataUrl: string;\n server: Server;\n /** Stops the server and drops open connections. */\n close(): Promise<void>;\n}\n\n/** Creates the IdP application without starting a server. */\nexport async function createIdp(input: IdpOptions): Promise<Idp> {\n const options = await resolveOptions(input);\n const logger = createLogger(options.logLevel);\n const state = createState(options);\n const app = createApp({ state, logger, rollSession: options.rollSession });\n return { app, state, options, logger };\n}\n\n/**\n * Creates the IdP and starts listening. The returned promise resolves once the\n * server accepts connections, so it can be awaited from a test `globalSetup`.\n */\nexport async function runServer(input: IdpOptions): Promise<RunningIdp> {\n const idp = await createIdp(input);\n const { app, options, logger } = idp;\n\n logger.log(configBanner(options));\n logger.log(`Starting IdP server on port ${pc.cyan(`${options.host}:${options.port}`)}...\\n`);\n\n const server = await new Promise<Server>((resolve, reject) => {\n const instance = serve(\n {\n fetch: app.fetch,\n port: options.port,\n hostname: options.host,\n ...(options.https\n ? {\n createServer: createHttpsServer,\n serverOptions: { key: options.httpsPrivateKey, cert: options.httpsCert },\n }\n : {}),\n },\n () => resolve(instance as Server),\n ) as Server;\n instance.once('error', reject);\n });\n\n const { address, port } = server.address() as AddressInfo;\n const scheme = options.https ? 'https' : 'http';\n const bannerHost = WILDCARD_ADDRESSES.includes(address) ? osHostname() : 'localhost';\n const url = `${scheme}://localhost:${port}`;\n logger.log(listeningBanner(`${scheme}://${bannerHost}:${port}`, !!options.sloUrl));\n\n return {\n ...idp,\n url,\n metadataUrl: `${url}/metadata`,\n server,\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()));\n server.closeAllConnections();\n }),\n };\n}\n"],"names":["name","resolve","signXml","sign","isXmlName","raw","signXmlDsig","esc","inflateRawAsync","signers.validateXmlEmbeddedSignature","signers.isValidContentAndSignature","templates.samlResponse","signers.signXml","signers.getSigAlg","signers.sign","logoutRequest","templates.logoutRequest","logoutResponse","templates.logoutResponse","samlResponse","session","transactionId","templates.metadata","metadata","logger","honoLogger","parseSamlRequest","osHostname"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { Hono } from 'hono';
2
+ import { type Logger } from './log.js';
3
+ import { type SessionVariables } from './session.js';
4
+ import { type IdpState } from './state.js';
5
+ type AppEnv = {
6
+ Variables: SessionVariables;
7
+ };
8
+ export interface CreateAppOptions {
9
+ state: IdpState;
10
+ logger?: Logger;
11
+ /** Create a new session for every authn request instead of reusing an existing session. */
12
+ rollSession?: boolean;
13
+ }
14
+ /** Creates the Hono application that serves the IdP. */
15
+ export declare function createApp({ state, logger, rollSession }: CreateAppOptions): Hono<AppEnv>;
16
+ export {};
@@ -0,0 +1,10 @@
1
+ import { Command } from 'commander';
2
+ import { type IdpOptions } from './options.js';
3
+ /** Long option aliases of the original `saml-idp` CLI. */
4
+ export declare const ALIASES: Record<string, string>;
5
+ /**
6
+ * Expands the aliases (`--acs` => `--acsUrl`) and the `--no-<flag>` form of boolean
7
+ * flags (`--no-signResponse` => `--signResponse=false`).
8
+ */
9
+ export declare function normalizeArgv(argv: string[]): string[];
10
+ export declare function buildProgram(onRun: (options: IdpOptions) => Promise<void>): Command;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,12 @@
1
+ import type { ClaimMetadata } from './vendor/samlp/index.js';
2
+ /** Profile of the user that is signed in. Values are rendered as SAML attributes. */
3
+ export type UserProfile = Record<string, string> & {
4
+ userName: string;
5
+ nameIdFormat: string;
6
+ };
7
+ export interface IdpConfig {
8
+ user: UserProfile;
9
+ metadata: ClaimMetadata[];
10
+ }
11
+ /** Returns a fresh copy of the default user profile and attribute metadata. */
12
+ export declare function createDefaultConfig(): IdpConfig;
@@ -0,0 +1,41 @@
1
+ import type { Server } from 'node:http';
2
+ import type { Hono } from 'hono';
3
+ import { type Logger } from './log.js';
4
+ import { type IdpOptions, type ResolvedOptions } from './options.js';
5
+ import type { SessionVariables } from './session.js';
6
+ import { type IdpState } from './state.js';
7
+ export { OptionsError } from './options.js';
8
+ export type { IdpOptions, ResolvedOptions, PemOrPath } from './options.js';
9
+ export type { LogLevel } from './log.js';
10
+ export { createDefaultConfig } from './default-config.js';
11
+ export type { IdpConfig, UserProfile } from './default-config.js';
12
+ export { IDP_PATHS } from './state.js';
13
+ export type { IdpSettings, IdpState } from './state.js';
14
+ export type { ClaimMetadata } from './vendor/samlp/index.js';
15
+ export interface Idp {
16
+ /** The Hono application. Use `app.request()` / `app.fetch` to call it without a network port. */
17
+ app: Hono<{
18
+ Variables: SessionVariables;
19
+ }>;
20
+ /** Mutable state (settings, user, attribute metadata). */
21
+ state: IdpState;
22
+ /** The effective options after defaults were applied. */
23
+ options: ResolvedOptions;
24
+ logger: Logger;
25
+ }
26
+ export interface RunningIdp extends Idp {
27
+ /** Base URL of the IdP, e.g. `http://localhost:7000`. */
28
+ url: string;
29
+ /** The IdP metadata URL. */
30
+ metadataUrl: string;
31
+ server: Server;
32
+ /** Stops the server and drops open connections. */
33
+ close(): Promise<void>;
34
+ }
35
+ /** Creates the IdP application without starting a server. */
36
+ export declare function createIdp(input: IdpOptions): Promise<Idp>;
37
+ /**
38
+ * Creates the IdP and starts listening. The returned promise resolves once the
39
+ * server accepts connections, so it can be awaited from a test `globalSetup`.
40
+ */
41
+ export declare function runServer(input: IdpOptions): Promise<RunningIdp>;