@sphereon/did-auth-siop
Version:
Self Issued OpenID V2 (SIOPv2) and OpenID 4 Verifiable Presentations (OID4VP)
290 lines (268 loc) • 11.5 kB
text/typescript
import { SigningAlgo } from '@sphereon/oid4vc-common'
import { IProofType } from '@sphereon/ssi-types'
import nock from 'nock'
import {
AuthorizationResponseOpts,
CreateAuthorizationRequestOpts,
OP,
PassBy,
ResponseIss,
ResponseMode,
ResponseType,
RP,
Scope,
SubjectIdentifierType,
SubjectType,
SupportedVersion,
VerifyAuthorizationRequestOpts,
} from '..'
import { getCreateJwtCallback, getVerifyJwtCallback, internalSignature } from './DidJwtTestUtils'
import { getResolver } from './ResolverTestUtils'
import { mockedGetEnterpriseAuthToken, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils'
import {
UNIT_TEST_TIMEOUT,
VERIFIER_LOGO_FOR_CLIENT,
VERIFIER_NAME_FOR_CLIENT,
VERIFIER_NAME_FOR_CLIENT_NL,
VERIFIERZ_PURPOSE_TO_VERIFY,
VERIFIERZ_PURPOSE_TO_VERIFY_NL,
} from './data/mockedData'
const EXAMPLE_REDIRECT_URL = 'https://acme.com/hello'
const EXAMPLE_REFERENCE_URL = 'https://rp.acme.com/siop/jwts'
const HEX_KEY = 'f857544a9d1097e242ff0b287a7e6e90f19cf973efe2317f2a4678739664420f'
const DID = 'did:ethr:0x0106a2e985b1E1De9B5ddb4aF6dC9e928F4e99D0'
const KID = 'did:ethr:0x0106a2e985b1E1De9B5ddb4aF6dC9e928F4e99D0#controller'
describe('OP OPBuilder should', () => {
/*it('throw Error when no arguments are passed', async () => {
expect.assertions(1);
await expect(() => new OPBuilder().build()).toThrowError(Error);
});*/
it('build an OP when all arguments are set', async () => {
expect.assertions(1)
expect(
OP.builder()
.withIssuer(ResponseIss.SELF_ISSUED_V2)
.withResponseMode(ResponseMode.POST)
.withRegistration({
passBy: PassBy.REFERENCE,
reference_uri: 'https://registration.here',
logo_uri: VERIFIER_LOGO_FOR_CLIENT,
clientName: VERIFIER_NAME_FOR_CLIENT,
'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100332',
clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY,
'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL,
})
.withCreateJwtCallback(internalSignature('myprivatekey', 'did:example:123', 'did:example:123#key', SigningAlgo.ES256K))
.withVerifyJwtCallback(getVerifyJwtCallback(getResolver('ethr'), { checkLinkedDomain: 'never' }))
.withExpiresIn(1000)
.withSupportedVersions([SupportedVersion.SIOPv2_ID1])
.build(),
).toBeInstanceOf(OP)
})
})
describe('OP should', () => {
const responseOpts: AuthorizationResponseOpts = {
responseURI: EXAMPLE_REDIRECT_URL,
responseURIType: 'redirect_uri',
createJwtCallback: getCreateJwtCallback({
hexPrivateKey: HEX_KEY,
did: DID,
kid: KID,
alg: SigningAlgo.ES256K,
}),
jwtIssuer: { method: 'did', didUrl: KID, alg: SigningAlgo.ES256K },
registration: {
authorizationEndpoint: 'www.myauthorizationendpoint.com',
responseTypesSupported: [ResponseType.ID_TOKEN],
subject_syntax_types_supported: ['did:web'],
vpFormats: {
ldp_vc: {
proof_type: [IProofType.EcdsaSecp256k1Signature2019, IProofType.EcdsaSecp256k1Signature2019],
},
},
logo_uri: VERIFIER_LOGO_FOR_CLIENT,
clientName: VERIFIER_NAME_FOR_CLIENT,
'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100333',
clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY,
'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL,
//TODO: fill it up with actual value
issuer: ResponseIss.SELF_ISSUED_V2,
passBy: PassBy.VALUE,
},
responseMode: ResponseMode.POST,
expiresIn: 2000,
}
const resolver = getResolver('ethr')
const verifyOpts: VerifyAuthorizationRequestOpts = {
verifyJwtCallback: getVerifyJwtCallback(resolver),
verification: {},
correlationId: '1234',
supportedVersions: [SupportedVersion.SIOPv2_ID1],
nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg',
}
/*it('throw Error when build from request opts without enough params', async () => {
expect.assertions(1);
await expect(() => OP.fromOpts({} as never, {} as never)).toThrowError(Error);
});*/
it('return an OP when all request arguments are set', async () => {
expect.assertions(1)
expect(OP.fromOpts(responseOpts, verifyOpts)).toBeInstanceOf(OP)
})
it(
'succeed from request opts when all params are set',
async () => {
const mockEntity = await mockedGetEnterpriseAuthToken('ACME Corp')
const requestOpts: CreateAuthorizationRequestOpts = {
version: SupportedVersion.SIOPv2_ID1,
requestObject: {
jwtIssuer: {
method: 'did',
didUrl: `${mockEntity.did}#controller`,
alg: SigningAlgo.ES256K,
options: {
kid: '1234',
},
},
passBy: PassBy.REFERENCE,
reference_uri: EXAMPLE_REFERENCE_URL,
createJwtCallback: getCreateJwtCallback({
hexPrivateKey: mockEntity.hexPrivateKey,
did: mockEntity.did,
kid: `${mockEntity.did}#controller`,
alg: SigningAlgo.ES256K,
}),
payload: {
redirect_uri: EXAMPLE_REDIRECT_URL,
client_id: WELL_KNOWN_OPENID_FEDERATION,
scope: 'test',
response_type: 'id_token',
},
},
clientMetadata: {
client_id: WELL_KNOWN_OPENID_FEDERATION,
idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256],
subject_syntax_types_supported: ['did:ethr', SubjectIdentifierType.DID],
requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256],
responseTypesSupported: [ResponseType.ID_TOKEN],
scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID],
subjectTypesSupported: [SubjectType.PAIRWISE],
vpFormatsSupported: {
jwt_vc: { alg: [SigningAlgo.EDDSA, SigningAlgo.ES256K, SigningAlgo.ES256] },
jwt_vp: { alg: [SigningAlgo.EDDSA, SigningAlgo.ES256K, SigningAlgo.ES256] },
jwt: { alg: [SigningAlgo.EDDSA, SigningAlgo.ES256K, SigningAlgo.ES256] },
},
passBy: PassBy.VALUE,
logo_uri: VERIFIER_LOGO_FOR_CLIENT,
clientName: VERIFIER_NAME_FOR_CLIENT,
'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100334',
clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY,
'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL,
},
}
const requestURI = await RP.fromRequestOpts(requestOpts).createAuthorizationRequestURI({
correlationId: '1234',
nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg',
state: 'b32f0087fc9816eb813fd11f',
jwtIssuer: { method: 'did', didUrl: `${mockEntity.did}#controller`, alg: SigningAlgo.ES256K, options: { kid: '1234' } },
})
nock('https://rp.acme.com').get('/siop/jwts').reply(200, requestURI.requestObjectJwt)
try {
const verifiedRequest = await OP.fromOpts(responseOpts, verifyOpts).verifyAuthorizationRequest(requestURI.encodedUri)
// console.log(JSON.stringify(verifiedRequest));
expect(verifiedRequest.issuer).toMatch(mockEntity.did)
expect(verifiedRequest.jwt).toBeDefined()
} catch (e) {
if (e.message.includes('Service Unavailable')) {
console.warn('Temporarily skipped due to Service Unavailable')
} else {
throw e
}
}
},
UNIT_TEST_TIMEOUT,
)
it('succeed from builder when all params are set', async () => {
try {
const rpMockEntity = await mockedGetEnterpriseAuthToken('ACME RP')
const opMockEntity = await mockedGetEnterpriseAuthToken('ACME OP')
const requestURI = await RP.builder({ requestVersion: SupportedVersion.SIOPv2_ID1 })
.withClientId(WELL_KNOWN_OPENID_FEDERATION)
.withScope('test')
.withResponseType(ResponseType.ID_TOKEN)
.withAuthorizationEndpoint('www.myauthorizationendpoint.com')
.withRedirectUri(EXAMPLE_REFERENCE_URL)
.withVerifyJwtCallback(getVerifyJwtCallback(resolver))
.withRequestBy(PassBy.VALUE)
.withCreateJwtCallback(
getCreateJwtCallback({
hexPrivateKey: rpMockEntity.hexPrivateKey,
did: rpMockEntity.did,
kid: `${rpMockEntity.did}#controller`,
alg: SigningAlgo.ES256K,
}),
)
.withClientMetadata({
client_id: WELL_KNOWN_OPENID_FEDERATION,
idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA],
requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256],
responseTypesSupported: [ResponseType.ID_TOKEN],
vpFormatsSupported: { jwt_vc: { alg: [SigningAlgo.EDDSA] } },
scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID],
subjectTypesSupported: [SubjectType.PAIRWISE],
subject_syntax_types_supported: ['did', 'did:ethr'],
passBy: PassBy.VALUE,
logo_uri: VERIFIER_LOGO_FOR_CLIENT,
clientName: VERIFIER_NAME_FOR_CLIENT,
'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100335',
clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY,
'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL,
})
.build()
.createAuthorizationRequestURI({
correlationId: '1234',
nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg',
state: 'b32f0087fc9816eb813fd11f',
jwtIssuer: { method: 'did', didUrl: `${rpMockEntity.did}#controller`, alg: SigningAlgo.ES256K },
})
const verifiedRequest = await OP.builder()
.withSupportedVersions([SupportedVersion.SIOPv2_ID1])
.withExpiresIn(1000)
.withIssuer(ResponseIss.SELF_ISSUED_V2)
.withVerifyJwtCallback(getVerifyJwtCallback(resolver, { checkLinkedDomain: 'never' }))
.withCreateJwtCallback(
getCreateJwtCallback({
hexPrivateKey: opMockEntity.hexPrivateKey,
did: opMockEntity.did,
kid: `${opMockEntity.did}#controller`,
alg: SigningAlgo.ES256K,
}),
)
.withRegistration({
idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA],
requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256],
responseTypesSupported: [ResponseType.ID_TOKEN],
vpFormats: { ldp_vc: { proof_type: [IProofType.EcdsaSecp256k1Signature2019, IProofType.EcdsaSecp256k1Signature2019] } },
scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID],
subjectTypesSupported: [SubjectType.PAIRWISE],
subject_syntax_types_supported: ['did', 'did:ethr'],
passBy: PassBy.VALUE,
logo_uri: VERIFIER_LOGO_FOR_CLIENT,
clientName: VERIFIER_NAME_FOR_CLIENT,
'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100336',
clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY,
'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL,
})
.build()
.verifyAuthorizationRequest(requestURI.encodedUri)
// console.log(JSON.stringify(verifiedRequest));
expect(verifiedRequest.issuer).toMatch(rpMockEntity.did)
expect(verifiedRequest.jwt).toBeDefined()
} catch (e) {
if (e.message.includes('Service Unavailable')) {
console.warn('Temporarily skipped due to Service Unavailable')
} else {
throw e
}
}
})
})