Skip to content

Commit

Permalink
Export generateServiceProviderMetadata (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
nwalters512 committed Feb 7, 2024
1 parent 31185e0 commit c5a2cc9
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 8 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,19 @@ The `decryptionCert` argument should be a public certificate matching the `decry

The `signingCert` argument should be a public certificate matching the `privateKey` and is required if the strategy is configured with a `privateKey`. An array of certificates can be provided to support certificate rotation. When supplying an array of certificates, the first entry in the array should match the current `privateKey`. Additional entries in the array can be used to publish upcoming certificates to IdPs before changing the `privateKey`.

### generateServiceProviderMetadata( params )

The underlying `generateServiceProviderMetadata` function is also exported directly. This is useful if you want to generate metadata without creating a strategy object.

```js
const { generateServiceProviderMetadata } = require("@node-saml/node-saml");

const metadata = generateServiceProviderMetadata({
issuer: "https://example.com",
callbackUrl: "https://example.com/callback",
});
```

## Security and signatures

Node-SAML uses the HTTP Redirect Binding for its `AuthnRequest`s (unless overridden with the `authnRequestBinding` parameter), and expects to receive the messages back via the HTTP POST binding.
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const DEFAULT_IDENTIFIER_FORMAT = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
export const DEFAULT_WANT_ASSERTIONS_SIGNED = true;
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SAML } from "./saml";
import { generateServiceProviderMetadata } from "./metadata";
import {
CacheItem,
CacheProvider,
Expand All @@ -18,6 +19,7 @@ import {

export {
SAML,
generateServiceProviderMetadata,
CacheItem,
CacheProvider,
SamlOptions,
Expand Down
9 changes: 6 additions & 3 deletions src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from "./types";
import { assertRequired, signXmlMetadata } from "./utility";
import { buildXmlBuilderObject } from "./xml";
import { generateUniqueId as generateUniqueIdDefault } from "./crypto";
import { DEFAULT_IDENTIFIER_FORMAT, DEFAULT_WANT_ASSERTIONS_SIGNED } from "./constants";

export const generateServiceProviderMetadata = (
params: GenerateServiceProviderMetadataParams,
Expand All @@ -15,13 +17,14 @@ export const generateServiceProviderMetadata = (
issuer,
callbackUrl,
logoutCallbackUrl,
identifierFormat,
wantAssertionsSigned,
decryptionPvk,
privateKey,
metadataContactPerson,
metadataOrganization,
generateUniqueId,
identifierFormat = DEFAULT_IDENTIFIER_FORMAT,
wantAssertionsSigned = DEFAULT_WANT_ASSERTIONS_SIGNED,
// This matches the default used in the `SAML` class.
generateUniqueId = generateUniqueIdDefault,
} = params;

let { signingCerts, decryptionCert } = params;
Expand Down
5 changes: 3 additions & 2 deletions src/saml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { keyInfoToPem, generateUniqueId } from "./crypto";
import { dateStringToTimestamp, generateInstant } from "./date-time";
import { signAuthnRequestPost } from "./saml-post-signing";
import { generateServiceProviderMetadata } from "./metadata";
import { DEFAULT_IDENTIFIER_FORMAT, DEFAULT_WANT_ASSERTIONS_SIGNED } from "./constants";

const debug = Debug("node-saml");
const inflateRawAsync = util.promisify(zlib.inflateRaw);
Expand Down Expand Up @@ -125,11 +126,11 @@ class SAML {
audience: ctorOptions.audience ?? ctorOptions.issuer ?? "unknown_audience", // use issuer as default
identifierFormat:
ctorOptions.identifierFormat === undefined
? "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
? DEFAULT_IDENTIFIER_FORMAT
: ctorOptions.identifierFormat,
allowCreate: ctorOptions.allowCreate ?? true,
spNameQualifier: ctorOptions.spNameQualifier,
wantAssertionsSigned: ctorOptions.wantAssertionsSigned ?? true,
wantAssertionsSigned: ctorOptions.wantAssertionsSigned ?? DEFAULT_WANT_ASSERTIONS_SIGNED,
wantAuthnResponseSigned: ctorOptions.wantAuthnResponseSigned ?? true,
authnContext: ctorOptions.authnContext ?? [
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
Expand Down
6 changes: 3 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export type CertCallback = (
export interface MandatorySamlOptions {
cert: string | string[] | CertCallback;
issuer: string;
callbackUrl: string;
}

export interface SamlIDPListConfig {
Expand Down Expand Up @@ -141,7 +142,6 @@ export enum ValidateInResponseTo {
*/
export interface SamlOptions extends Partial<SamlSigningOptions>, MandatorySamlOptions {
// Core
callbackUrl: string;
entryPoint?: string;
decryptionPvk?: string | Buffer;

Expand Down Expand Up @@ -216,7 +216,7 @@ export interface GenerateServiceProviderMetadataParams {
callbackUrl: SamlOptions["callbackUrl"];
logoutCallbackUrl?: SamlOptions["logoutCallbackUrl"];
identifierFormat?: SamlOptions["identifierFormat"];
wantAssertionsSigned: SamlOptions["wantAssertionsSigned"];
wantAssertionsSigned?: SamlOptions["wantAssertionsSigned"];
decryptionPvk?: SamlOptions["decryptionPvk"];
privateKey?: SamlOptions["privateKey"];
signatureAlgorithm?: SamlOptions["signatureAlgorithm"];
Expand All @@ -225,7 +225,7 @@ export interface GenerateServiceProviderMetadataParams {
signMetadata?: SamlOptions["signMetadata"];
metadataContactPerson?: SamlOptions["metadataContactPerson"];
metadataOrganization?: SamlOptions["metadataOrganization"];
generateUniqueId: SamlOptions["generateUniqueId"];
generateUniqueId?: SamlOptions["generateUniqueId"];
}

export type SamlConfig = Partial<SamlOptions> & MandatorySamlOptions;
Expand Down
31 changes: 31 additions & 0 deletions test/tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as assert from "assert";
import { FAKE_CERT, TEST_CERT } from "./types";
import { assertRequired, signXmlResponse } from "../src/utility";
import { parseDomFromString, validateSignature } from "../src/xml";
import { generateServiceProviderMetadata } from "../src/metadata";

const BAD_TEST_CERT =
"MIIEOTCCAyGgAwIBAgIJAKZgJdKdCdL6MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNVBAYTAkFVMREwDwYDVQQIEwhWaWN0b3JpYTESMBAGA1UEBxMJTWVsYm91cm5lMSEwHwYDVQQKExhUYWJjb3JwIEhvbGRpbmdzIExpbWl0ZWQxFzAVBgNVBAMTDnN0cy50YWIuY29tLmF1MB4XDTE3MDUzMDA4NTQwOFoXDTI3MDUyODA4NTQwOFowcDELMAkGA1UEBhMCQVUxETAPBgNVBAgTCFZpY3RvcmlhMRIwEAYDVQQHEwlNZWxib3VybmUxITAfBgNVBAoTGFRhYmNvcnAgSG9sZGluZ3MgTGltaXRlZDEXMBUGA1UEAxMOc3RzLnRhYi5jb20uYXUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0NuMcflq3rtupKYDf4a7lWmsXy66fYe9n8jB2DuLMakEJBlzn9j6B98IZftrilTq21VR7wUXROxG8BkN8IHY+l8X7lATmD28fFdZJj0c8Qk82eoq48faemth4fBMx2YrpnhU00jeXeP8dIIaJTPCHBTNgZltMMhphklN1YEPlzefJs3YD+Ryczy1JHbwETxt+BzO1JdjBe1fUTyl6KxAwWvtsNBURmQRYlDOk4GRgdkQnfxBuCpOMeOpV8wiBAi3h65Lab9C5avu4AJlA9e4qbOmWt6otQmgy5fiJVy6bH/d8uW7FJmSmePX9sqAWa9szhjdn36HHVQsfHC+IUEX7AgMBAAGjgdUwgdIwHQYDVR0OBBYEFN6z6cuxY7FTkg1S/lIjnS4x5ARWMIGiBgNVHSMEgZowgZeAFN6z6cuxY7FTkg1S/lIjnS4x5ARWoXSkcjBwMQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExEjAQBgNVBAcTCU1lbGJvdXJuZTEhMB8GA1UEChMYVGFiY29ycCBIb2xkaW5ncyBMaW1pdGVkMRcwFQYDVQQDEw5zdHMudGFiLmNvbS5hdYIJAKZgJdKdCdL6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAMi5HyvXgRa4+kKz3dk4SwAEXzeZRcsbeDJWVUxdb6a+JQxIoG7L9rSbd6yZvP/Xel5TrcwpCpl5eikzXB02/C0wZKWicNmDEBlOfw0Pc5ngdoh6ntxHIWm5QMlAfjR0dgTlojN4Msw2qk7cP1QEkV96e2BJUaqaNnM3zMvd7cfRjPNfbsbwl6hCCCAdwrALKYtBnjKVrCGPwO+xiw5mUJhZ1n6ZivTOdQEWbl26UO60J9ItiWP8VK0d0aChn326Ovt7qC4S3AgDlaJwcKe5Ifxl/UOWePGRwXj2UUuDWFhjtVmRntMmNZbe5yE8MkEvU+4/c6LqGwTCgDenRbK53Dgg";
Expand Down Expand Up @@ -3273,4 +3274,34 @@ describe("node-saml /", function () {
});
});
});

describe("generateServiceProviderMetadata", function () {
it("only requires issuer and callbackUrl parameters", function () {
const metadata = generateServiceProviderMetadata({
issuer: "https://www.example.com",
callbackUrl: "https://www.example.com/callback",
});

expect(metadata).to.be.a("string");
expect(metadata).to.contain('entityID="https://www.example.com"');
expect(metadata).to.contain('Location="https://www.example.com/callback"');
});

it("matches metadata from SAML object", function () {
const saml = new SAML({
cert: "no_cert_needed_for_metadata",
issuer: "https://www.example.com",
callbackUrl: "https://www.example.com/callback",
generateUniqueId: () => "d700077e-60ad-49c1-b93a-dd1753528708",
});

expect(
generateServiceProviderMetadata({
issuer: "https://www.example.com",
callbackUrl: "https://www.example.com/callback",
generateUniqueId: () => "d700077e-60ad-49c1-b93a-dd1753528708",
}),
).to.equal(saml.generateServiceProviderMetadata(null, null));
});
});
});

0 comments on commit c5a2cc9

Please sign in to comment.