Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/Sphereon-Opensource/OID4VC
Browse files Browse the repository at this point in the history
… into feature/SPRIND-116

# Conflicts:
#	pnpm-lock.yaml
  • Loading branch information
Zoe Maas committed Nov 22, 2024
2 parents 7089350 + 7be2c0d commit 60f5f3c
Show file tree
Hide file tree
Showing 27 changed files with 144 additions and 898 deletions.
10 changes: 6 additions & 4 deletions packages/callback-example/lib/__tests__/issuerCallback.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ import {
ProofOfPossession,
} from '@sphereon/oid4vci-common'
import { CredentialOfferSession } from '@sphereon/oid4vci-common'
import { CredentialSupportedBuilderV1_13, VcIssuer, VcIssuerBuilder } from '@sphereon/oid4vci-issuer'
import {
AuthorizationServerMetadataBuilder,
CredentialSupportedBuilderV1_13,
VcIssuer,
VcIssuerBuilder
} from '@sphereon/oid4vci-issuer'
import { MemoryStates } from '@sphereon/oid4vci-issuer'
import { CredentialDataSupplierResult } from '@sphereon/oid4vci-issuer/dist/types'
import { ICredential, IProofPurpose, IProofType, W3CVerifiableCredential } from '@sphereon/ssi-types'
import { DIDDocument } from 'did-resolver'
import * as jose from 'jose'

import { generateDid, getIssuerCallbackV1_0_11, getIssuerCallbackV1_0_13, verifyCredential } from '../IssuerCallback'
import {
AuthorizationServerMetadataBuilder
} from '@sphereon/oid4vci-issuer/dist/builder/AuthorizationServerMetadataBuilder'

const INITIATION_TEST_URI =
'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22:%22https://credential-issuer.example.com%22,%22credential_configuration_ids%22:%5B%22UniversityDegreeCredential%22%5D,%22grants%22:%7B%22urn:ietf:params:oauth:grant-type:pre-authorized_code%22:%7B%22pre-authorized_code%22:%22oaKazRN8I0IbtZ0C7JuMn5%22,%22tx_code%22:%7B%22input_mode%22:%22text%22,%22description%22:%22Please%20enter%20the%20serial%20number%20of%20your%20physical%20drivers%20license%22%7D%7D%7D%7D'
Expand Down
2 changes: 1 addition & 1 deletion packages/callback-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@sphereon/oid4vci-client": "workspace:*",
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/oid4vci-issuer": "workspace:*",
"@sphereon/ssi-types": "0.30.1",
"@sphereon/ssi-types": "0.30.2-next.279",
"jose": "^4.10.0"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/client/lib/MetadataClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export class MetadataClient {
: (authMetadata as CredentialIssuerMetadataV1_0_13);
}
debug(`Issuer ${issuer} token endpoint ${token_endpoint}, credential endpoint ${credential_endpoint}`);

return {
issuer,
token_endpoint,
Expand Down
8 changes: 6 additions & 2 deletions packages/client/lib/__tests__/SdJwt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import {
import nock from 'nock';

import { OpenID4VCIClientV1_0_13 } from '..';
import { createAccessTokenResponse, IssuerMetadataBuilderV1_13, VcIssuerBuilder } from '../../../issuer';
import { AuthorizationServerMetadataBuilder } from '../../../issuer/lib/builder/AuthorizationServerMetadataBuilder'
import {
AuthorizationServerMetadataBuilder,
createAccessTokenResponse,
IssuerMetadataBuilderV1_13,
VcIssuerBuilder
} from '../../../issuer'

export const UNIT_TEST_TIMEOUT = 30000;

Expand Down
2 changes: 1 addition & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"dependencies": {
"@sphereon/oid4vc-common": "workspace:*",
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/ssi-types": "0.30.1",
"@sphereon/ssi-types": "0.30.2-next.279",
"cross-fetch": "^3.1.8",
"debug": "^4.3.5"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build:clean": "tsc --build --clean && tsc --build"
},
"dependencies": {
"@sphereon/ssi-types": "0.30.1",
"@sphereon/ssi-types": "0.30.2-next.279",
"jwt-decode": "^4.0.0",
"sha.js": "^2.4.11",
"uint8arrays": "3.1.1",
Expand Down
6 changes: 3 additions & 3 deletions packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import {
PRE_AUTH_CODE_LITERAL,
PRE_AUTH_GRANT_LITERAL
} from '@sphereon/oid4vci-common'
import { VcIssuer } from '@sphereon/oid4vci-issuer/dist/VcIssuer'
import { CredentialSupportedBuilderV1_13, VcIssuerBuilder } from '@sphereon/oid4vci-issuer/dist/builder'
import {
AuthorizationServerMetadataBuilder
} from '@sphereon/oid4vci-issuer/dist/builder/AuthorizationServerMetadataBuilder'
} from '@sphereon/oid4vci-issuer'
import { VcIssuer } from '@sphereon/oid4vci-issuer/dist/VcIssuer'
import { CredentialSupportedBuilderV1_13, VcIssuerBuilder } from '@sphereon/oid4vci-issuer/dist/builder'
import { MemoryStates } from '@sphereon/oid4vci-issuer/dist/state-manager'
import { ExpressBuilder, ExpressSupport } from '@sphereon/ssi-express-support'
import { IProofPurpose, IProofType } from '@sphereon/ssi-types'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { VcIssuer } from '@sphereon/oid4vci-issuer'
import {
AuthorizationServerMetadataBuilder
} from '@sphereon/oid4vci-issuer/dist/builder/AuthorizationServerMetadataBuilder'
} from '@sphereon/oid4vci-issuer'
import { MemoryStates } from '@sphereon/oid4vci-issuer/dist/state-manager'
import { ExpressBuilder, ExpressSupport } from '@sphereon/ssi-express-support'
import { DIDDocument } from 'did-resolver'
Expand Down
4 changes: 2 additions & 2 deletions packages/issuer-rest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"@sphereon/oid4vc-common": "workspace:*",
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/oid4vci-issuer": "workspace:*",
"@sphereon/ssi-express-support": "0.30.1",
"@sphereon/ssi-types": "0.30.1",
"@sphereon/ssi-express-support": "0.30.2-next.279",
"@sphereon/ssi-types": "0.30.2-next.279",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
Expand Down
34 changes: 26 additions & 8 deletions packages/issuer/lib/VcIssuer.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { uuidv4 } from '@sphereon/oid4vc-common'
import {
ALG_ERROR,
AUD_ERROR, AuthorizationServerMetadata,
AUD_ERROR,
AuthorizationServerMetadata,
CNonceState,
CreateCredentialOfferURIResult,
CREDENTIAL_MISSING_ERROR,
CredentialConfigurationSupportedV1_0_13,
CredentialDataSupplierInput,
CredentialEventNames,
CredentialIssuerMetadata,
CredentialIssuerMetadataOptsV1_0_13,
CredentialOfferEventNames,
CredentialOfferSession,
CredentialOfferV1_0_13,
CredentialRequest,
CredentialRequestV1_0_13,
CredentialResponse,
DID_NO_DIDDOC_ERROR,
EVENTS,
IAT_ERROR,
ISSUER_CONFIG_ERROR,
IssueStatus,
Expand All @@ -35,13 +40,28 @@ import {
TYP_ERROR,
URIState
} from '@sphereon/oid4vci-common'
import { CredentialEventNames, CredentialOfferEventNames, EVENTS } from '@sphereon/oid4vci-common'
import { CredentialIssuerMetadataOptsV1_0_13 } from '@sphereon/oid4vci-common'
import { CompactSdJwtVc, CredentialMapper, InitiatorType, SubSystem, System, W3CVerifiableCredential } from '@sphereon/ssi-types'
import {
CompactSdJwtVc,
CredentialMapper,
InitiatorType,
SubSystem,
System,
W3CVerifiableCredential
} from '@sphereon/ssi-types'

import { assertValidPinNumber, createCredentialOfferObject, createCredentialOfferURIFromObject, CredentialOfferGrantInput } from './functions'
import {
assertValidPinNumber,
createCredentialOfferObject,
createCredentialOfferURIFromObject,
CredentialOfferGrantInput
} from './functions'
import { LookupStateManager } from './state-manager'
import { CredentialDataSupplier, CredentialDataSupplierArgs, CredentialIssuanceInput, CredentialSignerCallback } from './types'
import {
CredentialDataSupplier,
CredentialDataSupplierArgs,
CredentialIssuanceInput,
CredentialSignerCallback
} from './types'

export class VcIssuer<DIDDoc extends object> {
private readonly _issuerMetadata: CredentialIssuerMetadataOptsV1_0_13
Expand Down Expand Up @@ -667,6 +687,4 @@ export class VcIssuer<DIDDoc extends object> {
public get authorizationServerMetadata() {
return this._authorizationServerMetadata
}


}
3 changes: 1 addition & 2 deletions packages/issuer/lib/__tests__/VcIssuerBuilder.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { uuidv4 } from '@sphereon/oid4vc-common'
import { CredentialConfigurationSupportedV1_0_13, IssuerCredentialSubjectDisplay, IssueStatus, TokenErrorResponse } from '@sphereon/oid4vci-common'

import { AuthorizationServerMetadataBuilder } from '../builder/AuthorizationServerMetadataBuilder'
import { CredentialSupportedBuilderV1_13, VcIssuerBuilder } from '../index'
import { AuthorizationServerMetadataBuilder, CredentialSupportedBuilderV1_13, VcIssuerBuilder } from '../index'


const authorizationServerMetadata = new AuthorizationServerMetadataBuilder()
Expand Down
10 changes: 6 additions & 4 deletions packages/issuer/lib/builder/VcIssuerBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AuthorizationServerMetadata,
CNonceState,
CredentialConfigurationSupportedV1_0_13,
CredentialIssuerMetadataOptsV1_0_13,
CredentialOfferSession,
IssuerMetadata,
IssuerMetadataV1_0_13,
Expand All @@ -12,7 +13,6 @@ import {
TxCode,
URIState
} from '@sphereon/oid4vci-common'
import { CredentialIssuerMetadataOptsV1_0_13 } from '@sphereon/oid4vci-common'

import { VcIssuer } from '../VcIssuer'
import { MemoryStates } from '../state-manager'
Expand Down Expand Up @@ -52,7 +52,7 @@ export class VcIssuerBuilder<DIDDoc extends object> {
this.issuerMetadataBuilder = builder
return this
}

public withDefaultCredentialOfferBaseUri(baseUri: string) {
this.defaultCredentialOfferBaseUri = baseUri
return this
Expand Down Expand Up @@ -184,8 +184,10 @@ export class VcIssuerBuilder<DIDDoc extends object> {
if (!metadata.credential_endpoint || !metadata.credential_issuer || !this.issuerMetadata.credential_configurations_supported) {
throw new Error(TokenErrorResponse.invalid_request)
}
return new VcIssuer(metadata as IssuerMetadataV1_0_13, this.authorizationServerMetadata as AuthorizationServerMetadata, {
//TODO: discuss this with Niels. I did not find this in the spec. but I think we should somehow communicate this
return new VcIssuer(metadata as IssuerMetadataV1_0_13,
this.authorizationServerMetadata as AuthorizationServerMetadata,
{
//TODO: discuss this with Niels. I did not find this in the spec. but I think we should somehow communicate this
...(this.txCode && { txCode: this.txCode }),
defaultCredentialOfferBaseUri: this.defaultCredentialOfferBaseUri,
credentialSignerCallback: this.credentialSignerCallback,
Expand Down
1 change: 1 addition & 0 deletions packages/issuer/lib/builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './CredentialSupportedBuilderV1_13'
export * from './VcIssuerBuilder'
export * from './IssuerMetadataBuilderV1_13'
export * from './DisplayBuilder'
export * from './AuthorizationServerMetadataBuilder'
2 changes: 1 addition & 1 deletion packages/issuer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"dependencies": {
"@sphereon/oid4vc-common": "workspace:*",
"@sphereon/oid4vci-common": "workspace:*",
"@sphereon/ssi-types": "0.30.1",
"@sphereon/ssi-types": "0.30.2-next.279",
"uuid": "^9.0.0"
},
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/oid4vci-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@sphereon/oid4vc-common": "workspace:*",
"@sphereon/ssi-types": "0.30.1",
"@sphereon/ssi-types": "0.30.2-next.279",
"cross-fetch": "^3.1.8",
"debug": "^4.3.5",
"jwt-decode": "^4.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/siop-oid4vp/lib/__tests__/IT.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function getVCs(): IVerifiableCredential[] {
return vcs
}

describe('RP and OP interaction should', () => {
describe.skip('RP and OP interaction should', () => { // FIXME SDK-45 Uniresolver failing
it(
'succeed when calling each other in the full flow',
async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function getVCs(): OriginalVerifiableCredential[] {
return [SD_JWT_VC]
}

describe('RP and OP interaction should', () => {
describe.skip('RP and OP interaction should', () => { // FIXME SDK-45 Uniresolver failing
it('succeed when calling with presentation definitions and right verifiable presentation', async () => {
const opMock = await mockedGetEnterpriseAuthToken('OP')
const opMockEntity = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,12 @@ export class AuthorizationResponse {
let nonce: string | undefined = this._payload.nonce
if (this._payload?.vp_token) {
const presentations = this.payload.vp_token ? await extractPresentationsFromVpToken(this.payload.vp_token, opts) : []
if (!presentations || (Array.isArray(presentations) && presentations.length === 0)) {
return Promise.reject(Error('missing presentation(s)'))
}
const presentationsArray = Array.isArray(presentations) ? presentations : [presentations]


// We do not verify them, as that is done elsewhere. So we simply can take the first nonce
nonce = presentationsArray
// FIXME toWrappedVerifiablePresentation() does not extract the nonce yet from mdocs.
Expand Down
44 changes: 31 additions & 13 deletions packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ export const verifyPresentations = async (
authorizationResponse: AuthorizationResponse,
verifyOpts: VerifyAuthorizationResponseOpts,
): Promise<VerifiedOpenID4VPSubmission | null> => {
const presentations = authorizationResponse.payload.vp_token
? await extractPresentationsFromVpToken(authorizationResponse.payload.vp_token, { hasher: verifyOpts.hasher })
: []
if (!authorizationResponse.payload.vp_token || Array.isArray(authorizationResponse.payload.vp_token) && authorizationResponse.payload.vp_token.length === 0) {
return Promise.reject(Error('the payload is missing a vp_token'))
}

const presentations = await extractPresentationsFromVpToken(authorizationResponse.payload.vp_token, { hasher: verifyOpts.hasher })
const presentationDefinitions = verifyOpts.presentationDefinitions
? Array.isArray(verifyOpts.presentationDefinitions)
? verifyOpts.presentationDefinitions
Expand Down Expand Up @@ -103,8 +105,10 @@ export const verifyPresentations = async (
return null
}

if (!presentations || (Array.isArray(presentations) && presentations.length === 0)) {
return Promise.reject(Error('missing presentation(s)'))
}
const presentationsArray = Array.isArray(presentations) ? presentations : [presentations]

const presentationsWithoutMdoc = presentationsArray.filter((p) => p.format !== 'mso_mdoc')
const nonces = new Set(presentationsWithoutMdoc.map(extractNonceFromWrappedVerifiablePresentation))
if (presentationsWithoutMdoc.length > 0 && nonces.size !== 1) {
Expand Down Expand Up @@ -135,11 +139,14 @@ export const extractPresentationsFromVpToken = async (
vpToken: Array<W3CVerifiablePresentation | CompactSdJwtVc | string> | W3CVerifiablePresentation | CompactSdJwtVc | string,
opts?: { hasher?: Hasher },
): Promise<WrappedVerifiablePresentation[] | WrappedVerifiablePresentation> => {
if (Array.isArray(vpToken)) {
return vpToken.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: opts?.hasher }))
const tokens = Array.isArray(vpToken) ? vpToken : [vpToken];
const wrappedTokens = tokens.map(vp =>
CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: opts?.hasher })
);

return tokens.length === 1 ? wrappedTokens[0] : wrappedTokens;
}
return CredentialMapper.toWrappedVerifiablePresentation(vpToken, { hasher: opts?.hasher })
}

export const createPresentationSubmission = async (
verifiablePresentations: W3CVerifiablePresentation[],
opts?: { presentationDefinitions: (PresentationDefinitionWithLocation | IPresentationDefinition)[] },
Expand Down Expand Up @@ -278,8 +285,19 @@ export const assertValidVerifiablePresentations = async (args: {
presentationSubmission?: PresentationSubmission
hasher?: Hasher
}
}) => {
const presentationsArray = Array.isArray(args.presentations) ? args.presentations : [args.presentations]
}) : Promise<void> => {
const {presentations} = args
if (!presentations || (Array.isArray(presentations) && presentations.length === 0)) {
return Promise.reject(Error('missing presentation(s)'))
}

// Handle mdocs, keep them out of pex
let presentationsArray = (Array.isArray(presentations) ? presentations : [presentations])
if (presentationsArray.every(p => p.format === 'mso_mdoc')) {
return
}
presentationsArray = presentationsArray.filter((p) => p.format !== 'mso_mdoc')

if (
(!args.presentationDefinitions || args.presentationDefinitions.filter((a) => a.definition).length === 0) &&
(!presentationsArray || (Array.isArray(presentationsArray) && presentationsArray.filter((vp) => vp.presentation).length === 0))
Expand All @@ -293,15 +311,15 @@ export const assertValidVerifiablePresentations = async (args: {
args.presentationDefinitions.length &&
(!presentationsArray || (Array.isArray(presentationsArray) && presentationsArray.length === 0))
) {
throw new Error(SIOPErrors.AUTH_REQUEST_EXPECTS_VP)
return Promise.reject(Error(SIOPErrors.AUTH_REQUEST_EXPECTS_VP))
} else if (
(!args.presentationDefinitions || args.presentationDefinitions.length === 0) &&
presentationsArray &&
((Array.isArray(presentationsArray) && presentationsArray.length > 0) || !Array.isArray(presentationsArray))
) {
throw new Error(SIOPErrors.AUTH_REQUEST_DOESNT_EXPECT_VP)
return Promise.reject(Error(SIOPErrors.AUTH_REQUEST_DOESNT_EXPECT_VP))
} else if (args.presentationDefinitions && !args.opts.presentationSubmission) {
throw new Error(`No presentation submission present. Please use presentationSubmission opt argument!`)
return Promise.reject(Error(`No presentation submission present. Please use presentationSubmission opt argument!`))
} else if (args.presentationDefinitions && presentationsArray) {
await PresentationExchange.validatePresentationsAgainstDefinitions(
args.presentationDefinitions,
Expand Down
3 changes: 2 additions & 1 deletion packages/siop-oid4vp/lib/request-object/Payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ export const createRequestObjectPayload = async (opts: CreateAuthorizationReques
response_type: payload.response_type ?? ResponseType.ID_TOKEN,
scope: payload.scope,
//TODO implement /.well-known/openid-federation support in the OP side to resolve the client_id (URL) and retrieve the metadata
client_id: clientId,
client_id_scheme: payload.client_id_scheme,
...(clientId && { client_id: clientId }),
...(payload.entity_id && { entity_id: payload.entity_id }),
...(payload.redirect_uri && { redirect_uri: payload.redirect_uri }),
...(payload.response_uri && { response_uri: payload.response_uri }),
response_mode: payload.response_mode ?? ResponseMode.DIRECT_POST,
Expand Down
9 changes: 9 additions & 0 deletions packages/siop-oid4vp/lib/rp/RPBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class RPBuilder {

clientMetadata?: ClientMetadataOpts = undefined
clientId: string
entityId: string
clientIdScheme: string

hasher: Hasher
Expand Down Expand Up @@ -83,6 +84,14 @@ export class RPBuilder {
return this
}

withEntityId(entityId: string, targets?: PropertyTargets): RPBuilder {
this._authorizationRequestPayload.entity_id = assignIfAuth({ propertyValue: entityId, targets }, false)
this._requestObjectPayload.entity_id = assignIfRequestObject({ propertyValue: entityId, targets }, true)
this.entityId = entityId
return this
}


withIssuer(issuer: ResponseIss, targets?: PropertyTargets): RPBuilder {
this._authorizationRequestPayload.iss = assignIfAuth({ propertyValue: issuer, targets }, false)
this._requestObjectPayload.iss = assignIfRequestObject({ propertyValue: issuer, targets }, true)
Expand Down
Loading

0 comments on commit 60f5f3c

Please sign in to comment.