Skip to content

Commit

Permalink
feat: dcql support
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Auer <martin.auer97@gmail.com>
  • Loading branch information
auer-martin committed Feb 4, 2025
1 parent 8b84e2c commit 3413eb6
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 16 deletions.
2 changes: 1 addition & 1 deletion packages/oauth2/src/common/jwt/decode-jwt-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@openid4vc/utils'
import { Oauth2JwtParseError } from '../../error/Oauth2JwtParseError'
import type { InferSchemaOutput } from './decode-jwt'
import { vJwtHeader } from './v-jwt.js'
import { vJwtHeader } from './v-jwt'

export interface DecodeJwtHeaderOptions<HeaderSchema extends BaseSchema | undefined> {
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/oauth2/src/common/jwt/decode-jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
stringToJsonWithErrorHandling,
} from '@openid4vc/utils'
import { Oauth2JwtParseError } from '../../error/Oauth2JwtParseError'
import { decodeJwtHeader } from './decode-jwt-header.js'
import { decodeJwtHeader } from './decode-jwt-header'
import { type JwtSigner, type vJwtHeader, vJwtPayload } from './v-jwt'

export interface DecodeJwtOptions<
Expand Down Expand Up @@ -61,7 +61,7 @@ export function decodeJwt<
throw new Oauth2JwtParseError('Error parsing JWT')
}

const { header } = decodeJwtHeader({ jwe: jwtParts[0], headerSchema: options.headerSchema })
const { header } = decodeJwtHeader({ jwe: options.jwt, headerSchema: options.headerSchema })
const payload = parseWithErrorHandling(options.payloadSchema ?? vJwtPayload, payloadJson)

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Oauth2Error } from '@openid4vc/oauth2'
import type { CallbackContext } from '../../../oauth2/src/callbacks'
import type { verifyJarRequest } from '../jar/handle-jar-request/verify-jar-request.js'
import type { verifyJarRequest } from '../jar/handle-jar-request/verify-jar-request'
import type { ClientMetadata } from '../models/v-client-metadata'
import type { Openid4vpAuthRequest } from '../openid4vp-auth-request/v-openid4vp-auth-request'
import { type ClientIdScheme, vClientIdScheme } from './v-client-id-scheme'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { CallbackContext } from '@openid4vc/oauth2'
import * as v from 'valibot'
import { parseClientIdentifier } from '../client-identifier-scheme/parse-client-identifier-scheme'
import { verifyJarRequest } from '../jar/handle-jar-request/verify-jar-request.js'
import { verifyJarRequest } from '../jar/handle-jar-request/verify-jar-request'
import { type JarAuthRequest, vJarAuthRequest } from '../jar/v-jar-auth-request'
import type { WalletMetadata } from '../models/v-wallet-metadata'
import { parseTransactionData } from '../transaction-data/parse-transaction-data'
Expand Down Expand Up @@ -41,6 +41,12 @@ export async function verifyOpenid4vpAuthRequest(
}
| undefined

let dcql:
| {
query: unknown
}
| undefined

if (authRequestParams.presentation_definition || authRequestParams.presentation_definition_uri) {
if (authRequestParams.presentation_definition_uri) {
throw new Error('presentation_definition_uri is not supported')
Expand All @@ -51,6 +57,12 @@ export async function verifyOpenid4vpAuthRequest(
}
}

if (authRequestParams.dcql_query) {
dcql = {
query: authRequestParams.dcql_query,
}
}

const transactionData = authRequestParams.transaction_data
? parseTransactionData(authRequestParams.transaction_data)
: undefined
Expand All @@ -61,6 +73,7 @@ export async function verifyOpenid4vpAuthRequest(
jar,
client: { ...clientMeta },
pex,
dcql,
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type CallbackContext, type JwtSigner, Oauth2Error } from '@openid4vc/oauth2'
import { createJarmAuthResponse } from '../jarm/jarm-auth-response-create'
import { extractJwksFromClientMetadata } from '../jarm/jarm-extract-jwks'
import { jarmAssertMetadataSupported } from '../jarm/metadata/jarm-assert-metadata-supported.js'
import { jarmAssertMetadataSupported } from '../jarm/metadata/jarm-assert-metadata-supported'
import type { JarmServerMetadata } from '../jarm/metadata/v-jarm-as-metadata'
import type { Openid4vpAuthRequest } from '../openid4vp-auth-request/v-openid4vp-auth-request'
import type { Openid4vpAuthResponse } from './v-openid4vp-auth-response'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface VerifyOpenid4VpPexAuthorizationResponseResult {
export interface VerifyOpenid4VpDcqlAuthorizationResponseResult {
type: 'dcql'
dcql: {
presentation: VpTokenPresentationParseResult
presentation: Record<string, VpTokenPresentationParseResult>
} & ({ scope: string; query?: never } | { scope?: never; query: unknown })
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Oauth2Error } from '@openid4vc/oauth2'
import type { Openid4vpAuthRequest } from '../openid4vp-auth-request/v-openid4vp-auth-request'
import {
parseDcqlPresentationFromVpToken,
parsePresentationsFromVpToken,
parseSinglePresentationsFromVpToken,
} from '../vp-token/parse-presentations-from-vp-token'
import type { Openid4vpAuthResponse } from './v-openid4vp-auth-response'
import type { VerifyOpenid4VpAuthorizationResponseResult } from './verify-openid4vp-auth-response-result'
Expand Down Expand Up @@ -70,17 +70,12 @@ export function verifyOpenid4vpAuthorizationResponse(options: {
)
}

if (typeof responseParams.vp_token === 'string') {
if (typeof responseParams.vp_token !== 'string') {
throw new Oauth2Error('If DCQL was used the vp_token must be a JSON-encoded object.')
}

// TODO: ENABLE THIS CHECK ALL THE TIME ONCE WE KNOW HOW TO GET THE NONCE FOR MDOCS AND ANONCREDS
const presentation = parseSinglePresentationsFromVpToken({ vpToken: responseParams.vp_token, path: '$' })
if (presentation.nonce && requestParams.nonce !== presentation.nonce) {
throw new Oauth2Error(
'Presentation nonce mismatch. The nonce of the presentation does not match the nonce of the request.'
)
}
const presentation = parseDcqlPresentationFromVpToken({ vpToken: responseParams.vp_token, path: '$' })
// TODO: CHECK ALL THE NONCES ONCE WE KNOW HOW TO GET THE NONCE FOR MDOCS AND ANONCREDS

return {
type: 'dcql',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ export function parsePresentationsFromVpToken(options: { vpToken: VpToken }): [
)
}

export function parseDcqlPresentationFromVpToken(options: {
vpToken: unknown
path: string
}) {
const { vpToken: _vpToken } = options

const vpToken = parseIfJson(_vpToken)
if (!v.is(v.looseObject({}), vpToken)) {
throw new Oauth2Error(`Could not parse vp_token. Expected a JSON object. Received: ${typeof vpToken}`)
}

const dcqlPresentationRecord = Object.fromEntries(
Object.entries(vpToken).map(([key, value]) => {
return [key, parseSinglePresentationsFromVpToken({ vpToken: value, path: '$' })]
})
)

return dcqlPresentationRecord
}

export function parseSinglePresentationsFromVpToken(options: {
vpToken: unknown
path: string
Expand Down

0 comments on commit 3413eb6

Please sign in to comment.