@animo-id/eudi-wallet-functionality 0.0.0-alpha-20250603091506 → 0.0.0-alpha-20250603100722
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentContext } from '@credo-ts/core';
|
|
1
|
+
import { AgentContext, X509Certificate } from '@credo-ts/core';
|
|
2
2
|
import { OpenId4VpResolvedAuthorizationRequest } from '@credo-ts/openid4vc';
|
|
3
3
|
|
|
4
4
|
type VerifyAuthorizationRequestOptions = {
|
|
@@ -9,6 +9,7 @@ type VerifyAuthorizationRequestOptions = {
|
|
|
9
9
|
declare const verifyOpenid4VpAuthorizationRequest: (agentContext: AgentContext, { resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql }, trustedCertificates, allowUntrustedSigned, }: VerifyAuthorizationRequestOptions) => Promise<{
|
|
10
10
|
isValidButUntrusted: boolean;
|
|
11
11
|
isValidAndTrusted: boolean;
|
|
12
|
+
x509RegistrationCertificate: X509Certificate;
|
|
12
13
|
}[] | undefined>;
|
|
13
14
|
|
|
14
15
|
export { verifyOpenid4VpAuthorizationRequest };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentContext } from '@credo-ts/core';
|
|
1
|
+
import { AgentContext, X509Certificate } from '@credo-ts/core';
|
|
2
2
|
import { OpenId4VpResolvedAuthorizationRequest } from '@credo-ts/openid4vc';
|
|
3
3
|
|
|
4
4
|
type VerifyAuthorizationRequestOptions = {
|
|
@@ -9,6 +9,7 @@ type VerifyAuthorizationRequestOptions = {
|
|
|
9
9
|
declare const verifyOpenid4VpAuthorizationRequest: (agentContext: AgentContext, { resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql }, trustedCertificates, allowUntrustedSigned, }: VerifyAuthorizationRequestOptions) => Promise<{
|
|
10
10
|
isValidButUntrusted: boolean;
|
|
11
11
|
isValidAndTrusted: boolean;
|
|
12
|
+
x509RegistrationCertificate: X509Certificate;
|
|
12
13
|
}[] | undefined>;
|
|
13
14
|
|
|
14
15
|
export { verifyOpenid4VpAuthorizationRequest };
|
package/dist/index.js
CHANGED
|
@@ -230,7 +230,7 @@ var verifyOpenid4VpAuthorizationRequest = async (agentContext, {
|
|
|
230
230
|
"DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate"
|
|
231
231
|
);
|
|
232
232
|
}
|
|
233
|
-
results.push({ isValidButUntrusted, isValidAndTrusted });
|
|
233
|
+
results.push({ isValidButUntrusted, isValidAndTrusted, x509RegistrationCertificate: rpCert });
|
|
234
234
|
} else {
|
|
235
235
|
throw new Error(`only format of 'jwt' is supported`);
|
|
236
236
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/verifyOpenid4VpAuthorizationRequest.ts","../src/isDcqlQueryEqualOrSubset.ts"],"sourcesContent":["export { verifyOpenid4VpAuthorizationRequest } from './verifyOpenid4VpAuthorizationRequest'\n","import { allowedNodeEnvironmentFlags } from 'node:process'\nimport { type AgentContext, type DcqlQuery, JwsService, Jwt, X509Certificate } from '@credo-ts/core'\nimport type { OpenId4VpResolvedAuthorizationRequest } from '@credo-ts/openid4vc'\nimport z from 'zod'\nimport { isDcqlQueryEqualOrSubset } from './isDcqlQueryEqualOrSubset'\n\nexport type VerifyAuthorizationRequestOptions = {\n resolvedAuthorizationRequest: OpenId4VpResolvedAuthorizationRequest\n trustedCertificates?: Array<string>\n allowUntrustedSigned?: boolean\n}\n\nexport const verifyOpenid4VpAuthorizationRequest = async (\n agentContext: AgentContext,\n {\n resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql },\n trustedCertificates,\n allowUntrustedSigned,\n }: VerifyAuthorizationRequestOptions\n) => {\n const results = []\n if (!authorizationRequestPayload.verifier_attestations) return\n for (const va of authorizationRequestPayload.verifier_attestations) {\n // Here we verify it as a registration certificate according to\n // https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/flows/Wallet-Relying-Party-Authentication/#registration-certificate\n if (va.format === 'jwt') {\n if (typeof va.data !== 'string') {\n throw new Error('Only inline JWTs are supported')\n }\n\n const jwsService = agentContext.resolve(JwsService)\n\n let isValidButUntrusted = false\n let isValidAndTrusted = false\n\n const jwt = Jwt.fromSerializedJwt(va.data)\n\n try {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates,\n })\n isValidAndTrusted = isValid\n } catch {\n if (allowUntrustedSigned) {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates: jwt.header.x5c ?? [],\n })\n isValidButUntrusted = isValid\n }\n }\n\n if (jwt.header.typ !== 'rc-rp+jwt') {\n throw new Error(`only 'rc-rp+jwt' is supported as header typ. Request included: ${jwt.header.typ}`)\n }\n\n if (!signedAuthorizationRequest) {\n throw new Error('Request must be signed for the registration certificate')\n }\n\n if (signedAuthorizationRequest.signer.method !== 'x5c') {\n throw new Error('x5c is only supported for registration certificate')\n }\n\n const registrationCertificateHeaderSchema = z\n .object({\n typ: z.literal('rc-rp+jwt'),\n alg: z.string(),\n // sprin-d did not define this\n x5u: z.string().url().optional(),\n // sprin-d did not define this\n 'x5t#s256': z.string().optional(),\n })\n .passthrough()\n\n // TODO: does not support intermediaries\n const registrationCertificatePayloadSchema = z\n .object({\n credentials: z.array(\n z.object({\n format: z.string(),\n multiple: z.boolean().default(false),\n meta: z\n .object({\n vct_values: z.array(z.string()).optional(),\n doctype_value: z.string().optional(),\n })\n .optional(),\n trusted_authorities: z\n .array(z.object({ type: z.string(), values: z.array(z.string()) }))\n .nonempty()\n .optional(),\n require_cryptographic_holder_binding: z.boolean().default(true),\n claims: z\n .array(\n z.object({\n id: z.string().optional(),\n path: z.array(z.string()).nonempty().nonempty(),\n values: z.array(z.number().or(z.boolean())).optional(),\n })\n )\n .nonempty()\n .optional(),\n claim_sets: z.array(z.array(z.string())).nonempty().optional(),\n })\n ),\n contact: z.object({\n website: z.string().url(),\n 'e-mail': z.string().email(),\n phone: z.string(),\n }),\n sub: z.string(),\n // Should be service\n services: z.array(z.object({ lang: z.string(), name: z.string() })),\n public_body: z.boolean().default(false),\n entitlements: z.array(z.any()),\n provided_attestations: z\n .array(\n z.object({\n format: z.string(),\n meta: z.any(),\n })\n )\n .optional(),\n privacy_policy: z.string().url(),\n iat: z.number().optional(),\n exp: z.number().optional(),\n purpose: z\n .array(\n z.object({\n locale: z.string().optional(),\n lang: z.string().optional(),\n name: z.string(),\n })\n )\n .optional(),\n status: z.any(),\n })\n .passthrough()\n\n registrationCertificateHeaderSchema.parse(jwt.header)\n const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson())\n\n const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c\n const rpCert = X509Certificate.fromEncodedCertificate(rpCertEncoded)\n\n if (rpCert.subject !== parsedPayload.sub) {\n throw new Error(\n `Subject in the certificate of the auth request: '${rpCert.subject}' is not equal to the subject of the registration certificate: '${parsedPayload.sub}'`\n )\n }\n\n if (parsedPayload.iat && new Date().getTime() / 1000 <= parsedPayload.iat) {\n throw new Error('Issued at timestamp of the registration certificate is in the future')\n }\n\n // TODO: check the status of the registration certificate\n\n if (!dcql) {\n throw new Error('DCQL must be used when working registration certificates')\n }\n\n if (\n authorizationRequestPayload.presentation_definition ||\n authorizationRequestPayload.presentation_definition_uri\n ) {\n throw new Error('Presentation Exchange is not supported for the registration certificate')\n }\n\n const isValidDcqlQuery = isDcqlQueryEqualOrSubset(dcql.queryResult, parsedPayload as unknown as DcqlQuery)\n\n if (!isValidDcqlQuery) {\n throw new Error(\n 'DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate'\n )\n }\n\n results.push({ isValidButUntrusted, isValidAndTrusted })\n } else {\n throw new Error(`only format of 'jwt' is supported`)\n }\n }\n return results\n}\n","import { type DcqlQuery, equalsIgnoreOrder, equalsWithOrder } from '@credo-ts/core'\n\nexport function isDcqlQueryEqualOrSubset(arq: DcqlQuery, rcq: DcqlQuery): boolean {\n if (rcq.credential_sets) {\n return false\n }\n\n if (rcq.credentials.some((c) => c.id)) {\n return false\n }\n\n // only sd-jwt and mdoc are supported\n if (arq.credentials.some((c) => c.format !== 'mso_mdoc' && c.format !== 'vc+sd-jwt' && c.format !== 'dc+sd-jwt')) {\n return false\n }\n\n credentialQueryLoop: for (const credentialQuery of arq.credentials) {\n const matchingRcqCredentialQueriesBasedOnFormat = rcq.credentials.filter((c) => c.format === credentialQuery.format)\n\n if (matchingRcqCredentialQueriesBasedOnFormat.length === 0) return false\n\n switch (credentialQuery.format) {\n case 'mso_mdoc': {\n const doctypeValue = credentialQuery.meta?.doctype_value\n if (!doctypeValue) return false\n if (typeof credentialQuery.meta?.doctype_value !== 'string') return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & { format: 'mso_mdoc' } =>\n !!(c.format === 'mso_mdoc' && c.meta && c.meta.doctype_value === doctypeValue)\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for mdoc, this means matching the `path[0]` (namespace) and `path[1]` (value name)\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c &&\n matchedRequest.claims?.some(\n (mrc) => 'path' in mrc && c.path[0] === mrc.path[0] && c.path[1] === mrc.path[1]\n )\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n case 'dc+sd-jwt':\n case 'vc+sd-jwt': {\n const vctValues = credentialQuery.meta?.vct_values\n if (!vctValues) return false\n if (credentialQuery.meta?.vct_values?.length === 0) return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & ({ format: 'dc+sd-jwt' } | { format: 'vc+sd-jwt' }) =>\n !!(\n (c.format === 'dc+sd-jwt' || c.format === 'vc+sd-jwt') &&\n c.meta?.vct_values &&\n equalsIgnoreOrder(c.meta.vct_values, vctValues)\n )\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for sd-jwt, this means making sure that every `path[n]` is in the registration certificate\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c && matchedRequest.claims?.some((mrc) => 'path' in mrc && equalsWithOrder(c.path, mrc.path))\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n default:\n return false\n }\n }\n\n return true\n}\n"],"mappings":"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","names":["import_core","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/verifyOpenid4VpAuthorizationRequest.ts","../src/isDcqlQueryEqualOrSubset.ts"],"sourcesContent":["export { verifyOpenid4VpAuthorizationRequest } from './verifyOpenid4VpAuthorizationRequest'\n","import { allowedNodeEnvironmentFlags } from 'node:process'\nimport { type AgentContext, type DcqlQuery, JwsService, Jwt, X509Certificate } from '@credo-ts/core'\nimport type { OpenId4VpResolvedAuthorizationRequest } from '@credo-ts/openid4vc'\nimport z from 'zod'\nimport { isDcqlQueryEqualOrSubset } from './isDcqlQueryEqualOrSubset'\n\nexport type VerifyAuthorizationRequestOptions = {\n resolvedAuthorizationRequest: OpenId4VpResolvedAuthorizationRequest\n trustedCertificates?: Array<string>\n allowUntrustedSigned?: boolean\n}\n\nexport const verifyOpenid4VpAuthorizationRequest = async (\n agentContext: AgentContext,\n {\n resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql },\n trustedCertificates,\n allowUntrustedSigned,\n }: VerifyAuthorizationRequestOptions\n) => {\n const results = []\n if (!authorizationRequestPayload.verifier_attestations) return\n for (const va of authorizationRequestPayload.verifier_attestations) {\n // Here we verify it as a registration certificate according to\n // https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/flows/Wallet-Relying-Party-Authentication/#registration-certificate\n if (va.format === 'jwt') {\n if (typeof va.data !== 'string') {\n throw new Error('Only inline JWTs are supported')\n }\n\n const jwsService = agentContext.resolve(JwsService)\n\n let isValidButUntrusted = false\n let isValidAndTrusted = false\n\n const jwt = Jwt.fromSerializedJwt(va.data)\n\n try {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates,\n })\n isValidAndTrusted = isValid\n } catch {\n if (allowUntrustedSigned) {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates: jwt.header.x5c ?? [],\n })\n isValidButUntrusted = isValid\n }\n }\n\n if (jwt.header.typ !== 'rc-rp+jwt') {\n throw new Error(`only 'rc-rp+jwt' is supported as header typ. Request included: ${jwt.header.typ}`)\n }\n\n if (!signedAuthorizationRequest) {\n throw new Error('Request must be signed for the registration certificate')\n }\n\n if (signedAuthorizationRequest.signer.method !== 'x5c') {\n throw new Error('x5c is only supported for registration certificate')\n }\n\n const registrationCertificateHeaderSchema = z\n .object({\n typ: z.literal('rc-rp+jwt'),\n alg: z.string(),\n // sprin-d did not define this\n x5u: z.string().url().optional(),\n // sprin-d did not define this\n 'x5t#s256': z.string().optional(),\n })\n .passthrough()\n\n // TODO: does not support intermediaries\n const registrationCertificatePayloadSchema = z\n .object({\n credentials: z.array(\n z.object({\n format: z.string(),\n multiple: z.boolean().default(false),\n meta: z\n .object({\n vct_values: z.array(z.string()).optional(),\n doctype_value: z.string().optional(),\n })\n .optional(),\n trusted_authorities: z\n .array(z.object({ type: z.string(), values: z.array(z.string()) }))\n .nonempty()\n .optional(),\n require_cryptographic_holder_binding: z.boolean().default(true),\n claims: z\n .array(\n z.object({\n id: z.string().optional(),\n path: z.array(z.string()).nonempty().nonempty(),\n values: z.array(z.number().or(z.boolean())).optional(),\n })\n )\n .nonempty()\n .optional(),\n claim_sets: z.array(z.array(z.string())).nonempty().optional(),\n })\n ),\n contact: z.object({\n website: z.string().url(),\n 'e-mail': z.string().email(),\n phone: z.string(),\n }),\n sub: z.string(),\n // Should be service\n services: z.array(z.object({ lang: z.string(), name: z.string() })),\n public_body: z.boolean().default(false),\n entitlements: z.array(z.any()),\n provided_attestations: z\n .array(\n z.object({\n format: z.string(),\n meta: z.any(),\n })\n )\n .optional(),\n privacy_policy: z.string().url(),\n iat: z.number().optional(),\n exp: z.number().optional(),\n purpose: z\n .array(\n z.object({\n locale: z.string().optional(),\n lang: z.string().optional(),\n name: z.string(),\n })\n )\n .optional(),\n status: z.any(),\n })\n .passthrough()\n\n registrationCertificateHeaderSchema.parse(jwt.header)\n const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson())\n\n const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c\n const rpCert = X509Certificate.fromEncodedCertificate(rpCertEncoded)\n\n if (rpCert.subject !== parsedPayload.sub) {\n throw new Error(\n `Subject in the certificate of the auth request: '${rpCert.subject}' is not equal to the subject of the registration certificate: '${parsedPayload.sub}'`\n )\n }\n\n if (parsedPayload.iat && new Date().getTime() / 1000 <= parsedPayload.iat) {\n throw new Error('Issued at timestamp of the registration certificate is in the future')\n }\n\n // TODO: check the status of the registration certificate\n\n if (!dcql) {\n throw new Error('DCQL must be used when working registration certificates')\n }\n\n if (\n authorizationRequestPayload.presentation_definition ||\n authorizationRequestPayload.presentation_definition_uri\n ) {\n throw new Error('Presentation Exchange is not supported for the registration certificate')\n }\n\n const isValidDcqlQuery = isDcqlQueryEqualOrSubset(dcql.queryResult, parsedPayload as unknown as DcqlQuery)\n\n if (!isValidDcqlQuery) {\n throw new Error(\n 'DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate'\n )\n }\n\n results.push({ isValidButUntrusted, isValidAndTrusted, x509RegistrationCertificate: rpCert })\n } else {\n throw new Error(`only format of 'jwt' is supported`)\n }\n }\n return results\n}\n","import { type DcqlQuery, equalsIgnoreOrder, equalsWithOrder } from '@credo-ts/core'\n\nexport function isDcqlQueryEqualOrSubset(arq: DcqlQuery, rcq: DcqlQuery): boolean {\n if (rcq.credential_sets) {\n return false\n }\n\n if (rcq.credentials.some((c) => c.id)) {\n return false\n }\n\n // only sd-jwt and mdoc are supported\n if (arq.credentials.some((c) => c.format !== 'mso_mdoc' && c.format !== 'vc+sd-jwt' && c.format !== 'dc+sd-jwt')) {\n return false\n }\n\n credentialQueryLoop: for (const credentialQuery of arq.credentials) {\n const matchingRcqCredentialQueriesBasedOnFormat = rcq.credentials.filter((c) => c.format === credentialQuery.format)\n\n if (matchingRcqCredentialQueriesBasedOnFormat.length === 0) return false\n\n switch (credentialQuery.format) {\n case 'mso_mdoc': {\n const doctypeValue = credentialQuery.meta?.doctype_value\n if (!doctypeValue) return false\n if (typeof credentialQuery.meta?.doctype_value !== 'string') return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & { format: 'mso_mdoc' } =>\n !!(c.format === 'mso_mdoc' && c.meta && c.meta.doctype_value === doctypeValue)\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for mdoc, this means matching the `path[0]` (namespace) and `path[1]` (value name)\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c &&\n matchedRequest.claims?.some(\n (mrc) => 'path' in mrc && c.path[0] === mrc.path[0] && c.path[1] === mrc.path[1]\n )\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n case 'dc+sd-jwt':\n case 'vc+sd-jwt': {\n const vctValues = credentialQuery.meta?.vct_values\n if (!vctValues) return false\n if (credentialQuery.meta?.vct_values?.length === 0) return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & ({ format: 'dc+sd-jwt' } | { format: 'vc+sd-jwt' }) =>\n !!(\n (c.format === 'dc+sd-jwt' || c.format === 'vc+sd-jwt') &&\n c.meta?.vct_values &&\n equalsIgnoreOrder(c.meta.vct_values, vctValues)\n )\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for sd-jwt, this means making sure that every `path[n]` is in the registration certificate\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c && matchedRequest.claims?.some((mrc) => 'path' in mrc && equalsWithOrder(c.path, mrc.path))\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n default:\n return false\n }\n }\n\n return true\n}\n"],"mappings":"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","names":["import_core","z"]}
|
package/dist/index.mjs
CHANGED
|
@@ -194,7 +194,7 @@ var verifyOpenid4VpAuthorizationRequest = async (agentContext, {
|
|
|
194
194
|
"DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate"
|
|
195
195
|
);
|
|
196
196
|
}
|
|
197
|
-
results.push({ isValidButUntrusted, isValidAndTrusted });
|
|
197
|
+
results.push({ isValidButUntrusted, isValidAndTrusted, x509RegistrationCertificate: rpCert });
|
|
198
198
|
} else {
|
|
199
199
|
throw new Error(`only format of 'jwt' is supported`);
|
|
200
200
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/verifyOpenid4VpAuthorizationRequest.ts","../src/isDcqlQueryEqualOrSubset.ts"],"sourcesContent":["import { allowedNodeEnvironmentFlags } from 'node:process'\nimport { type AgentContext, type DcqlQuery, JwsService, Jwt, X509Certificate } from '@credo-ts/core'\nimport type { OpenId4VpResolvedAuthorizationRequest } from '@credo-ts/openid4vc'\nimport z from 'zod'\nimport { isDcqlQueryEqualOrSubset } from './isDcqlQueryEqualOrSubset'\n\nexport type VerifyAuthorizationRequestOptions = {\n resolvedAuthorizationRequest: OpenId4VpResolvedAuthorizationRequest\n trustedCertificates?: Array<string>\n allowUntrustedSigned?: boolean\n}\n\nexport const verifyOpenid4VpAuthorizationRequest = async (\n agentContext: AgentContext,\n {\n resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql },\n trustedCertificates,\n allowUntrustedSigned,\n }: VerifyAuthorizationRequestOptions\n) => {\n const results = []\n if (!authorizationRequestPayload.verifier_attestations) return\n for (const va of authorizationRequestPayload.verifier_attestations) {\n // Here we verify it as a registration certificate according to\n // https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/flows/Wallet-Relying-Party-Authentication/#registration-certificate\n if (va.format === 'jwt') {\n if (typeof va.data !== 'string') {\n throw new Error('Only inline JWTs are supported')\n }\n\n const jwsService = agentContext.resolve(JwsService)\n\n let isValidButUntrusted = false\n let isValidAndTrusted = false\n\n const jwt = Jwt.fromSerializedJwt(va.data)\n\n try {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates,\n })\n isValidAndTrusted = isValid\n } catch {\n if (allowUntrustedSigned) {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates: jwt.header.x5c ?? [],\n })\n isValidButUntrusted = isValid\n }\n }\n\n if (jwt.header.typ !== 'rc-rp+jwt') {\n throw new Error(`only 'rc-rp+jwt' is supported as header typ. Request included: ${jwt.header.typ}`)\n }\n\n if (!signedAuthorizationRequest) {\n throw new Error('Request must be signed for the registration certificate')\n }\n\n if (signedAuthorizationRequest.signer.method !== 'x5c') {\n throw new Error('x5c is only supported for registration certificate')\n }\n\n const registrationCertificateHeaderSchema = z\n .object({\n typ: z.literal('rc-rp+jwt'),\n alg: z.string(),\n // sprin-d did not define this\n x5u: z.string().url().optional(),\n // sprin-d did not define this\n 'x5t#s256': z.string().optional(),\n })\n .passthrough()\n\n // TODO: does not support intermediaries\n const registrationCertificatePayloadSchema = z\n .object({\n credentials: z.array(\n z.object({\n format: z.string(),\n multiple: z.boolean().default(false),\n meta: z\n .object({\n vct_values: z.array(z.string()).optional(),\n doctype_value: z.string().optional(),\n })\n .optional(),\n trusted_authorities: z\n .array(z.object({ type: z.string(), values: z.array(z.string()) }))\n .nonempty()\n .optional(),\n require_cryptographic_holder_binding: z.boolean().default(true),\n claims: z\n .array(\n z.object({\n id: z.string().optional(),\n path: z.array(z.string()).nonempty().nonempty(),\n values: z.array(z.number().or(z.boolean())).optional(),\n })\n )\n .nonempty()\n .optional(),\n claim_sets: z.array(z.array(z.string())).nonempty().optional(),\n })\n ),\n contact: z.object({\n website: z.string().url(),\n 'e-mail': z.string().email(),\n phone: z.string(),\n }),\n sub: z.string(),\n // Should be service\n services: z.array(z.object({ lang: z.string(), name: z.string() })),\n public_body: z.boolean().default(false),\n entitlements: z.array(z.any()),\n provided_attestations: z\n .array(\n z.object({\n format: z.string(),\n meta: z.any(),\n })\n )\n .optional(),\n privacy_policy: z.string().url(),\n iat: z.number().optional(),\n exp: z.number().optional(),\n purpose: z\n .array(\n z.object({\n locale: z.string().optional(),\n lang: z.string().optional(),\n name: z.string(),\n })\n )\n .optional(),\n status: z.any(),\n })\n .passthrough()\n\n registrationCertificateHeaderSchema.parse(jwt.header)\n const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson())\n\n const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c\n const rpCert = X509Certificate.fromEncodedCertificate(rpCertEncoded)\n\n if (rpCert.subject !== parsedPayload.sub) {\n throw new Error(\n `Subject in the certificate of the auth request: '${rpCert.subject}' is not equal to the subject of the registration certificate: '${parsedPayload.sub}'`\n )\n }\n\n if (parsedPayload.iat && new Date().getTime() / 1000 <= parsedPayload.iat) {\n throw new Error('Issued at timestamp of the registration certificate is in the future')\n }\n\n // TODO: check the status of the registration certificate\n\n if (!dcql) {\n throw new Error('DCQL must be used when working registration certificates')\n }\n\n if (\n authorizationRequestPayload.presentation_definition ||\n authorizationRequestPayload.presentation_definition_uri\n ) {\n throw new Error('Presentation Exchange is not supported for the registration certificate')\n }\n\n const isValidDcqlQuery = isDcqlQueryEqualOrSubset(dcql.queryResult, parsedPayload as unknown as DcqlQuery)\n\n if (!isValidDcqlQuery) {\n throw new Error(\n 'DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate'\n )\n }\n\n results.push({ isValidButUntrusted, isValidAndTrusted })\n } else {\n throw new Error(`only format of 'jwt' is supported`)\n }\n }\n return results\n}\n","import { type DcqlQuery, equalsIgnoreOrder, equalsWithOrder } from '@credo-ts/core'\n\nexport function isDcqlQueryEqualOrSubset(arq: DcqlQuery, rcq: DcqlQuery): boolean {\n if (rcq.credential_sets) {\n return false\n }\n\n if (rcq.credentials.some((c) => c.id)) {\n return false\n }\n\n // only sd-jwt and mdoc are supported\n if (arq.credentials.some((c) => c.format !== 'mso_mdoc' && c.format !== 'vc+sd-jwt' && c.format !== 'dc+sd-jwt')) {\n return false\n }\n\n credentialQueryLoop: for (const credentialQuery of arq.credentials) {\n const matchingRcqCredentialQueriesBasedOnFormat = rcq.credentials.filter((c) => c.format === credentialQuery.format)\n\n if (matchingRcqCredentialQueriesBasedOnFormat.length === 0) return false\n\n switch (credentialQuery.format) {\n case 'mso_mdoc': {\n const doctypeValue = credentialQuery.meta?.doctype_value\n if (!doctypeValue) return false\n if (typeof credentialQuery.meta?.doctype_value !== 'string') return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & { format: 'mso_mdoc' } =>\n !!(c.format === 'mso_mdoc' && c.meta && c.meta.doctype_value === doctypeValue)\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for mdoc, this means matching the `path[0]` (namespace) and `path[1]` (value name)\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c &&\n matchedRequest.claims?.some(\n (mrc) => 'path' in mrc && c.path[0] === mrc.path[0] && c.path[1] === mrc.path[1]\n )\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n case 'dc+sd-jwt':\n case 'vc+sd-jwt': {\n const vctValues = credentialQuery.meta?.vct_values\n if (!vctValues) return false\n if (credentialQuery.meta?.vct_values?.length === 0) return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & ({ format: 'dc+sd-jwt' } | { format: 'vc+sd-jwt' }) =>\n !!(\n (c.format === 'dc+sd-jwt' || c.format === 'vc+sd-jwt') &&\n c.meta?.vct_values &&\n equalsIgnoreOrder(c.meta.vct_values, vctValues)\n )\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for sd-jwt, this means making sure that every `path[n]` is in the registration certificate\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c && matchedRequest.claims?.some((mrc) => 'path' in mrc && equalsWithOrder(c.path, mrc.path))\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n default:\n return false\n }\n }\n\n return true\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/verifyOpenid4VpAuthorizationRequest.ts","../src/isDcqlQueryEqualOrSubset.ts"],"sourcesContent":["import { allowedNodeEnvironmentFlags } from 'node:process'\nimport { type AgentContext, type DcqlQuery, JwsService, Jwt, X509Certificate } from '@credo-ts/core'\nimport type { OpenId4VpResolvedAuthorizationRequest } from '@credo-ts/openid4vc'\nimport z from 'zod'\nimport { isDcqlQueryEqualOrSubset } from './isDcqlQueryEqualOrSubset'\n\nexport type VerifyAuthorizationRequestOptions = {\n resolvedAuthorizationRequest: OpenId4VpResolvedAuthorizationRequest\n trustedCertificates?: Array<string>\n allowUntrustedSigned?: boolean\n}\n\nexport const verifyOpenid4VpAuthorizationRequest = async (\n agentContext: AgentContext,\n {\n resolvedAuthorizationRequest: { authorizationRequestPayload, signedAuthorizationRequest, dcql },\n trustedCertificates,\n allowUntrustedSigned,\n }: VerifyAuthorizationRequestOptions\n) => {\n const results = []\n if (!authorizationRequestPayload.verifier_attestations) return\n for (const va of authorizationRequestPayload.verifier_attestations) {\n // Here we verify it as a registration certificate according to\n // https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/flows/Wallet-Relying-Party-Authentication/#registration-certificate\n if (va.format === 'jwt') {\n if (typeof va.data !== 'string') {\n throw new Error('Only inline JWTs are supported')\n }\n\n const jwsService = agentContext.resolve(JwsService)\n\n let isValidButUntrusted = false\n let isValidAndTrusted = false\n\n const jwt = Jwt.fromSerializedJwt(va.data)\n\n try {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates,\n })\n isValidAndTrusted = isValid\n } catch {\n if (allowUntrustedSigned) {\n const { isValid } = await jwsService.verifyJws(agentContext, {\n jws: va.data,\n trustedCertificates: jwt.header.x5c ?? [],\n })\n isValidButUntrusted = isValid\n }\n }\n\n if (jwt.header.typ !== 'rc-rp+jwt') {\n throw new Error(`only 'rc-rp+jwt' is supported as header typ. Request included: ${jwt.header.typ}`)\n }\n\n if (!signedAuthorizationRequest) {\n throw new Error('Request must be signed for the registration certificate')\n }\n\n if (signedAuthorizationRequest.signer.method !== 'x5c') {\n throw new Error('x5c is only supported for registration certificate')\n }\n\n const registrationCertificateHeaderSchema = z\n .object({\n typ: z.literal('rc-rp+jwt'),\n alg: z.string(),\n // sprin-d did not define this\n x5u: z.string().url().optional(),\n // sprin-d did not define this\n 'x5t#s256': z.string().optional(),\n })\n .passthrough()\n\n // TODO: does not support intermediaries\n const registrationCertificatePayloadSchema = z\n .object({\n credentials: z.array(\n z.object({\n format: z.string(),\n multiple: z.boolean().default(false),\n meta: z\n .object({\n vct_values: z.array(z.string()).optional(),\n doctype_value: z.string().optional(),\n })\n .optional(),\n trusted_authorities: z\n .array(z.object({ type: z.string(), values: z.array(z.string()) }))\n .nonempty()\n .optional(),\n require_cryptographic_holder_binding: z.boolean().default(true),\n claims: z\n .array(\n z.object({\n id: z.string().optional(),\n path: z.array(z.string()).nonempty().nonempty(),\n values: z.array(z.number().or(z.boolean())).optional(),\n })\n )\n .nonempty()\n .optional(),\n claim_sets: z.array(z.array(z.string())).nonempty().optional(),\n })\n ),\n contact: z.object({\n website: z.string().url(),\n 'e-mail': z.string().email(),\n phone: z.string(),\n }),\n sub: z.string(),\n // Should be service\n services: z.array(z.object({ lang: z.string(), name: z.string() })),\n public_body: z.boolean().default(false),\n entitlements: z.array(z.any()),\n provided_attestations: z\n .array(\n z.object({\n format: z.string(),\n meta: z.any(),\n })\n )\n .optional(),\n privacy_policy: z.string().url(),\n iat: z.number().optional(),\n exp: z.number().optional(),\n purpose: z\n .array(\n z.object({\n locale: z.string().optional(),\n lang: z.string().optional(),\n name: z.string(),\n })\n )\n .optional(),\n status: z.any(),\n })\n .passthrough()\n\n registrationCertificateHeaderSchema.parse(jwt.header)\n const parsedPayload = registrationCertificatePayloadSchema.parse(jwt.payload.toJson())\n\n const [rpCertEncoded] = signedAuthorizationRequest.signer.x5c\n const rpCert = X509Certificate.fromEncodedCertificate(rpCertEncoded)\n\n if (rpCert.subject !== parsedPayload.sub) {\n throw new Error(\n `Subject in the certificate of the auth request: '${rpCert.subject}' is not equal to the subject of the registration certificate: '${parsedPayload.sub}'`\n )\n }\n\n if (parsedPayload.iat && new Date().getTime() / 1000 <= parsedPayload.iat) {\n throw new Error('Issued at timestamp of the registration certificate is in the future')\n }\n\n // TODO: check the status of the registration certificate\n\n if (!dcql) {\n throw new Error('DCQL must be used when working registration certificates')\n }\n\n if (\n authorizationRequestPayload.presentation_definition ||\n authorizationRequestPayload.presentation_definition_uri\n ) {\n throw new Error('Presentation Exchange is not supported for the registration certificate')\n }\n\n const isValidDcqlQuery = isDcqlQueryEqualOrSubset(dcql.queryResult, parsedPayload as unknown as DcqlQuery)\n\n if (!isValidDcqlQuery) {\n throw new Error(\n 'DCQL query in the authorization request is not equal or a valid subset of the DCQl query provided in the registration certificate'\n )\n }\n\n results.push({ isValidButUntrusted, isValidAndTrusted, x509RegistrationCertificate: rpCert })\n } else {\n throw new Error(`only format of 'jwt' is supported`)\n }\n }\n return results\n}\n","import { type DcqlQuery, equalsIgnoreOrder, equalsWithOrder } from '@credo-ts/core'\n\nexport function isDcqlQueryEqualOrSubset(arq: DcqlQuery, rcq: DcqlQuery): boolean {\n if (rcq.credential_sets) {\n return false\n }\n\n if (rcq.credentials.some((c) => c.id)) {\n return false\n }\n\n // only sd-jwt and mdoc are supported\n if (arq.credentials.some((c) => c.format !== 'mso_mdoc' && c.format !== 'vc+sd-jwt' && c.format !== 'dc+sd-jwt')) {\n return false\n }\n\n credentialQueryLoop: for (const credentialQuery of arq.credentials) {\n const matchingRcqCredentialQueriesBasedOnFormat = rcq.credentials.filter((c) => c.format === credentialQuery.format)\n\n if (matchingRcqCredentialQueriesBasedOnFormat.length === 0) return false\n\n switch (credentialQuery.format) {\n case 'mso_mdoc': {\n const doctypeValue = credentialQuery.meta?.doctype_value\n if (!doctypeValue) return false\n if (typeof credentialQuery.meta?.doctype_value !== 'string') return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & { format: 'mso_mdoc' } =>\n !!(c.format === 'mso_mdoc' && c.meta && c.meta.doctype_value === doctypeValue)\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for mdoc, this means matching the `path[0]` (namespace) and `path[1]` (value name)\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c &&\n matchedRequest.claims?.some(\n (mrc) => 'path' in mrc && c.path[0] === mrc.path[0] && c.path[1] === mrc.path[1]\n )\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n case 'dc+sd-jwt':\n case 'vc+sd-jwt': {\n const vctValues = credentialQuery.meta?.vct_values\n if (!vctValues) return false\n if (credentialQuery.meta?.vct_values?.length === 0) return false\n\n const foundMatchingRequests = matchingRcqCredentialQueriesBasedOnFormat.filter(\n (c): c is typeof c & ({ format: 'dc+sd-jwt' } | { format: 'vc+sd-jwt' }) =>\n !!(\n (c.format === 'dc+sd-jwt' || c.format === 'vc+sd-jwt') &&\n c.meta?.vct_values &&\n equalsIgnoreOrder(c.meta.vct_values, vctValues)\n )\n )\n\n // We do not know which one we have to pick based on the meta+format\n if (foundMatchingRequests.length === 0) return false\n\n let foundFullyMatching = false\n for (const matchedRequest of foundMatchingRequests) {\n // credentialQuery.claims must match or be subset of matchedRequest\n\n // If the claims is empty, everything within the specific format+meta is allowed\n if (!matchedRequest.claims) continue credentialQueryLoop\n\n // If no specific claims are request, we allow it as the format+meta is allowed to be requested\n // but this requests no additional claims\n if (!credentialQuery.claims) continue credentialQueryLoop\n\n // Every claim request in the authorization request must be found in the registration certificate\n // for sd-jwt, this means making sure that every `path[n]` is in the registration certificate\n const isEveryClaimAllowedToBeRequested = credentialQuery.claims.every(\n (c) =>\n 'path' in c && matchedRequest.claims?.some((mrc) => 'path' in mrc && equalsWithOrder(c.path, mrc.path))\n )\n if (isEveryClaimAllowedToBeRequested) {\n foundFullyMatching = true\n }\n }\n\n if (!foundFullyMatching) return false\n\n break\n }\n default:\n return false\n }\n }\n\n return true\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED