From 824ff038d66bb53d9ab92e4f977130352212fb19 Mon Sep 17 00:00:00 2001 From: Jim Isaacs Date: Mon, 1 Mar 2021 17:32:44 -0800 Subject: [PATCH] some exports cleanup still a WIP to see if I can continue to attack circular architecture --- src/binding-post.ts | 8 +- src/binding-redirect.ts | 6 +- src/binding.ts | 16 ++++ src/entity-idp.ts | 42 +++++++---- src/entity-sp.ts | 52 +++++++++---- src/entity.ts | 81 ++++++++++++++------ src/flow.ts | 5 +- src/index.ts | 14 ++-- src/libsaml.ts | 82 ++++++++++---------- src/metadata-idp.ts | 14 +++- src/metadata-sp.ts | 21 ++++-- src/metadata.ts | 19 ++++- src/types.ts | 161 ---------------------------------------- src/urn.ts | 11 ++- test/flow.ts | 13 ++-- test/index.ts | 2 +- test/issues.ts | 2 +- 17 files changed, 262 insertions(+), 287 deletions(-) create mode 100644 src/binding.ts delete mode 100644 src/types.ts diff --git a/src/binding-post.ts b/src/binding-post.ts index 8fa0ebe4..6d6b91f3 100644 --- a/src/binding-post.ts +++ b/src/binding-post.ts @@ -4,13 +4,13 @@ * @desc Binding-level API, declare the functions using POST binding */ -import type { BindingContext, Entity } from './entity'; -import type { IdentityProvider } from './entity-idp'; +import type { BindingContext } from './binding'; +import type { Entity, ParsedLogoutRequest } from './entity'; +import type { IdentityProvider, ParsedLoginRequest } from './entity-idp'; import type { ServiceProvider } from './entity-sp'; import { SamlifyError, SamlifyErrorCode } from './error'; import type { FlowResult } from './flow'; -import libsaml, { CustomTagReplacement } from './libsaml'; -import type { ParsedLoginRequest, ParsedLogoutRequest } from './types'; +import { CustomTagReplacement, libsaml } from './libsaml'; import { BindingNamespace, StatusCode } from './urn'; import { base64Decode, base64Encode, isNonEmptyArray } from './utility'; diff --git a/src/binding-redirect.ts b/src/binding-redirect.ts index cdeaff40..70101f48 100644 --- a/src/binding-redirect.ts +++ b/src/binding-redirect.ts @@ -3,13 +3,13 @@ * @author tngan * @desc Binding-level API, declare the functions using Redirect binding */ -import type { BindingContext, Entity } from './entity'; +import type { BindingContext } from './binding'; +import type { Entity, ParsedLogoutRequest } from './entity'; import type { IdentityProvider } from './entity-idp'; import type { ServiceProvider } from './entity-sp'; import { SamlifyError, SamlifyErrorCode } from './error'; import type { FlowResult } from './flow'; -import libsaml, { CustomTagReplacement } from './libsaml'; -import type { ParsedLogoutRequest, RequestSignatureAlgorithm } from './types'; +import { CustomTagReplacement, libsaml, RequestSignatureAlgorithm } from './libsaml'; import { BindingNamespace, StatusCode, wording } from './urn'; import { base64Encode, deflateString } from './utility'; diff --git a/src/binding.ts b/src/binding.ts new file mode 100644 index 00000000..77cba33e --- /dev/null +++ b/src/binding.ts @@ -0,0 +1,16 @@ +export interface ESamlHttpRequest { + query?: any; + body?: any; + octetString?: string; +} + +export interface BindingContext { + context: string; + id: string; +} + +export interface PostBindingContext extends BindingContext { + relayState?: string; + entityEndpoint: string; + type: 'SAMLRequest' | 'SAMLResponse'; +} diff --git a/src/entity-idp.ts b/src/entity-idp.ts index 4c210395..4da45f39 100644 --- a/src/entity-idp.ts +++ b/src/entity-idp.ts @@ -3,20 +3,38 @@ * @author tngan * @desc Declares the actions taken by identity provider */ +import type { ESamlHttpRequest } from './binding'; import postBinding from './binding-post'; -import { Entity, ESamlHttpRequest } from './entity'; +import { Entity, EntitySettings } from './entity'; import type { ServiceProvider } from './entity-sp'; import { SamlifyError, SamlifyErrorCode } from './error'; import { flow, FlowResult } from './flow'; -import type { CustomTagReplacement } from './libsaml'; -import metadataIdp, { MetadataIdp } from './metadata-idp'; -import type { IdentityProviderSettings, ParsedLoginRequest } from './types'; +import type { CustomTagReplacement, LoginResponseTemplate } from './libsaml'; +import type { SSOService } from './metadata'; +import { metadataIdp, MetadataIdp } from './metadata-idp'; import { BindingNamespace, ParserType } from './urn'; +export interface IdentityProviderSettings extends EntitySettings { + /** template of login response */ + loginResponseTemplate?: LoginResponseTemplate; + + singleSignOnService?: SSOService[]; + wantAuthnRequestsSigned?: boolean; + wantLogoutRequestSignedResponseSigned?: boolean; +} + +export interface ParsedLoginRequest { + authnContextClassRef?: string; + issuer?: string; + nameIDPolicy?: { format?: string; allowCreate?: string }; + request?: { id?: string; issueInstant?: string; destination?: string; assertionConsumerServiceUrl?: string }; + signature?: string; +} + /** * Identity prvider can be configured using either metadata importing or idpSetting */ -export default function (props: IdentityProviderSettings) { +export function identityProvider(props: IdentityProviderSettings) { return new IdentityProvider(props); } @@ -25,15 +43,13 @@ export default function (props: IdentityProviderSettings) { */ export class IdentityProvider extends Entity { constructor(idpSettings: IdentityProviderSettings) { - const entitySettings = Object.assign( - { - wantAuthnRequestsSigned: false, - tagPrefix: { - encryptedAssertion: 'saml', - }, + const entitySettings = { + wantAuthnRequestsSigned: false, + tagPrefix: { + encryptedAssertion: 'saml', }, - idpSettings - ); + ...idpSettings, + }; const entityMeta = metadataIdp(entitySettings.metadata || entitySettings); // setting with metadata has higher precedence entitySettings.wantAuthnRequestsSigned = entityMeta.isWantAuthnRequestsSigned(); diff --git a/src/entity-sp.ts b/src/entity-sp.ts index 6f6dad7d..14a1c6c5 100644 --- a/src/entity-sp.ts +++ b/src/entity-sp.ts @@ -3,21 +3,47 @@ * @author tngan * @desc Declares the actions taken by service provider */ +import type { BindingContext, ESamlHttpRequest, PostBindingContext } from './binding'; import postBinding from './binding-post'; import redirectBinding from './binding-redirect'; -import { BindingContext, Entity, ESamlHttpRequest, PostBindingContext } from './entity'; +import { Entity, EntitySettings } from './entity'; import type { IdentityProvider } from './entity-idp'; import { SamlifyError, SamlifyErrorCode } from './error'; import { flow, FlowResult } from './flow'; -import type { CustomTagReplacement } from './libsaml'; -import metadataSp, { MetadataSp } from './metadata-sp'; -import type { ParsedLoginResponse, ServiceProviderSettings } from './types'; -import { BindingNamespace, ParserType } from './urn'; +import type { CustomTagReplacement, SAMLDocumentTemplate } from './libsaml'; +import type { SSOService } from './metadata'; +import { metadataSp, MetadataSp } from './metadata-sp'; +import { BindingNamespace, MetaElement, ParserType } from './urn'; + +export interface ServiceProviderSettings extends EntitySettings { + assertionConsumerService?: SSOService[]; + authnRequestsSigned?: boolean; + elementsOrder?: (keyof MetaElement)[]; + wantAssertionsSigned?: boolean; + wantMessageSigned?: boolean; + + /** template of login request */ + loginRequestTemplate?: SAMLDocumentTemplate; + + allowCreate?: boolean; + // will be deprecated soon + relayState?: string; +} + +export interface ParsedLoginResponse { + attributes?: Record; + audience?: string; + conditions?: { notBefore: string; notOnOrAfter: string }; + issuer?: string; + nameID?: string; + response?: { id?: string; issueInstant?: string; destination?: string; inResponseTo?: string }; + sessionIndex?: { authnInstant?: string; sessionNotOnOrAfter?: string; sessionIndex?: string }; +} /* * @desc interface function */ -export default function (props: ServiceProviderSettings) { +export function serviceProvider(props: ServiceProviderSettings) { return new ServiceProvider(props); } @@ -31,14 +57,12 @@ export class ServiceProvider extends Entity * @param {object} spSettings settings of service provider */ constructor(spSettings: ServiceProviderSettings) { - const entitySettings = Object.assign( - { - authnRequestsSigned: false, - wantAssertionsSigned: false, - wantMessageSigned: false, - }, - spSettings - ); + const entitySettings = { + authnRequestsSigned: false, + wantAssertionsSigned: false, + wantMessageSigned: false, + ...spSettings, + }; const entityMeta = metadataSp(entitySettings.metadata || entitySettings); // setting with metadata has higher precedence entitySettings.authnRequestsSigned = entityMeta.isAuthnRequestSigned(); diff --git a/src/entity.ts b/src/entity.ts index 0efe925f..0f7aa9b3 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -4,14 +4,22 @@ * @desc An abstraction for identity provider and service provider. */ import { v4 as uuid } from 'uuid'; +import type { BindingContext, ESamlHttpRequest, PostBindingContext } from './binding'; import postBinding from './binding-post'; import redirectBinding from './binding-redirect'; import { SamlifyError, SamlifyErrorCode } from './error'; import { flow, FlowResult } from './flow'; -import type { CustomTagReplacement } from './libsaml'; -import type { Metadata } from './metadata'; -import type { EntitySettings, ParsedLogoutRequest, ParsedLogoutResponse } from './types'; -import { algorithms, BindingNamespace, messageConfigurations, ParserType } from './urn'; +import type { + CustomTagReplacement, + EncryptionAlgorithm, + KeyEncryptionAlgorithm, + LogoutResponseTemplate, + RequestSignatureAlgorithm, + SAMLDocumentTemplate, + SignatureConfig, +} from './libsaml'; +import type { Metadata, SSOService } from './metadata'; +import { algorithms, BindingNamespace, messageConfigurations, MessageSignatureOrder, ParserType } from './urn'; import { isNonEmptyArray, isString } from './utility'; const dataEncryptionAlgorithm = algorithms.encryption.data; @@ -32,27 +40,58 @@ const defaultEntitySetting = { relayState: '', } as const; -export interface ESamlHttpRequest { - query?: any; - body?: any; - octetString?: string; -} +export interface EntitySettings { + metadata?: string | Buffer; + entityID?: string; + singleLogoutService?: SSOService[]; + + isAssertionEncrypted?: boolean; + + /** signature algorithm */ + requestSignatureAlgorithm?: RequestSignatureAlgorithm; + dataEncryptionAlgorithm?: EncryptionAlgorithm; + keyEncryptionAlgorithm?: KeyEncryptionAlgorithm; + + messageSigningOrder?: MessageSignatureOrder; + signatureConfig?: SignatureConfig; + transformationAlgorithms?: string[]; + wantLogoutRequestSigned?: boolean; + wantLogoutResponseSigned?: boolean; + + signingCert?: string | Buffer; + privateKey?: string | Buffer; + privateKeyPass?: string; + + encryptCert?: string | Buffer; + encPrivateKey?: string | Buffer; + encPrivateKeyPass?: string; + + /** template of logout request */ + logoutRequestTemplate?: SAMLDocumentTemplate; + /** template of logout response */ + logoutResponseTemplate?: LogoutResponseTemplate; + + nameIDFormat?: string[]; + // https://github.com/tngan/samlify/issues/337 + clockDrifts?: [number, number]; + /** customized function used for generating request ID */ + generateID?: () => string; -export interface BindingContext { - context: string; - id: string; + /** Declare the tag of specific xml document node. `TagPrefixKey` currently supports `encryptedAssertion` only */ + tagPrefix?: { encryptedAssertion?: string }; } -export interface PostBindingContext extends BindingContext { - relayState?: string; - entityEndpoint: string; - type: 'SAMLRequest' | 'SAMLResponse'; +export interface ParsedLogoutRequest { + request?: { id?: string; issueInstant?: string; destination?: string }; + issuer?: string; + nameID?: string; + signature?: string; } -export interface ParseResult { - samlContent: string; - extract: any; - sigAlg: string; +export interface ParsedLogoutResponse { + response?: { id?: string; destination?: string; inResponseTo?: string }; + issuer?: string; + signature?: string; } export class Entity { @@ -64,7 +103,7 @@ export class Entity[1]; + +export interface SAMLDocumentTemplate { + context?: string; +} +export interface LoginResponseAttribute { + name: string; + nameFormat: string; + valueXsiType: string; + valueTag: string; + valueXmlnsXs?: string; + valueXmlnsXsi?: string; +} +export interface LoginResponseTemplate extends Required { + attributes?: LoginResponseAttribute[]; +} +export type LoginRequestTemplate = Required; + +export type LogoutRequestTemplate = Required; + +export type LogoutResponseTemplate = Required; + +export interface CustomTagReplacement< + Values extends Record = Record +> { + (template: string, values: Values): readonly [template?: string, values?: Values]; +} + const signatureAlgorithms = algorithms.signature; const digestAlgorithms = algorithms.digest; const certUse = wording.certUse; @@ -46,7 +79,7 @@ const dom = DOMParser; // } // } -export interface SignatureConstructor { +interface SignatureConstructor { rawSamlMessage: string; referenceTagXPath?: string; privateKey: string; @@ -59,49 +92,18 @@ export interface SignatureConstructor { transformationAlgorithms?: string[]; } -export interface SignatureVerifierOptions { +interface SignatureVerifierOptions { metadata?: Metadata; keyFile?: string; signatureAlgorithm?: RequestSignatureAlgorithm; } -export interface ExtractorResult { - [key: string]: any; - signature?: string | string[]; - issuer?: string | string[]; - nameid?: string; - notexist?: boolean; -} - -export interface LoginResponseAttribute { - name: string; - nameFormat: string; - valueXsiType: string; - valueTag: string; - valueXmlnsXs?: string; - valueXmlnsXsi?: string; -} -export interface LoginResponseTemplate extends Required { - attributes?: LoginResponseAttribute[]; -} -export type LoginRequestTemplate = Required; - -export type LogoutRequestTemplate = Required; - -export type LogoutResponseTemplate = Required; - -export type KeyUse = 'signing' | 'encryption'; +type KeyUse = 'signing' | 'encryption'; -export interface KeyComponent { +interface KeyComponent { [key: string]: any; } -export interface CustomTagReplacement< - Values extends Record = Record -> { - (template: string, values: Values): readonly [template?: string, values?: Values]; -} - const libSaml = () => { /** * @desc helper function to get back the query param for redirect binding for SLO/SSO @@ -166,7 +168,7 @@ const libSaml = () => { * @param {string} sigAlg signature algorithm * @return {string} signing algorithm short-hand for the module node-rsa */ - function getSigningScheme(sigAlg?: RequestSignatureAlgorithm): NodeRSA.Options['signingScheme'] { + function getSigningScheme(sigAlg?: RequestSignatureAlgorithm): nrsa.Options['signingScheme'] { if (sigAlg) { const algAlias = nrsaAliasMapping[sigAlg]; if (algAlias != null) return algAlias; @@ -529,7 +531,7 @@ const libSaml = () => { */ verifyMessageSignature( metadata: Metadata, - octetString: NodeRSA.Data, + octetString: nrsa.Data, signature: Buffer, verifyAlgorithm?: RequestSignatureAlgorithm ) { @@ -707,4 +709,4 @@ ${targetEntityMetadata.getX509Certificate(certUse.encrypt)} }; }; -export default libSaml(); +export const libsaml = libSaml(); diff --git a/src/metadata-idp.ts b/src/metadata-idp.ts index c14c33e8..229f6871 100644 --- a/src/metadata-idp.ts +++ b/src/metadata-idp.ts @@ -5,16 +5,22 @@ */ import xml from 'xml'; import { SamlifyError, SamlifyErrorCode } from './error'; -import libsaml from './libsaml'; -import { Metadata } from './metadata'; -import type { MetadataIdpConstructorOptions } from './types'; +import { libsaml, RequestSignatureAlgorithm } from './libsaml'; +import { Metadata, MetadataFile, MetadataOptions } from './metadata'; import { BindingNamespace, names } from './urn'; import { isNonEmptyArray, isString } from './utility'; +interface MetadataIdpOptions extends MetadataOptions { + requestSignatureAlgorithm?: RequestSignatureAlgorithm; + wantAuthnRequestsSigned?: boolean; +} + +export type MetadataIdpConstructorOptions = MetadataIdpOptions | MetadataFile; + /* * @desc interface function */ -export default function (meta: MetadataIdpConstructorOptions) { +export function metadataIdp(meta: MetadataIdpConstructorOptions) { return new MetadataIdp(meta); } diff --git a/src/metadata-sp.ts b/src/metadata-sp.ts index 687d3ba2..9f80176d 100644 --- a/src/metadata-sp.ts +++ b/src/metadata-sp.ts @@ -4,16 +4,27 @@ * @desc Metadata of service provider */ import xml from 'xml'; -import libsaml from './libsaml'; -import { Metadata } from './metadata'; -import type { MetadataSpConstructorOptions, MetaElement } from './types'; -import { BindingNamespace, elementsOrder as order, names } from './urn'; +import { libsaml } from './libsaml'; +import { Metadata, MetadataFile, MetadataOptions, SSOService } from './metadata'; +import { BindingNamespace, elementsOrder as order, MetaElement, names } from './urn'; import { isNonEmptyArray, isString } from './utility'; +interface MetadataSpOptions extends MetadataOptions { + assertionConsumerService?: SSOService[]; + authnRequestsSigned?: boolean; + elementsOrder?: (keyof MetaElement)[]; + // TODO: Not sure if this is used. Consider removing. + signatureConfig?: Record; + wantAssertionsSigned?: boolean; + wantMessageSigned?: boolean; +} + +export type MetadataSpConstructorOptions = MetadataSpOptions | MetadataFile; + /* * @desc interface function */ -export default function (meta: MetadataSpConstructorOptions) { +export function metadataSp(meta: MetadataSpConstructorOptions) { return new MetadataSp(meta); } diff --git a/src/metadata.ts b/src/metadata.ts index 5ba0df49..76a945fe 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -9,6 +9,23 @@ import { extract } from './extractor'; import type { BindingNamespace } from './urn'; import { isString } from './utility'; +export interface SSOService { + isDefault?: boolean; + Binding: BindingNamespace; + Location: string; +} + +export interface MetadataOptions { + encryptCert?: string | Buffer; + entityID?: string; + nameIDFormat?: string[]; + signingCert?: string | Buffer; + singleLogoutService?: SSOService[]; + singleSignOnService?: SSOService[]; +} + +export type MetadataFile = string | Buffer; + export class Metadata { private xmlString: string; protected meta: any; @@ -17,7 +34,7 @@ export class Metadata { * @param {string | Buffer} metadata xml * @param {object} extraParse for custom metadata extractor */ - constructor(xml: string | Buffer, extraParse: any = []) { + constructor(xml: MetadataFile, extraParse: any = []) { this.xmlString = xml.toString(); this.meta = extract( this.xmlString, diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 5ed0ab36..00000000 --- a/src/types.ts +++ /dev/null @@ -1,161 +0,0 @@ -import type { SignedXml } from 'xml-crypto'; -import type { EncryptionAlgorithm, KeyEncryptionAlgorithm } from 'xml-encryption'; -import type { LoginResponseTemplate, LogoutResponseTemplate } from './libsaml'; -import type { BindingNamespace, MessageSignatureOrder } from './urn'; - -interface SSOService { - isDefault?: boolean; - Binding: BindingNamespace; - Location: string; -} - -export type RequestSignatureAlgorithm = - | 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' - | 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' - | 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'; - -// https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf (P.16, 18) -export interface MetaElement { - AssertionConsumerService?: any[]; - AttributeConsumingService?: any[]; - KeyDescriptor?: any[]; - NameIDFormat?: any[]; - SingleLogoutService?: any[]; -} - -interface MetadataOptions { - encryptCert?: string | Buffer; - entityID?: string; - nameIDFormat?: string[]; - signingCert?: string | Buffer; - singleLogoutService?: SSOService[]; - singleSignOnService?: SSOService[]; -} - -interface MetadataIdpOptions extends MetadataOptions { - requestSignatureAlgorithm?: RequestSignatureAlgorithm; - wantAuthnRequestsSigned?: boolean; -} - -interface MetadataSpOptions extends MetadataOptions { - assertionConsumerService?: SSOService[]; - authnRequestsSigned?: boolean; - elementsOrder?: (keyof MetaElement)[]; - // TODO: Not sure if this is used. Consider removing. - signatureConfig?: Record; - wantAssertionsSigned?: boolean; - wantMessageSigned?: boolean; -} - -type MetadataFile = string | Buffer; - -export type MetadataIdpConstructorOptions = MetadataIdpOptions | MetadataFile; -export type MetadataSpConstructorOptions = MetadataSpOptions | MetadataFile; - -export interface SAMLDocumentTemplate { - context?: string; -} - -export type SignatureConfig = Parameters[1]; - -export interface EntitySettings { - metadata?: string | Buffer; - entityID?: string; - assertionConsumerService?: SSOService[]; - singleLogoutService?: SSOService[]; - - authnRequestsSigned?: boolean; - isAssertionEncrypted?: boolean; - - /** signature algorithm */ - requestSignatureAlgorithm?: RequestSignatureAlgorithm; - dataEncryptionAlgorithm?: EncryptionAlgorithm; - keyEncryptionAlgorithm?: KeyEncryptionAlgorithm; - - messageSigningOrder?: MessageSignatureOrder; - signatureConfig?: SignatureConfig; - transformationAlgorithms?: string[]; - wantAssertionsSigned?: boolean; - wantLogoutRequestSigned?: boolean; - wantLogoutResponseSigned?: boolean; - wantMessageSigned?: boolean; - - signingCert?: string | Buffer; - privateKey?: string | Buffer; - privateKeyPass?: string; - - encryptCert?: string | Buffer; - encPrivateKey?: string | Buffer; - encPrivateKeyPass?: string; - - /** template of login request */ - loginRequestTemplate?: SAMLDocumentTemplate; - /** template of logout request */ - logoutRequestTemplate?: SAMLDocumentTemplate; - /** template of logout response */ - logoutResponseTemplate?: LogoutResponseTemplate; - - nameIDFormat?: string[]; - allowCreate?: boolean; - // will be deprecated soon - relayState?: string; - // https://github.com/tngan/samlify/issues/337 - clockDrifts?: [number, number]; - /** customized function used for generating request ID */ - generateID?: () => string; - - /** Declare the tag of specific xml document node. `TagPrefixKey` currently supports `encryptedAssertion` only */ - tagPrefix?: { encryptedAssertion?: string }; -} - -export interface ServiceProviderSettings extends EntitySettings { - authnRequestsSigned?: boolean; - wantAssertionsSigned?: boolean; - wantMessageSigned?: boolean; - assertionConsumerService?: SSOService[]; - loginRequestTemplate?: SAMLDocumentTemplate; - logoutRequestTemplate?: SAMLDocumentTemplate; - transformationAlgorithms?: string[]; - allowCreate?: boolean; - // will be deprecated soon - relayState?: string; - // https://github.com/tngan/samlify/issues/337 - clockDrifts?: [number, number]; -} - -export interface IdentityProviderSettings extends EntitySettings { - /** template of login response */ - loginResponseTemplate?: LoginResponseTemplate; - - singleSignOnService?: SSOService[]; - wantAuthnRequestsSigned?: boolean; - wantLogoutRequestSignedResponseSigned?: boolean; -} - -export interface ParsedLoginRequest { - authnContextClassRef?: string; - issuer?: string; - nameIDPolicy?: { format?: string; allowCreate?: string }; - request?: { id?: string; issueInstant?: string; destination?: string; assertionConsumerServiceUrl?: string }; - signature?: string; -} -export interface ParsedLoginResponse { - attributes?: Record; - audience?: string; - conditions?: { notBefore: string; notOnOrAfter: string }; - issuer?: string; - nameID?: string; - response?: { id?: string; issueInstant?: string; destination?: string; inResponseTo?: string }; - sessionIndex?: { authnInstant?: string; sessionNotOnOrAfter?: string; sessionIndex?: string }; -} -export interface ParsedLogoutRequest { - request?: { id?: string; issueInstant?: string; destination?: string }; - issuer?: string; - nameID?: string; - signature?: string; -} -export interface ParsedLogoutResponse { - response?: { id?: string; destination?: string; inResponseTo?: string }; - issuer?: string; - signature?: string; -} diff --git a/src/urn.ts b/src/urn.ts index bff8a6e1..f9fe91c6 100644 --- a/src/urn.ts +++ b/src/urn.ts @@ -3,8 +3,6 @@ * @author tngan * @desc Includes all keywords need in samlify */ -import type { MetaElement } from './types'; - export enum BindingNamespace { Redirect = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', Post = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', @@ -162,6 +160,15 @@ export const wording = { }, } as const; +// https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf (P.16, 18) +export interface MetaElement { + AssertionConsumerService?: any[]; + AttributeConsumingService?: any[]; + KeyDescriptor?: any[]; + NameIDFormat?: any[]; + SingleLogoutService?: any[]; +} + // https://wiki.shibboleth.net/confluence/display/CONCEPT/MetadataForSP // some idps restrict the order of elements in entity descriptors export const elementsOrder = { diff --git a/test/flow.ts b/test/flow.ts index 6ffc49a0..e9b1d96c 100644 --- a/test/flow.ts +++ b/test/flow.ts @@ -783,13 +783,12 @@ test('send login response with encrypted non-signed assertion with EncryptThenSi test('Customize prefix (saml2) for encrypted assertion tag', async (t) => { const user = { email: 'test@email.com' }; - const idpCustomizePfx = identityProvider( - Object.assign(defaultIdpConfig, { - tagPrefix: { - encryptedAssertion: 'saml2', - }, - }) - ); + const idpCustomizePfx = identityProvider({ + ...defaultIdpConfig, + tagPrefix: { + encryptedAssertion: 'saml2', + }, + }); const { /*id,*/ context: SAMLResponse } = await idpCustomizePfx.createLoginResponse( sp, sampleRequestInfo, diff --git a/test/index.ts b/test/index.ts index 71d75361..944a740b 100644 --- a/test/index.ts +++ b/test/index.ts @@ -435,7 +435,7 @@ test('getAssertionConsumerService with two bindings', (t) => { ); }); test('sp metadata with shibboleth elements order', (t) => { - const spToShib = serviceProvider(Object.assign({}, baseConfig, { elementsOrder: elementsOrder.shibboleth })); + const spToShib = serviceProvider({ ...baseConfig, elementsOrder: elementsOrder.shibboleth }); t.is( spToShib.getMetadata(), 'MIIDozCCAougAwIBAgIJAKNsmL8QbfpwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTAeFw0xNTA3MDUxNzU2NDdaFw0xODA3MDQxNzU2NDdaMGgxCzAJBgNVBAYTAkhLMRIwEAYDVQQIDAlIb25nIEtvbmcxCzAJBgNVBAcMAkhLMRMwEQYDVQQKDApub2RlLXNhbWwyMSMwIQYJKoZIhvcNAQkBFhRub2RlLnNhbWwyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQJAB8JrsLQbUuJa8akzLqO1EZqClS0tQp+w+5wgufp07WwGn/shma8dcQNj1dbjszI5HBeVFjOKIxlfjmNB9ovhQPstBjP/UPQYp1Ip2IoHCYX9HDgMz3xyXKbHthUzZaECz+p+7WtgwhczRkBLDOm2k15qhPYGPw0vH2zbVRGWUBS9dy2Mp3tqlVbP0xZ9CDNkhCJkV9SMNfoCVW/VYPqK2QBo7ki4obm5x5ixFQSSHsKbVARVzyQH5iNjFe1TdAp3rDwrE5Lc1NQlQaxR5Gnb2NZApDORRZIVlNv2WUdi9QvM0yCzjQ90jP0OAogHhRYaxg0/vgNEye46h+PiY0CAwEAAaNQME4wHQYDVR0OBBYEFEVkjcLAITndky090Ay74QqCmQKIMB8GA1UdIwQYMBaAFEVkjcLAITndky090Ay74QqCmQKIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG4lYX3KQXenez4LpDnZhcFBEZi9YstUKPF5EKd+WplpVbcTQc1A3/Z+uHRmyV8h+pQzeF6Liob37G87YpacPplJI66cf2Rj7j8hSBNbdr+66E2qpcEhAF1iJmzBNyhb/ydlEuVpn8/EsoP+HvBeiDl5gon3562MzZIgV/pLdTfxHyW6hzAQhjGq2UhcvR+gXNVJvHP2eS4jlHnJkB9bfo0kvf87Q+D6XKX3q5c3mO8tqW6UpqHSC+uLEpzZiNLeuFa4TUIhgBgjDjlRrNDKu8ndancSn3yBHYnqJ2t9cR+coFnnjYABQpNrvk4mtmXY8SXoBzYG9Y+lqeAun6+0YyE=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' diff --git a/test/issues.ts b/test/issues.ts index 53b0fe82..5b3b63ff 100644 --- a/test/issues.ts +++ b/test/issues.ts @@ -6,7 +6,7 @@ import { DOMParser as dom } from 'xmldom'; import { identityProvider, serviceProvider } from '../src'; import { isSamlifyError, SamlifyErrorCode } from '../src/error'; import { extract, isElement } from '../src/extractor'; -import libsaml from '../src/libsaml'; +import { libsaml } from '../src/libsaml'; import { BindingNamespace, wording } from '../src/urn'; import { inflateString } from '../src/utility';