import { TokenType , ValidatorBuilder , IdTokenTokenValidator , VerifiableCredentialTokenValidator , VerifiablePresentationTokenValidator , IExpectedVerifiableCredential , IExpectedVerifiablePresentation , IExpectedIdToken , IExpectedSiop , IExpectedSelfIssued , Validator , CryptoBuilder , ManagedHttpResolver , ClaimToken , JsonWebSignatureToken } from '../lib/index' ; import { IssuanceHelpers } from './IssuanceHelpers' ; import TestSetup from './TestSetup' ; import ValidationQueue from '../lib/input_validation/ValidationQueue' ; import { Crypto , SelfIssuedTokenValidator } from '../lib/index' ; import VerifiableCredentialConstants from '../lib/verifiable_credential/VerifiableCredentialConstants' ; import ValidationQueueItem from '../lib/input_validation/ValidationQueueItem' ; const clone = require ( 'clone' ) ; describe ( 'Validator' , ( ) => { let crypto ; let setup ; let originalTimeout = jasmine . DEFAULT_TIMEOUT_INTERVAL ; beforeEach ( async ( ) => { jasmine . DEFAULT_TIMEOUT_INTERVAL = 60000 ; setup = new TestSetup ( ) ; crypto = setup . crypto ; await setup . generateKeys ( ) ; } ) ; afterEach ( async ( ) => { jasmine . DEFAULT_TIMEOUT_INTERVAL = originalTimeout ; setup . fetchMock . reset ( ) ; } ) ; it ( 'should validate id token' , async ( ) => { const siop = ( await IssuanceHelpers . createRequest ( setup , TokenType . idToken , true ) ) [ 2 ] ; const expected = siop . expected . filter ( ( token ) => token . type === TokenType . idToken ) [ 0 ] ; const { idToken } = siop ; let tokenValidator = new IdTokenTokenValidator ( setup . validatorOptions , expected ) ; expect ( ( ) => tokenValidator . getTokens ( < any > undefined , < any > undefined ) ) . toThrowMatching ( ( exception ) => exception . message === `Not implemented` && exception . code === 'VCSDKIDTV01' ) ; let selfIssuedValidator = new SelfIssuedTokenValidator ( setup . validatorOptions , expected ) ; expect ( selfIssuedValidator . isType ) . toEqual ( TokenType . selfIssued ) ; expect ( ( ) => selfIssuedValidator . getTokens ( < any > undefined , < any > undefined ) ) . toThrowMatching ( ( exception ) => exception . message === `Not implemented` && exception . code === 'VCSDKSITV01' ) ; let validator = new ValidatorBuilder ( crypto ) . useValidators ( tokenValidator ) . useTrustedIssuerConfigurationsForIdTokens ( [ setup . defaultIdTokenConfiguration ] ) . build ( ) ; let response = await validator . validate ( idToken ) ; expect ( response . result ) . toBeTruthy ( ) ; expect ( response . validationResult ?. idTokens ) . toBeDefined ( ) ; validator = new ValidatorBuilder ( crypto ) . useTrustedIssuerConfigurationsForIdTokens ( [ setup . defaultIdTokenConfiguration ] ) . build ( ) ; response = await validator . validate ( idToken ) ; expect ( response . result ) . toBeTruthy ( ) ; expect ( response . validationResult ?. idTokens ) . toBeDefined ( ) ; validator = validator . builder . useTrustedIssuerConfigurationsForIdTokens ( [ setup . defaultIdTokenConfiguration ] ) . build ( ) ; response = await validator . validate ( idToken ) ; expect ( response . result ) . toBeTruthy ( ) ; expect ( validator . builder . trustedIssuerConfigurationsForIdTokens ) . toEqual ( [ setup . defaultIdTokenConfiguration ] ) ; expect ( response . validationResult ?. verifiablePresentations ) . toBeUndefined ( ) ; tokenValidator = new IdTokenTokenValidator ( setup . validatorOptions , expected ) ; validator = new ValidatorBuilder ( crypto ) . useValidators ( tokenValidator ) . useTrustedIssuerConfigurationsForIdTokens ( [ setup . defaultIdTokenConfiguration ] ) . build ( ) ; response = await validator . validate ( new ClaimToken ( TokenType . idToken , idToken . rawToken , 'https://example.com/wrong-config' ) ) ; expect ( response . result ) . toBeFalse ( ) ; expect ( response . detailedError ) . toBeTruthy ( ) ; expect ( response . code ) . toEqual ( 'VCSDKIDVa05' ) ; expect ( response . status ) . toEqual ( 403 ) ; const clonedExpected = clone ( expected ) ; clonedExpected . configuration = [ 'xxx' ] ; tokenValidator = new IdTokenTokenValidator ( setup . validatorOptions , clonedExpected ) ; validator = new ValidatorBuilder ( crypto ) . useValidators ( tokenValidator ) . build ( ) ; response = await validator . validate ( new ClaimToken ( TokenType . idToken , idToken . rawToken , 'xxx' ) ) ; expect ( response . result ) . toBeFalsy ( ) ; expect ( response . detailedError ) . toEqual ( `Could not fetch token configuration` ) ; expect ( response . code ) . toEqual ( 'VCSDKVaHe34' ) ; } ) ; it ( 'should validate verifiable credentials' , async ( ) => { const [ request , options , siop ] = await IssuanceHelpers . createRequest ( setup , TokenType . verifiableCredential , true ) ; const expected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiableCredential ) [ 0 ] ; const tokenValidator = new VerifiableCredentialTokenValidator ( setup . validatorOptions , expected ) ; expect ( ( ) => tokenValidator . getTokens ( < any > undefined , < any > undefined ) ) . toThrowMatching ( ( exception ) => exception . message === `Not implemented` && exception . code === 'VCSDKVCTV01' ) ; const validator = new ValidatorBuilder ( crypto ) . useValidators ( tokenValidator ) . build ( ) ; let response = await validator . validate ( siop . vc . rawToken ) ; expect ( response . result ) . toBeTruthy ( ) ; const splitted = siop . vc . rawToken . split ( '.' ) ; response = await validator . validate ( `${ splitted [ 0 ] }.${ splitted [ 1 ] }.1234${ splitted [ 2 ] }` ) ; expect ( response . result ) . toBeFalsy ( response . detailedError ) ; expect ( response . status ) . toEqual ( ValidatorBuilder . INVALID_TOKEN_STATUS_CODE ) ; expect ( response . code ) . toEqual ( 'VCSDKVaHe27' ) ; } ) ; it ( 'should validate verifiable presentations' , async ( ) => { const [ request , options , siop ] = await IssuanceHelpers . createRequest ( setup , TokenType . verifiablePresentationJwt , true ) ; const vcExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiableCredential ) [ 0 ] ; const vpExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiablePresentationJwt ) [ 0 ] ; const vcAttestationName = Object . keys ( siop . attestations . presentations ) [ 0 ] ; const map = { siop : vcExpected } ; map [ vcAttestationName ] = vcExpected ; const vpValidator = new VerifiablePresentationTokenValidator ( setup . validatorOptions , vpExpected ) ; const vcValidator = new VerifiableCredentialTokenValidator ( setup . validatorOptions , map ) ; let validator = new ValidatorBuilder ( crypto ) . useValidators ( [ vcValidator , vpValidator ] ) . enableFeatureVerifiedCredentialsStatusCheck ( false ) . build ( ) ; expect ( vpValidator . isType ) . toEqual ( TokenType . verifiablePresentationJwt ) ; expect ( vcValidator . isType ) . toEqual ( TokenType . verifiableCredential ) ; let queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'vp' , siop . vp ) ; let response = await vpValidator . validate ( queue , queue . getNextToken ( ) ! , setup . defaultUserDid ) ; expect ( response . result ) . toBeTruthy ( 'vpValidator succeeded' ) ; expect ( response . tokensToValidate ! [ `DrivingLicense` ] . rawToken ) . toEqual ( siop . vc . rawToken ) ; let clonedResult = clone ( response ) ; delete clonedResult . payloadObject . vp ; let getTokensResponse = vpValidator . getTokens ( clonedResult , queue ) ; expect ( getTokensResponse . result ) . toBeFalsy ( getTokensResponse . detailedError ) ; expect ( getTokensResponse . detailedError ) . toEqual ( 'No verifiable credential' ) ; expect ( getTokensResponse . code ) . toEqual ( 'VCSDKVPTV01' ) ; expect ( getTokensResponse . status ) . toEqual ( ValidatorBuilder . INVALID_TOKEN_STATUS_CODE ) ; clonedResult = clone ( response ) ; delete clonedResult . payloadObject . vp . verifiableCredential ; getTokensResponse = vpValidator . getTokens ( clonedResult , queue ) ; expect ( getTokensResponse . result ) . toBeFalsy ( getTokensResponse . detailedError ) ; expect ( getTokensResponse . detailedError ) . toEqual ( 'No verifiable credential' ) ; expect ( getTokensResponse . code ) . toEqual ( 'VCSDKVPTV01' ) ; expect ( getTokensResponse . status ) . toEqual ( ValidatorBuilder . INVALID_TOKEN_STATUS_CODE ) ; queue = new ValidationQueue ( ) ; queue . enqueueToken ( vcAttestationName , siop . vc ) ; response = await vcValidator . validate ( queue , queue . getNextToken ( ) ! , setup . defaultUserDid ) ; expect ( response . result ) . toBeTruthy ( 'vcValidator succeeded' ) ; queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'vp' , siop . vp ) ; let token = queue . getNextToken ( ) ! . tokenToValidate ; response = await validator . validate ( token ) ; expect ( response . result ) . toBeTruthy ( 'check validator' ) ; expect ( response . validationResult ?. verifiableCredentials ) . toBeDefined ( ) ; validator = new ValidatorBuilder ( crypto ) . useValidators ( [ ] ) . build ( ) ; queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'vp' , siop . vp ) ; response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . result ) . toBeFalsy ( ) ; expect ( response . detailedError ) . toEqual ( 'verifiablePresentationJwt does not has a TokenValidator' ) ; expect ( response . code ) . toEqual ( 'VCSDKVtor02' ) ; validator = new ValidatorBuilder ( crypto ) . useValidators ( vpValidator ) . enableFeatureVerifiedCredentialsStatusCheck ( false ) . build ( ) ; queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'vp' , siop . vp ) ; response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . result ) . toBeFalsy ( ) ; expect ( response . detailedError ) . toEqual ( 'verifiableCredential does not has a TokenValidator' ) ; expect ( response . code ) . toEqual ( 'VCSDKVtor02' ) ; } ) ; it ( 'should validate presentation siop' , async ( ) => { const [ request , options , siop ] = await IssuanceHelpers . createRequest ( setup , TokenType . verifiablePresentationJwt , false ) ; const siopExpected = siop . expected . filter ( ( token ) => token . type === TokenType . siopPresentationAttestation ) [ 0 ] ; const vcExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiableCredential ) [ 0 ] ; let validator = new ValidatorBuilder ( crypto ) . useAudienceUrl ( siopExpected . audience ) . useTrustedIssuersForVerifiableCredentials ( vcExpected . contractIssuers ) . enableFeatureVerifiedCredentialsStatusCheck ( false ) . build ( ) ; expect ( validator . builder . audienceUrl ) . toEqual ( siopExpected . audience ) ; const queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'siopPresentationAttestation' , request ) ; let response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . result ) . toBeTruthy ( ) ; expect ( response . status ) . toEqual ( 200 ) ; expect ( validator . tokenValidators [ 'siopPresentationAttestation' ] . isType ) . toEqual ( TokenType . siopPresentationAttestation ) ; expect ( response . validationResult ?. siop ) . toBeDefined ( ) ; expect ( response . validationResult ?. verifiablePresentations ) . toBeDefined ( ) ; expect ( response . detailedError ) . toBeUndefined ( ) ; expect ( response . tokensToValidate ) . toBeUndefined ( ) ; expect ( response . validationResult ?. did ) . toEqual ( setup . defaultUserDid ) ; expect ( response . validationResult ?. siopJti ) . toEqual ( IssuanceHelpers . jti ) ; expect ( response . validationResult ?. idTokens ) . toBeUndefined ( ) ; expect ( response . validationResult ?. selfIssued ) . toBeUndefined ( ) ; expect ( response . validationResult ?. verifiableCredentials ) . toBeDefined ( ) ; expect ( response . validationResult ?. verifiableCredentials ! [ 'DrivingLicense' ] . decodedToken . vc . credentialSubject . givenName ) . toEqual ( 'Jules' ) ; validator = validator . builder . useTrustedIssuersForVerifiableCredentials ( { someCredential : vcExpected . contractIssuers . DrivingLicense } ) . build ( ) ; queue . enqueueToken ( 'siopPresentationAttestation' , request ) ; response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . result ) . toBeFalsy ( ) ; expect ( response . detailedError ) . toEqual ( `Expected should have contractIssuers set for verifiableCredential. Missing contractIssuers for 'DrivingLicense'.` ) ; expect ( response . code ) . toEqual ( 'VCSDKVCVA16' ) ; expect ( response . status ) . toEqual ( 403 ) ; queue . enqueueToken ( 'siopPresentationAttestation' , < any > { claims : { } } ) ; response = await validator . validate ( < any > queue . getNextToken ( ) ! ) ; expect ( response . detailedError ) . toEqual ( 'Wrong token type. Expected string or ClaimToken' ) ; expect ( response . code ) . toEqual ( 'VCSDKVtor01' ) ; let spiedMethod = ClaimToken . create ; let createSpy = spyOn ( ClaimToken , 'create' ) . and . callFake ( ( ) : ClaimToken => { throw new Error ( 'some create error' ) ; } ) ; queue . enqueueToken ( 'siopPresentationAttestation' , request ) ; response = await validator . validate ( < any > queue . getNextToken ( ) ! . tokenToValidate . rawToken ) ; expect ( response . detailedError ) . toEqual ( 'some create error' ) ; createSpy . and . callFake ( ( token , id ) : { [ key ] : ClaimToken ; } => { return spiedMethod ( token , id ) ; } ) ; spiedMethod = ClaimToken . getClaimTokensFromAttestations ; let getClaimTokensFromAttestationsSpy = spyOn ( ClaimToken , 'getClaimTokensFromAttestations' ) . and . callFake ( ( ) : { [ key ] : ClaimToken ; } => { throw new Error ( 'some error' ) ; } ) ; queue . enqueueToken ( 'siopPresentationAttestation' , request ) ; response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . detailedError ) . toEqual ( 'some error' ) ; getClaimTokensFromAttestationsSpy . and . callFake ( ( attestations ) : { [ key ] : ClaimToken ; } => { return spiedMethod ( attestations ) ; } ) ; spiedMethod = Validator . getClaimToken ; let getClaimTokenSpy = spyOn ( Validator , 'getClaimToken' ) . and . callFake ( ( ) : ClaimToken => { throw new Error ( 'some getClaimToken error' ) ; } ) ; queue . enqueueToken ( 'siopPresentationAttestation' , request ) ; response = await validator . validate ( < any > queue . getNextToken ( ) ! . tokenToValidate . rawToken ) ; expect ( response . detailedError ) . toEqual ( 'some getClaimToken error' ) ; getClaimTokenSpy . and . callFake ( ( ) : ClaimToken => { return < ClaimToken > { type : < any > 'test' } ; } ) ; queue . enqueueToken ( 'siopPresentationAttestation' , request ) ; validator . tokenValidators [ 'test' ] = validator . tokenValidators [ 'siopPresentationAttestation' ] ; response = await validator . validate ( < any > queue . getNextToken ( ) ! . tokenToValidate . rawToken ) ; expect ( response . detailedError ) . toEqual ( `test is not supported` ) ; expect ( response . code ) . toEqual ( 'VCSDKVtor03' ) ; getClaimTokenSpy . and . callFake ( ( queueItem ) : ClaimToken => { return spiedMethod ( queueItem ) ; } ) ; } ) ; xit ( 'should validate siop and status' , async ( ) => { const [ request , options , siop ] = await IssuanceHelpers . createRequest ( setup , TokenType . verifiablePresentationJwt , true ) ; const siopExpected = siop . expected . filter ( ( token ) => token . type === TokenType . siopIssuance ) [ 0 ] ; const vpExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiablePresentationJwt ) [ 0 ] ; const vcExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiableCredential ) [ 0 ] ; const idTokenExpected = siop . expected . filter ( ( token ) => token . type === TokenType . idToken ) [ 0 ] ; const siExpected = siop . expected . filter ( ( token ) => token . type === TokenType . selfIssued ) [ 0 ] ; let validator = new ValidatorBuilder ( crypto ) . useAudienceUrl ( siopExpected . audience ) . useTrustedIssuerConfigurationsForIdTokens ( idTokenExpected . configuration ) . useTrustedIssuersForVerifiableCredentials ( vcExpected . contractIssuers ) . useResolver ( new ManagedHttpResolver ( VerifiableCredentialConstants . UNIVERSAL_RESOLVER_URL ) ) . enableFeatureVerifiedCredentialsStatusCheck ( true ) . build ( ) ; const queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'siop' , request ) ; const response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . result ) . toBeTruthy ( response . detailedError ) ; expect ( response . status ) . toEqual ( 200 ) ; } ) ; it ( 'should validate siop with default validators' , async ( ) => { const [ request , options , siop ] = await IssuanceHelpers . createRequest ( setup , TokenType . verifiablePresentationJwt , true ) ; const siopExpected = siop . expected . filter ( ( token ) => token . type === TokenType . siopIssuance ) [ 0 ] ; const vpExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiablePresentationJwt ) [ 0 ] ; const vcExpected = siop . expected . filter ( ( token ) => token . type === TokenType . verifiableCredential ) [ 0 ] ; const idTokenExpected = siop . expected . filter ( ( token ) => token . type === TokenType . idToken ) [ 0 ] ; const siExpected = siop . expected . filter ( ( token ) => token . type === TokenType . selfIssued ) [ 0 ] ; let validator = new ValidatorBuilder ( crypto ) . useAudienceUrl ( siopExpected . audience ) . useTrustedIssuerConfigurationsForIdTokens ( idTokenExpected . configuration ) . useTrustedIssuersForVerifiableCredentials ( vcExpected . contractIssuers ) . useResolver ( new ManagedHttpResolver ( VerifiableCredentialConstants . UNIVERSAL_RESOLVER_URL ) ) . enableFeatureVerifiedCredentialsStatusCheck ( false ) . build ( ) ; expect ( validator . resolver ) . toBeDefined ( ) ; const queue = new ValidationQueue ( ) ; queue . enqueueToken ( 'siop' , request ) ; const response = await validator . validate ( queue . getNextToken ( ) ! . tokenToValidate ) ; expect ( response . result ) . toBeTruthy ( ) ; expect ( response . status ) . toEqual ( 200 ) ; expect ( response . detailedError ) . toBeUndefined ( ) ; expect ( response . tokensToValidate ) . toBeUndefined ( ) ; expect ( response . validationResult ?. did ) . toEqual ( setup . defaultUserDid ) ; expect ( response . validationResult ?. siopJti ) . toEqual ( IssuanceHelpers . jti ) ; expect ( response . validationResult ?. siop ) . toBeDefined ( ) ; expect ( response . validationResult ?. verifiablePresentations ) . toBeDefined ( ) ; expect ( response . validationResult ?. idTokens ) . toBeDefined ( ) ; for ( let idtoken in response . validationResult ?. idTokens ) { expect ( response . validationResult ?. idTokens [ idtoken ] . decodedToken . upn ) . toEqual ( 'jules@pulpfiction.com' ) ; } expect ( response . validationResult ?. selfIssued ) . toBeDefined ( ) ; expect ( response . validationResult ?. selfIssued ! . decodedToken . name ) . toEqual ( 'jules' ) ; expect ( response . validationResult ?. verifiableCredentials ) . toBeDefined ( ) ; expect ( response . validationResult ?. verifiableCredentials ! [ 'DrivingLicense' ] . decodedToken . vc . credentialSubject . givenName ) . toEqual ( 'Jules' ) ; } ) ; it ( 'should read the contract id with no spaces' , ( ) => { const id = 'foo' ; const url = `https://test.com/v1.0/abc/def/contracts/${ id }` ; const response = Validator . readContractId ( url ) ; expect ( response ) . toEqual ( id ) ; } ) ; it ( 'should read the contract id with spaces' , ( ) => { const id = 'foo bar' ; const url = `https://test.com/v1.0/abc/def/contracts/${ encodeURIComponent ( id ) }` ; const response = Validator . readContractId ( url ) ; expect ( response ) . toEqual ( id ) ; } ) ; it ( 'should read the contract id with spaces and query' , ( ) => { const id = 'foo bar' ; const url = `https://test.com/v1.0/abc/def/contracts/${ encodeURIComponent ( id ) }?qs=abcdefggh` ; const response = Validator . readContractId ( url ) ; expect ( response ) . toEqual ( id ) ; } ) ; it ( 'should validate a siop' , async ( ) => { setup . fetchMock . reset ( ) ; let crypto = new CryptoBuilder ( ) . build ( ) ; let validator = new ValidatorBuilder ( crypto ) . build ( ) ; const req = 'your token here' ; console . log ( req ) ; const response = await validator . validate ( req ) ; expect ( response . result ) . toBeFalsy ( ) ; } ) ; describe ( 'setValidationResult()' , ( ) => { let validator ; beforeAll ( ( ) => { validator = new Validator ( new ValidatorBuilder ( new Crypto ( new CryptoBuilder ( ) ) ) ) ; } ) ; it ( 'should add IdTokens and IdTokenHints to the same object' , ( ) => { const dummyPayload = JsonWebSignatureToken . encode ( { } , { } ) ; const queue = new ValidationQueue ( ) ; const id = 'idToken' ; const idTokenClaimToken = new ClaimToken ( TokenType . idToken , dummyPayload , id ) ; const idTokenHintClaimToken = new ClaimToken ( TokenType . idTokenHint , dummyPayload , VerifiableCredentialConstants . TOKEN_SI_ISS ) ; const idTokenItem = new ValidationQueueItem ( id , idTokenClaimToken ) ; const idTokenHintItem = new ValidationQueueItem ( VerifiableCredentialConstants . TOKEN_SI_ISS , idTokenHintClaimToken ) ; queue . enqueueItem ( idTokenItem ) ; queue . enqueueItem ( idTokenHintItem ) ; idTokenItem . setResult ( { result : true , status : 200 } , idTokenClaimToken ) ; idTokenHintItem . setResult ( { result : true , status : 200 } , idTokenHintClaimToken ) ; const idTokens = validator [ 'setValidationResult' ] ( queue ) . idTokens ! ; expect ( idTokens ) . toBeTruthy ( ) ; expect ( Object . keys ( idTokens ) . length ) . toEqual ( 2 ) ; const { [ id ] : outputIdTokenClaimToken , [ VerifiableCredentialConstants . TOKEN_SI_ISS ] : outputIdTokenHintClaimToken } = idTokens ; expect ( outputIdTokenClaimToken ) . toEqual ( idTokenClaimToken ) ; expect ( outputIdTokenHintClaimToken ) . toEqual ( idTokenHintClaimToken ) ; } ) ; } ) ; } ) ;