file author tngan
*  desc import { DOMParser } from '@xmldom/xmldom' ; import utility , { flattenDeep , isString } from './utility' ; import { algorithms , wording , namespace } from './urn' ; import { select } from 'xpath' ; import { MetadataInterface } from './metadata' ; import nrsa , { SigningSchemeHash } from 'node-rsa' ; import { SignedXml , FileKeyInfo } from 'xml-crypto' ; import * as xmlenc from '@authenio/xml-encryption' ; import { extract } from './extractor' ; import camelCase from 'camelcase' ; import { getContext } from './api' ; const signatureAlgorithms = algorithms . signature ; const digestAlgorithms = algorithms . digest ; const certUse = wording . certUse ; const urlParams = wording . urlParams ; const dom = DOMParser ; export interface SignatureConstructor { rawSamlMessage : string ; referenceTagXPath ? : string ; privateKey : string ; privateKeyPass ? : string ; signatureAlgorithm : string ; signingCert : string | Buffer ; isBase64Output ? : boolean ; signatureConfig ? : any ; isMessageSigned ? : boolean ; transformationAlgorithms ? : string [ ] ; } export interface SignatureVerifierOptions { metadata ? : MetadataInterface ; keyFile ? : string ; signatureAlgorithm ? : string ; } export interface ExtractorResult { [ key ] : 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 LoginResponseAdditionalTemplates { attributeStatementTemplate ? : AttributeStatementTemplate ; attributeTemplate ? : AttributeTemplate ; } export interface BaseSamlTemplate { context : string ; } export interface LoginResponseTemplate extends BaseSamlTemplate { attributes ? : LoginResponseAttribute [ ] ; additionalTemplates ? : LoginResponseAdditionalTemplates ; } export interface AttributeStatementTemplate extends BaseSamlTemplate { } export interface AttributeTemplate extends BaseSamlTemplate { } export interface LoginRequestTemplate extends BaseSamlTemplate { } export interface LogoutRequestTemplate extends BaseSamlTemplate { } export interface LogoutResponseTemplate extends BaseSamlTemplate { } export type KeyUse = 'signing' | 'encryption' ; export interface KeyComponent { [ key ] : any ; } export interface LibSamlInterface { getQueryParamByType : ( type : string ) => string ; createXPath : ( local , isExtractAll ? : boolean ) => string ; replaceTagsByValue : ( rawXML : string , tagValues : any ) => string ; attributeStatementBuilder : ( attributes : LoginResponseAttribute [ ] , attributeTemplate : AttributeTemplate , attributeStatementTemplate : AttributeStatementTemplate ) => string ; constructSAMLSignature : ( opts : SignatureConstructor ) => string ; verifySignature : ( xml : string , opts ) => [ boolean , any ] ; createKeySection : ( use : KeyUse , cert : string | Buffer ) => { } ; constructMessageSignature : ( octetString : string , key : string , passphrase ? : string , isBase64 ? : boolean , signingAlgorithm ? : string ) => string ; verifyMessageSignature : ( metadata , octetString : string , signature : string | Buffer , verifyAlgorithm ? : string ) => boolean ; getKeyInfo : ( x509Certificate : string , signatureConfig ? : any ) => void ; encryptAssertion : ( sourceEntity , targetEntity , entireXML : string ) => Promise < string > ; decryptAssertion : ( here , entireXML : string ) => Promise < [ string , any ] > ; getSigningScheme : ( sigAlg : string ) => string | null ; getDigestMethod : ( sigAlg : string ) => string | null ; nrsaAliasMapping : any ; defaultLoginRequestTemplate : LoginRequestTemplate ; defaultLoginResponseTemplate : LoginResponseTemplate ; defaultAttributeStatementTemplate : AttributeStatementTemplate ; defaultAttributeTemplate : AttributeTemplate ; defaultLogoutRequestTemplate : LogoutRequestTemplate ; defaultLogoutResponseTemplate : LogoutResponseTemplate ; } const libSaml = ( ) => { desc type { string } function getQueryParamByType ( type ) { if ( [ urlParams . logoutRequest , urlParams . samlRequest ] . indexOf ( type ) !== - 1 ) { return 'SAMLRequest' ; } if ( [ urlParams . logoutResponse , urlParams . samlResponse ] . indexOf ( type ) !== - 1 ) { return 'SAMLResponse' ; } throw new Error ( 'ERR_UNDEFINED_QUERY_PARAMS' ) ; } /**
     *
     */ const nrsaAliasMapping = { 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' : 'pkcs1-sha1' , 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' : 'pkcs1-sha256' , 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' : 'pkcs1-sha512' , } ; desc type { LoginRequestTemplate } const defaultLoginRequestTemplate = { context : '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>' , } ; desc type { LogoutRequestTemplate } const defaultLogoutRequestTemplate = { context : '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID></samlp:LogoutRequest>' , } ; desc type { AttributeStatementTemplate } const defaultAttributeStatementTemplate = { context : '<saml:AttributeStatement>{Attributes}</saml:AttributeStatement>' , } ; desc type { AttributeTemplate } const defaultAttributeTemplate = { context : '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}"><saml:AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</saml:AttributeValue></saml:Attribute>' , } ; desc type { LoginResponseTemplate } const defaultLoginResponseTemplate = { context : '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AuthnStatement}{AttributeStatement}</saml:Assertion></samlp:Response>' , attributes : [ ] , additionalTemplates : { "attributeStatementTemplate" : defaultAttributeStatementTemplate , "attributeTemplate" : defaultAttributeTemplate } } ; desc type { LogoutResponseTemplate } const defaultLogoutResponseTemplate = { context : '<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status></samlp:LogoutResponse>' , } ; private desc param { string } sigAlg return { string function getSigningScheme ( sigAlg ? ) { if ( sigAlg ) { const algAlias = nrsaAliasMapping [ sigAlg ] ; if ( ! ( algAlias === undefined ) ) { return algAlias ; } } return nrsaAliasMapping [ signatureAlgorithms . RSA_SHA1 ] ; } private desc param { string } sigAlg return { string function getDigestMethod ( sigAlg ) { const digestAlg = digestAlgorithms [ sigAlg ] ; if ( ! ( digestAlg === undefined ) ) { return digestAlg ; } return null ; } public desc param { string param { boolean } isExtractAll return { string } function createXPath ( local , isExtractAll ? ) { if ( isString ( local ) ) { return isExtractAll === true ? "//*[local-name(.)='" + local + "']/text()" : "//*[local-name(.)='" + local + "']" ; } return "//*[local-name(.)='" + local . name + "']/@" + local . attr ; } private desc param { string } prefix param { content } content return { string } function tagging ( prefix , content ) { const camelContent = camelCase ( content ) ; return prefix + camelContent . charAt ( 0 ) . toUpperCase ( ) + camelContent . slice ( 1 ) ; } return { createXPath , getQueryParamByType , defaultLoginRequestTemplate , defaultLoginResponseTemplate , defaultAttributeStatementTemplate , defaultAttributeTemplate , defaultLogoutRequestTemplate , defaultLogoutResponseTemplate , desc param { string } rawXML param { array } tagValues return { string } replaceTagsByValue ( rawXML , tagValues ) { Object . keys ( tagValues ) . forEach ( t => { rawXML = rawXML . replace ( new RegExp ( `{${ t }}` , 'g' ) , tagValues [ t ] ) ; } ) ; return rawXML ; } , desc param { LoginResponseAttribute } attributes param { AttributeTemplate } attributeTemplate param { AttributeStatementTemplate } attributeStatementTemplate return { string } attributeStatementBuilder ( attributes , attributeTemplate = defaultAttributeTemplate , attributeStatementTemplate = defaultAttributeStatementTemplate ) { const attr = attributes . map ( ( { name , nameFormat , valueTag , valueXsiType , valueXmlnsXs , valueXmlnsXsi } ) => { const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema' ; const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance' ; let attributeLine = attributeTemplate . context ; attributeLine = attributeLine . replace ( '{Name}' , name ) ; attributeLine = attributeLine . replace ( '{NameFormat}' , nameFormat ) ; attributeLine = attributeLine . replace ( '{ValueXmlnsXs}' , valueXmlnsXs ? valueXmlnsXs : defaultValueXmlnsXs ) ; attributeLine = attributeLine . replace ( '{ValueXmlnsXsi}' , valueXmlnsXsi ? valueXmlnsXsi : defaultValueXmlnsXsi ) ; attributeLine = attributeLine . replace ( '{ValueXsiType}' , valueXsiType ) ; attributeLine = attributeLine . replace ( '{Value}' , `{${ tagging ( 'attr' , valueTag ) }}` ) ; return attributeLine ; } ) . join ( '' ) ; return attributeStatementTemplate . context . replace ( '{Attributes}' , attr ) ; } , desc param { string } rawSamlMessage param { string } referenceTagXPath param { string } privateKey param { string } passphrase param { string | buffer } signingCert param { string } signatureAlgorithm param { string [ ] } transformationAlgorithms return { string } constructSAMLSignature ( opts ) { const { rawSamlMessage , referenceTagXPath , privateKey , privateKeyPass , signatureAlgorithm = signatureAlgorithms . RSA_SHA256 , transformationAlgorithms = [ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' , 'http://www.w3.org/2001/10/xml-exc-c14n#' , ] , signingCert , signatureConfig , isBase64Output = true , isMessageSigned = false , } = opts ; const sig = new SignedXml ( ) ; if ( referenceTagXPath ) { sig . addReference ( referenceTagXPath , opts . transformationAlgorithms , getDigestMethod ( signatureAlgorithm ) ) ; } if ( isMessageSigned ) { sig . addReference ( '/*' , transformationAlgorithms , getDigestMethod ( signatureAlgorithm ) , '' , '' , '' , false ) ; } sig . signatureAlgorithm = signatureAlgorithm ; sig . keyInfoProvider = new this . getKeyInfo ( signingCert , signatureConfig ) ; sig . signingKey = utility . readPrivateKey ( privateKey , privateKeyPass , true ) ; if ( signatureConfig ) { sig . computeSignature ( rawSamlMessage , signatureConfig ) ; } else { sig . computeSignature ( rawSamlMessage ) ; } return isBase64Output !== false ? utility . base64Encode ( sig . getSignedXml ( ) ) : sig . getSignedXml ( ) ; } , desc param { string } xml param { signature } signature param { SignatureVerifierOptions } opts return { boolean } verifySignature ( xml , opts ) { const doc = new dom ( ) . parseFromString ( xml ) ; const messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']" ; const assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']" ; const wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']" ; let selection = [ ] ; let assertionNode = null ; const messageSignatureNode = select ( messageSignatureXpath , doc ) ; const assertionSignatureNode = select ( assertionSignatureXpath , doc ) ; const wrappingElementNode = select ( wrappingElementsXPath , doc ) ; selection = selection . concat ( messageSignatureNode ) ; selection = selection . concat ( assertionSignatureNode ) ; if ( wrappingElementNode . length !== 0 ) { throw new Error ( 'ERR_POTENTIAL_WRAPPING_ATTACK' ) ; } if ( selection . length === 0 ) { throw new Error ( 'ERR_ZERO_SIGNATURE' ) ; } const sig = new SignedXml ( ) ; let verified = true ; selection . forEach ( signatureNode => { sig . signatureAlgorithm = opts . signatureAlgorithm ; if ( ! opts . keyFile && ! opts . metadata ) { throw new Error ( 'ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS' ) ; } if ( opts . keyFile ) { sig . keyInfoProvider = new FileKeyInfo ( opts . keyFile ) ; } if ( opts . metadata ) { const certificateNode = select ( ".//*[local-name(.)='X509Certificate']" , signatureNode ) as any ; let metadataCert = opts . metadata . getX509Certificate ( certUse . signing ) ; if ( Array . isArray ( metadataCert ) ) { metadataCert = flattenDeep ( metadataCert ) ; } else if ( typeof metadataCert === 'string' ) { metadataCert = [ metadataCert ] ; } metadataCert = metadataCert . map ( utility . normalizeCerString ) ; if ( certificateNode . length === 0 && metadataCert . length === 0 ) { throw new Error ( 'NO_SELECTED_CERTIFICATE' ) ; } if ( certificateNode . length !== 0 ) { const x509CertificateData = certificateNode [ 0 ] . firstChild . data ; const x509Certificate = utility . normalizeCerString ( x509CertificateData ) ; if ( metadataCert . length >= 1 && ! metadataCert . find ( cert => cert . trim ( ) === x509Certificate . trim ( ) ) ) { throw new Error ( 'ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA' ) ; } sig . keyInfoProvider = new this . getKeyInfo ( x509Certificate ) ; } else { sig . keyInfoProvider = new this . getKeyInfo ( metadataCert [ 0 ] ) ; } } sig . loadSignature ( signatureNode ) ; doc . removeChild ( signatureNode ) ; verified = verified && sig . checkSignature ( doc . toString ( ) ) ; if ( ! verified ) { throw new Error ( 'ERR_FAILED_TO_VERIFY_SIGNATURE' ) ; } } ) ; if ( messageSignatureNode . length === 1 ) { const node = select ( "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']" , doc ) ; if ( node . length === 1 ) { assertionNode = node [ 0 ] . toString ( ) ; } } if ( assertionSignatureNode . length === 1 ) { const verifiedAssertionInfo = extract ( assertionSignatureNode [ 0 ] . toString ( ) , [ { key : 'refURI' , localPath : [ 'Signature' , 'SignedInfo' , 'Reference' ] , attributes : [ 'URI' ] } ] ) ; const desiredAssertionInfo = extract ( doc . toString ( ) , [ { key : 'id' , localPath : [ '~Response' , 'Assertion' ] , attributes : [ 'ID' ] } ] ) ; if ( verifiedAssertionInfo . refURI !== `#${ desiredAssertionInfo . id }` ) { throw new Error ( 'ERR_POTENTIAL_WRAPPING_ATTACK' ) ; } const verifiedDoc = extract ( doc . toString ( ) , [ { key : 'assertion' , localPath : [ '~Response' , 'Assertion' ] , attributes : [ ] , context : true } ] ) ; assertionNode = verifiedDoc . assertion . toString ( ) ; } return [ verified , assertionNode ] ; } , desc param { string } use param { string } certString return { object } createKeySection ( use , certString ) { return { [ 'KeyDescriptor' ] : [ { _attr : { use } , } , { [ 'ds:KeyInfo' ] : [ { _attr : { 'xmlns:ds' : 'http://www.w3.org/2000/09/xmldsig#' , } , } , { [ 'ds:X509Data' ] : [ { 'ds:X509Certificate' : utility . normalizeCerString ( certString ) , } ] , } , ] , } ] , } ; } , desc param { string } octetString param { string } key param { string } passphrase param { string } signingAlgorithm return { string } constructMessageSignature ( octetString , key , passphrase ? , isBase64 ? , signingAlgorithm ? ) { const decryptedKey = new nrsa ( utility . readPrivateKey ( key , passphrase ) , 'private' , { signingScheme : getSigningScheme ( signingAlgorithm ) , } ) ; const signature = decryptedKey . sign ( octetString ) ; return isBase64 !== false ? signature . toString ( 'base64' ) : signature ; } , desc param { Metadata } metadata param { string } octetString param { string } signature param { string } verifyAlgorithm return { boolean } verifyMessageSignature ( metadata , octetString , signature , verifyAlgorithm ? ) { const signCert = metadata . getX509Certificate ( certUse . signing ) ; const signingScheme = getSigningScheme ( verifyAlgorithm ) ; const key = new nrsa ( utility . getPublicKeyPemFromCertificate ( signCert ) , 'public' , { signingScheme } ) ; return key . verify ( Buffer . from ( octetString ) , Buffer . from ( signature ) ) ; } , desc param { string } x509Certificate return { string } getKeyInfo ( x509Certificate , signatureConfig = { } ) { this . getKeyInfo = key => { const prefix = signatureConfig . prefix ? `${ signatureConfig . prefix }:` : '' ; return `<${ prefix }X509Data><${ prefix }X509Certificate>${ x509Certificate }</${ prefix }X509Certificate></${ prefix }X509Data>` ; } ; this . getKey = keyInfo => { return utility . getPublicKeyPemFromCertificate ( x509Certificate ) . toString ( ) ; } ; } , desc param { Entity } sourceEntity param { Entity } targetEntity param { string } xml return { Promise } encryptAssertion ( sourceEntity , targetEntity , xml ? ) { return new Promise < string > ( ( resolve , reject ) => { if ( ! xml ) { return reject ( new Error ( 'ERR_UNDEFINED_ASSERTION' ) ) ; } const sourceEntitySetting = sourceEntity . entitySetting ; const targetEntityMetadata = targetEntity . entityMeta ; const doc = new dom ( ) . parseFromString ( xml ) ; const assertions = select ( "//*[local-name(.)='Assertion']" , doc ) as Node [ ] ; if ( ! Array . isArray ( assertions ) ) { throw new Error ( 'ERR_NO_ASSERTION' ) ; } if ( assertions . length !== 1 ) { throw new Error ( 'ERR_MULTIPLE_ASSERTION' ) ; } if ( sourceEntitySetting . isAssertionEncrypted ) { xmlenc . encrypt ( assertions [ 0 ] . toString ( ) , { rsa_pub : Buffer . from ( utility . getPublicKeyPemFromCertificate ( targetEntityMetadata . getX509Certificate ( certUse . encrypt ) ) . replace ( /\r?\n|\r/g , '' ) ) , pem : Buffer . from ( '-----BEGIN CERTIFICATE-----' + targetEntityMetadata . getX509Certificate ( certUse . encrypt ) + '-----END CERTIFICATE-----' ) , encryptionAlgorithm : sourceEntitySetting . dataEncryptionAlgorithm , keyEncryptionAlgorithm : sourceEntitySetting . keyEncryptionAlgorithm , } , ( err , res ) => { if ( err ) { console . error ( err ) ; return reject ( new Error ( 'ERR_EXCEPTION_OF_ASSERTION_ENCRYPTION' ) ) ; } if ( ! res ) { return reject ( new Error ( 'ERR_UNDEFINED_ENCRYPTED_ASSERTION' ) ) ; } const { encryptedAssertion : encAssertionPrefix } = sourceEntitySetting . tagPrefix ; const encryptAssertionNode = new dom ( ) . parseFromString ( `<${ encAssertionPrefix }:EncryptedAssertion xmlns:${ encAssertionPrefix }="${ namespace . names . assertion }">${ res }</${ encAssertionPrefix }:EncryptedAssertion>` ) ; doc . replaceChild ( encryptAssertionNode , assertions [ 0 ] ) ; return resolve ( utility . base64Encode ( doc . toString ( ) ) ) ; } ) ; } else { return resolve ( utility . base64Encode ( xml ) ) ; } } ) ; } , desc param { string } type param { Entity } here param { Entity } from param { string } entireXML return { function } decryptAssertion ( here , entireXML ) { return new Promise < [ string , any ] > ( ( resolve , reject ) => { if ( ! entireXML ) { return reject ( new Error ( 'ERR_UNDEFINED_ASSERTION' ) ) ; } const hereSetting = here . entitySetting ; const xml = new dom ( ) . parseFromString ( entireXML ) ; const encryptedAssertions = select ( "/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']" , xml ) as Node [ ] ; if ( ! Array . isArray ( encryptedAssertions ) ) { throw new Error ( 'ERR_UNDEFINED_ENCRYPTED_ASSERTION' ) ; } if ( encryptedAssertions . length !== 1 ) { throw new Error ( 'ERR_MULTIPLE_ASSERTION' ) ; } return xmlenc . decrypt ( encryptedAssertions [ 0 ] . toString ( ) , { key : utility . readPrivateKey ( hereSetting . encPrivateKey , hereSetting . encPrivateKeyPass ) , } , ( err , res ) => { if ( err ) { console . error ( err ) ; return reject ( new Error ( 'ERR_EXCEPTION_OF_ASSERTION_DECRYPTION' ) ) ; } if ( ! res ) { return reject ( new Error ( 'ERR_UNDEFINED_ENCRYPTED_ASSERTION' ) ) ; } const assertionNode = new dom ( ) . parseFromString ( res ) ; xml . replaceChild ( assertionNode , encryptedAssertions [ 0 ] ) ; return resolve ( [ xml . toString ( ) , res ] ) ; } ) ; } ) ; } , desc async isValidXml ( input ) { const { validate } = getContext ( ) ; /**
             * user can write a validate function that always returns
             * a resolved promise and skip the validator even in
             * production, user will take the responsibility if
             * they intend to skip the validation
             */ if ( ! validate ) { return Promise . reject ( 'Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)' ) ; } try { return await validate ( input ) ; } catch ( e ) { throw e ; } } , } ; } ; export default libSaml ( ) ;