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,43 @@
1
+ import { ALGORITHMS } from './constants.js';
2
+ import { type XmlDocument } from './xml.js';
3
+ type SignatureAlgorithmName = keyof typeof ALGORITHMS.SIGNATURE;
4
+ type DigestAlgorithmName = keyof typeof ALGORITHMS.DIGEST;
5
+ export interface SigningOptions {
6
+ key: string | Buffer;
7
+ cert: string | Buffer;
8
+ signatureAlgorithm?: SignatureAlgorithmName;
9
+ digestAlgorithm?: DigestAlgorithmName;
10
+ /** XPath of the element to sign (default: `<LogoutRequest>`). */
11
+ reference?: string;
12
+ }
13
+ export interface ValidationOptions {
14
+ signingCert?: string | Buffer;
15
+ thumbprints?: string | string[];
16
+ }
17
+ export declare function getSigAlg(options: {
18
+ signatureAlgorithm?: SignatureAlgorithmName;
19
+ }): string;
20
+ /** Adds an enveloped signature (placed after `<Issuer>`) to the root element. */
21
+ export declare function signXml(options: SigningOptions, xml: string): string;
22
+ export declare function certToPEM(cert: string | Buffer): string;
23
+ /**
24
+ * Validates the enveloped signature of the root element of `xml`.
25
+ *
26
+ * - If thumbprints are configured, the certificate embedded in `<KeyInfo>` is
27
+ * used and its thumbprint must match one of them.
28
+ * - Otherwise the configured `signingCert` is the only trusted key.
29
+ *
30
+ * @returns a list of validation errors (empty when the signature is valid)
31
+ */
32
+ export declare function validateXmlEmbeddedSignature(xml: XmlDocument, xmlString: string, options: ValidationOptions): string[];
33
+ /** Computes a detached signature (HTTP-Redirect binding) over `content`. */
34
+ export declare function sign(options: {
35
+ key: string | Buffer;
36
+ signatureAlgorithm?: SignatureAlgorithmName;
37
+ }, content: string): string;
38
+ /** Verifies a detached signature. `signatureAlgorithm` is the `SigAlg` URI. */
39
+ export declare function isValidContentAndSignature(content: string, signature: string, options: {
40
+ signingCert: string | Buffer;
41
+ signatureAlgorithm: string;
42
+ }): boolean;
43
+ export {};
@@ -0,0 +1,52 @@
1
+ export interface SamlResponseData {
2
+ id: string;
3
+ instant: string;
4
+ destination?: string;
5
+ inResponseTo?: string;
6
+ issuer: string;
7
+ samlStatusCode: string;
8
+ samlStatusMessage?: string;
9
+ /** Raw (already serialized) assertion XML. */
10
+ assertion?: string;
11
+ }
12
+ export declare function samlResponse(data: SamlResponseData): string;
13
+ export interface LogoutResponseData {
14
+ id: string;
15
+ instant: string;
16
+ destination?: string;
17
+ inResponseTo?: string;
18
+ issuer: string;
19
+ samlStatusCode: string;
20
+ samlStatusMessage?: string;
21
+ }
22
+ export declare function logoutResponse(data: LogoutResponseData): string;
23
+ export interface LogoutRequestData {
24
+ ID: string;
25
+ IssueInstant: string;
26
+ Issuer: string;
27
+ Destination?: string;
28
+ NameID: {
29
+ value?: string;
30
+ Format?: string;
31
+ NameQualifier?: string;
32
+ SPNameQualifier?: string;
33
+ SPProvidedID?: string;
34
+ };
35
+ SessionIndex?: string;
36
+ }
37
+ export declare function logoutRequest(data: LogoutRequestData): string;
38
+ export interface MetadataData {
39
+ issuer: string;
40
+ pem: string;
41
+ redirectEndpoint: string;
42
+ postEndpoint: string;
43
+ logoutEndpoints: {
44
+ redirect?: string;
45
+ post?: string;
46
+ };
47
+ claimTypes: Array<{
48
+ id: string;
49
+ displayName: string;
50
+ }>;
51
+ }
52
+ export declare function metadata(data: MetadataData): string;
@@ -0,0 +1,2 @@
1
+ /** Removes whitespace-only text nodes and re-serializes the document. */
2
+ export declare function trimXml(xml: string): string;
@@ -0,0 +1,55 @@
1
+ import { type ElementType } from './constants.js';
2
+ import { type XmlDocument } from './xml.js';
3
+ /** Minimal, framework-agnostic view of an incoming HTTP request. */
4
+ export interface SamlHttpInput {
5
+ query: Record<string, string | undefined>;
6
+ body: Record<string, string | undefined>;
7
+ }
8
+ export interface SigningCredentials {
9
+ cert?: string | Buffer;
10
+ thumbprints?: string | string[];
11
+ }
12
+ export interface ParseRequestOptions {
13
+ signingCert?: string | Buffer;
14
+ thumbprints?: string | string[];
15
+ relayState?: string;
16
+ signaturePath?: string;
17
+ /**
18
+ * Resolves the credentials used to validate the signature of the incoming
19
+ * message from its issuer (and for logout requests, session index / NameID).
20
+ */
21
+ getCredentials?: (issuer: string | undefined, sessionIndices: string[] | undefined, nameId: string | undefined) => SigningCredentials | undefined | Promise<SigningCredentials | undefined>;
22
+ }
23
+ interface ValidateOptions {
24
+ signingCert?: string | Buffer;
25
+ thumbprints?: string | string[];
26
+ deflate?: boolean;
27
+ relayState?: string;
28
+ signaturePath?: string;
29
+ }
30
+ /** Generates a random 20 character hexadecimal identifier. */
31
+ export declare function generateUniqueID(): string;
32
+ /**
33
+ * Formats the given date in `xs:DateTime` format as per Core SAML "1.3.3 Time Values":
34
+ * UTC, no time zone component, millisecond precision.
35
+ */
36
+ export declare function formatXmlDateTime(date: Date): string;
37
+ export declare function generateInstant(): string;
38
+ export declare function appendQueryString(initialUrl: string, query: Record<string, string>): string;
39
+ /**
40
+ * Decodes (base64, then optionally inflates) and parses a SAML request, and
41
+ * validates its signature when a signing credential is available.
42
+ *
43
+ * @returns the parsed DOM, or `undefined` when there is no SAML message
44
+ */
45
+ export declare function parseSamlRequest(input: SamlHttpInput, samlRequest: string | undefined, type: ElementType, options?: ParseRequestOptions): Promise<XmlDocument | undefined>;
46
+ /**
47
+ * Validates the signature of a SAML request/response received through the
48
+ * HTTP-POST binding (embedded XML signature) or HTTP-Redirect binding
49
+ * (detached `Signature` / `SigAlg` query parameters).
50
+ *
51
+ * @throws when the message is signed with an invalid signature, or unsigned
52
+ * while a signing certificate is configured.
53
+ */
54
+ export declare function validateSignature(input: SamlHttpInput, elementType: ElementType, xml: XmlDocument, xmlString: string, options: ValidateOptions): void;
55
+ export {};
@@ -0,0 +1,7 @@
1
+ import { DOMParser } from '@xmldom/xmldom';
2
+ /** DOM node/document types produced by `@xmldom/xmldom`. */
3
+ export type XmlDocument = ReturnType<DOMParser['parseFromString']>;
4
+ export type XmlNode = NonNullable<XmlDocument['documentElement']>;
5
+ export declare function parseXml(xml: string): XmlDocument;
6
+ /** Evaluates an XPath expression and always returns an array of nodes. */
7
+ export declare function selectNodes(expression: string, node: unknown): XmlNode[];
@@ -0,0 +1,12 @@
1
+ import type { FC } from 'hono/jsx';
2
+ export interface AutoPostFormProps {
3
+ /** Form action (ACS / SLO URL of the Service Provider). */
4
+ url: string;
5
+ /** Name of the hidden field carrying the SAML message. */
6
+ field: string;
7
+ /** Base64 encoded SAML message. */
8
+ value: string;
9
+ relayState?: string;
10
+ }
11
+ /** Page that immediately POSTs a SAML message to the Service Provider (HTTP-POST binding). */
12
+ export declare const AutoPostForm: FC<AutoPostFormProps>;
@@ -0,0 +1,8 @@
1
+ import type { FC } from 'hono/jsx';
2
+ export interface ErrorPageProps {
3
+ message: string;
4
+ status?: number;
5
+ stack?: string;
6
+ issuer?: string;
7
+ }
8
+ export declare const ErrorPage: FC<ErrorPageProps>;
@@ -0,0 +1,4 @@
1
+ import type { FC, PropsWithChildren } from 'hono/jsx';
2
+ export declare const Layout: FC<PropsWithChildren<{
3
+ issuer?: string;
4
+ }>>;
@@ -0,0 +1,5 @@
1
+ import type { FC } from 'hono/jsx';
2
+ import type { IdpSettings } from '../state.js';
3
+ export declare const SettingsPage: FC<{
4
+ idp: IdpSettings;
5
+ }>;
@@ -0,0 +1,20 @@
1
+ import type { FC } from 'hono/jsx';
2
+ import type { UserProfile } from '../default-config.js';
3
+ import { type IdpSettings } from '../state.js';
4
+ import type { ClaimMetadata } from '../vendor/samlp/index.js';
5
+ export interface AuthnRequestInfo {
6
+ relayState?: string;
7
+ id?: string;
8
+ issuer?: string;
9
+ destination?: string;
10
+ acsUrl?: string;
11
+ forceAuthn?: boolean;
12
+ }
13
+ export interface UserPageProps {
14
+ user: UserProfile;
15
+ sessionIndex?: string;
16
+ metadata: ClaimMetadata[];
17
+ authnRequest?: AuthnRequestInfo;
18
+ idp: IdpSettings;
19
+ }
20
+ export declare const UserPage: FC<UserPageProps>;
@@ -0,0 +1,5 @@
1
+ export { AutoPostForm } from './AutoPostForm.js';
2
+ export { ErrorPage } from './ErrorPage.js';
3
+ export { SettingsPage } from './SettingsPage.js';
4
+ export { UserPage } from './UserPage.js';
5
+ export type { AuthnRequestInfo } from './UserPage.js';
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "saml-test-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Test SAML 2.0 Identity Provider (IdP) for E2E tests",
5
+ "private": false,
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "saml",
10
+ "idp",
11
+ "e2e",
12
+ "hono"
13
+ ],
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/types/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/types/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ }
24
+ },
25
+ "bin": {
26
+ "saml-idp": "./dist/cli.js"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "assets",
31
+ "LICENSE*"
32
+ ],
33
+ "scripts": {
34
+ "dev": "vite",
35
+ "build": "vite build && tsc -p tsconfig.build.json",
36
+ "prepare": "npm run build",
37
+ "typecheck": "tsc --noEmit",
38
+ "start": "node dist/cli.js",
39
+ "test": "vitest run"
40
+ },
41
+ "dependencies": {
42
+ "@hono/node-server": "^1.19.0",
43
+ "@xmldom/xmldom": "^0.9.8",
44
+ "commander": "^14.0.0",
45
+ "hono": "^4.9.0",
46
+ "picocolors": "^1.1.1",
47
+ "xml-crypto": "^6.1.2",
48
+ "xml-encryption": "^3.1.0",
49
+ "xml-formatter": "^3.6.6",
50
+ "xml-name-validator": "^5.0.0",
51
+ "xpath": "^0.0.34"
52
+ },
53
+ "devDependencies": {
54
+ "@hono/vite-dev-server": "^0.20.0",
55
+ "@types/node": "^22.0.0",
56
+ "typescript": "^5.9.0",
57
+ "vite": "^7.0.0",
58
+ "vitest": "^5.0.4"
59
+ }
60
+ }