Skip to content

Commit

Permalink
fix: simplify expected Resolver type in verify methods (#159)
Browse files Browse the repository at this point in the history
fixes #158
  • Loading branch information
mirceanis authored Mar 25, 2021
1 parent 2f36154 commit 969de89
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 42 deletions.
6 changes: 4 additions & 2 deletions src/JWT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ export interface JWTOptions {
expiresIn?: number
}

export type Public<T> = { [P in keyof T]: T[P] }

export interface JWTVerifyOptions {
/** @deprecated Please use `proofPurpose: 'authentication' instead` */
auth?: boolean
audience?: string
callbackUrl?: string
resolver?: Resolver
resolver?: Public<Resolver>
skewTime?: number
/** See https://www.w3.org/TR/did-spec-registries/#verification-relationships */
proofPurpose?: 'authentication' | 'assertionMethod' | 'capabilityDelegation' | 'capabilityInvocation' | string
Expand Down Expand Up @@ -348,7 +350,7 @@ export async function verifyJWT(
* @return {Promise<DIDAuthenticator>} a promise which resolves with a response object containing an array of authenticators or if non exist rejects with an error
*/
export async function resolveAuthenticator(
resolver: Resolver,
resolver: Public<Resolver>,
alg: string,
issuer: string,
proofPurpose?: string
Expand Down
57 changes: 20 additions & 37 deletions src/__tests__/JWT.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ describe('createJWT()', () => {
})

describe('verifyJWT()', () => {
const resolver = ({ resolve: jest.fn().mockReturnValue(didDoc) } as unknown) as Resolver
const resolver = { resolve: jest.fn().mockReturnValue(didDoc) }

describe('pregenerated JWT', () => {
// tslint:disable-next-line: max-line-length
Expand Down Expand Up @@ -249,7 +249,7 @@ describe('verifyJWT()', () => {
// tslint:disable-next-line: max-line-length
const incomingJwt =
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE0ODUzMjExMzMsImlzcyI6ImRpZDpldGhyOjB4OTBlNDVkNzViZDEyNDZlMDkyNDg3MjAxODY0N2RiYTk5NmE4ZTdiOSIsInJlcXVlc3RlZCI6WyJuYW1lIiwicGhvbmUiXX0.KIG2zUO8Quf3ucb9jIncZ1CmH0v-fAZlsKvesfsd9x4RzU0qrvinVd9d30DOeZOwdwEdXkET_wuPoOECwU0IKA'
const legacyResolver = ({ resolve: jest.fn().mockReturnValue(didDocLegacy) } as unknown) as Resolver
const legacyResolver = { resolve: jest.fn().mockReturnValue(didDocLegacy) }

it('verifies the JWT and return correct payload', async () => {
expect.assertions(1)
Expand Down Expand Up @@ -370,7 +370,7 @@ describe('verifyJWT()', () => {

it('handles ES256K algorithm with ethereum address - github #14', async () => {
expect.assertions(1)
const ethResolver = ({
const ethResolver = {
resolve: jest.fn().mockReturnValue({
didDocument: {
id: did,
Expand All @@ -384,15 +384,15 @@ describe('verifyJWT()', () => {
]
}
})
} as unknown) as Resolver
}
const jwt = await createJWT({ hello: 'world' }, { issuer: aud, signer, alg: 'ES256K' })
const { payload } = await verifyJWT(jwt, { resolver: ethResolver })
return expect(payload).toMatchSnapshot()
})

it('handles ES256K algorithm with blockchainAccountId - github #14, #155', async () => {
expect.assertions(1)
const ethResolver = ({
const ethResolver = {
resolve: jest.fn().mockReturnValue({
didDocument: {
id: did,
Expand All @@ -406,7 +406,7 @@ describe('verifyJWT()', () => {
]
}
})
} as unknown) as Resolver
}
const jwt = await createJWT({ hello: 'world' }, { issuer: aud, signer, alg: 'ES256K' })
const { payload } = await verifyJWT(jwt, { resolver: ethResolver })
return expect(payload).toMatchSnapshot()
Expand Down Expand Up @@ -664,11 +664,7 @@ describe('resolveAuthenticator()', () => {
describe('ES256K', () => {
it('finds public key', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(singleKey) } as unknown) as Resolver,
alg,
did
)
const authenticators = await resolveAuthenticator({ resolve: jest.fn().mockReturnValue(singleKey) }, alg, did)
return expect(authenticators).toEqual({
authenticators: [ecKey1],
issuer: did,
Expand All @@ -679,7 +675,7 @@ describe('resolveAuthenticator()', () => {
it('filters out irrelevant public keys', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) } as unknown) as Resolver,
{ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) },
alg,
did
)
Expand All @@ -693,7 +689,7 @@ describe('resolveAuthenticator()', () => {
it('only list authenticators able to authenticate a user', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) } as unknown) as Resolver,
{ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) },
alg,
did,
'authentication'
Expand All @@ -708,7 +704,7 @@ describe('resolveAuthenticator()', () => {
it('lists authenticators with multiple key types in doc', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(multipleAuthTypes) } as unknown) as Resolver,
{ resolve: jest.fn().mockReturnValue(multipleAuthTypes) },
alg,
did,
'authentication'
Expand All @@ -723,11 +719,7 @@ describe('resolveAuthenticator()', () => {
it('errors if no suitable public keys exist', async () => {
expect.assertions(1)
return await expect(
resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(unsupportedFormat) } as unknown) as Resolver,
alg,
did
)
resolveAuthenticator({ resolve: jest.fn().mockReturnValue(unsupportedFormat) }, alg, did)
).rejects.toThrowError(`DID document for ${did} does not have public keys for ${alg}`)
})
})
Expand All @@ -737,7 +729,7 @@ describe('resolveAuthenticator()', () => {
it('filters out irrelevant public keys', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) } as unknown) as Resolver,
{ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) },
alg,
did
)
Expand All @@ -751,7 +743,7 @@ describe('resolveAuthenticator()', () => {
it('only list authenticators able to authenticate a user', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) } as unknown) as Resolver,
{ resolve: jest.fn().mockReturnValue(multipleKeysLegacy) },
alg,
did,
'authentication'
Expand All @@ -766,7 +758,7 @@ describe('resolveAuthenticator()', () => {
it('lists authenticators with multiple key types in doc', async () => {
expect.assertions(1)
const authenticators = await resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(multipleAuthTypes) } as unknown) as Resolver,
{ resolve: jest.fn().mockReturnValue(multipleAuthTypes) },
alg,
did,
'authentication'
Expand All @@ -781,24 +773,15 @@ describe('resolveAuthenticator()', () => {
it('errors if no suitable public keys exist', async () => {
expect.assertions(1)
return await expect(
resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(unsupportedFormat) } as unknown) as Resolver,
alg,
did
)
resolveAuthenticator({ resolve: jest.fn().mockReturnValue(unsupportedFormat) }, alg, did)
).rejects.toThrowError(`DID document for ${did} does not have public keys for ${alg}`)
})
})

it('errors if no suitable public keys exist for authentication', async () => {
expect.assertions(1)
return await expect(
resolveAuthenticator(
({ resolve: jest.fn().mockReturnValue(singleKey) } as unknown) as Resolver,
alg,
did,
'authentication'
)
resolveAuthenticator({ resolve: jest.fn().mockReturnValue(singleKey) }, alg, did, 'authentication')
).rejects.toThrowError(
`DID document for ${did} does not have public keys suitable for ES256K with authentication purpose`
)
Expand All @@ -807,20 +790,20 @@ describe('resolveAuthenticator()', () => {
it('errors if no public keys exist', async () => {
expect.assertions(1)
return await expect(
resolveAuthenticator(({ resolve: jest.fn().mockReturnValue(noPublicKey) } as unknown) as Resolver, alg, did)
resolveAuthenticator({ resolve: jest.fn().mockReturnValue(noPublicKey) }, alg, did)
).rejects.toThrowError(`DID document for ${did} does not have public keys for ${alg}`)
})

it('errors if no DID document exists', async () => {
expect.assertions(1)
return await expect(
resolveAuthenticator(
({
{
resolve: jest.fn().mockReturnValue({
didResolutionMetadata: { error: 'notFound' },
didDocument: null
})
} as unknown) as Resolver,
},
alg,
did
)
Expand All @@ -830,7 +813,7 @@ describe('resolveAuthenticator()', () => {
it('errors if no supported signature types exist', async () => {
expect.assertions(1)
return await expect(
resolveAuthenticator(({ resolve: jest.fn().mockReturnValue(singleKey) } as unknown) as Resolver, 'ESBAD', did)
resolveAuthenticator({ resolve: jest.fn().mockReturnValue(singleKey) }, 'ESBAD', did)
).rejects.toThrowError('No supported signature types for algorithm ESBAD')
})
})
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/didkey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('Ed25519', () => {

it('handles EdDSA algorithm with did:key', async () => {
expect.assertions(1)
const resolver = ({
const resolver = {
resolve: async () => ({
didResolutionMetadata: {},
didDocumentMetadata: {},
Expand All @@ -40,7 +40,7 @@ describe('Ed25519', () => {
]
}
})
} as unknown) as Resolver
}
const jwt =
'eyJhbGciOiJFZERTQSJ9.eyJleHAiOjE3NjQ4Nzg5MDgsImlzcyI6ImRpZDprZXk6ejZNa29USHNnTk5yYnk4SnpDTlExaVJMeVc1UVE2UjhYdXU2QUE4aWdHck1WUFVNIiwibmJmIjoxNjA3MTEyNTA4LCJzdWIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9pZGVudGl0eS5mb3VuZGF0aW9uLy53ZWxsLWtub3duL2RpZC1jb25maWd1cmF0aW9uL3YxIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1rb1RIc2dOTnJieThKekNOUTFpUkx5VzVRUTZSOFh1dTZBQThpZ0dyTVZQVU0iLCJvcmlnaW4iOiJpZGVudGl0eS5mb3VuZGF0aW9uIn0sImV4cGlyYXRpb25EYXRlIjoiMjAyNS0xMi0wNFQxNDowODoyOC0wNjowMCIsImlzc3VhbmNlRGF0ZSI6IjIwMjAtMTItMDRUMTQ6MDg6MjgtMDY6MDAiLCJpc3N1ZXIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJEb21haW5MaW5rYWdlQ3JlZGVudGlhbCJdfX0.6ovgQ-T_rmYueviySqXhzMzgqJMAizOGUKAObQr2iikoRNsb8DHfna4rh1puwWqYwgT3QJVpzdO_xZARAYM9Dw'
const { payload } = await verifyJWT(jwt, { resolver })
Expand Down
3 changes: 2 additions & 1 deletion src/xc20pEncryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { concatKDF } from './Digest'
import { bytesToBase64url, base58ToBytes, encodeBase64url, toSealed, base64ToBytes } from './util'
import { Recipient, EncryptionResult, Encrypter, Decrypter } from './JWE'
import type { VerificationMethod, Resolver } from 'did-resolver'
import { Public } from './JWT'

function xc20pEncrypter(key: Uint8Array): (cleartext: Uint8Array, aad?: Uint8Array) => EncryptionResult {
const cipher = new XChaCha20Poly1305(key)
Expand Down Expand Up @@ -78,7 +79,7 @@ export function x25519Encrypter(publicKey: Uint8Array, kid?: string): Encrypter
return { alg, enc: 'XC20P', encrypt, encryptCek }
}

export async function resolveX25519Encrypters(dids: string[], resolver: Resolver): Promise<Encrypter[]> {
export async function resolveX25519Encrypters(dids: string[], resolver: Public<Resolver>): Promise<Encrypter[]> {
return Promise.all(
dids.map(async (did) => {
const { didResolutionMetadata, didDocument } = await resolver.resolve(did)
Expand Down

0 comments on commit 969de89

Please sign in to comment.