@ewqwe/digital-identity 1.1.1
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/LICENSE +12 -0
- package/README.md +193 -0
- package/dist/api-client.d.ts +22 -0
- package/dist/attestation.d.ts +80 -0
- package/dist/config.d.ts +72 -0
- package/dist/dcql.d.ts +266 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +1112 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib.d.ts +18 -0
- package/dist/types.d.ts +521 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
Copyright (c) 2026 NILIMI SAS
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
5
|
+
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
6
|
+
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
7
|
+
|
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
11
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
12
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# @ewqwe/digital-identity
|
|
2
|
+
|
|
3
|
+
npm-compatible JavaScript/TypeScript library for EU Digital Identity (OpenID4VP, DCQL, mDoc, SD-JWT VC).
|
|
4
|
+
|
|
5
|
+
Part of the [ewQwe TypeScript workspace](../README.md). When used inside the workspace the
|
|
6
|
+
library is resolved via pnpm's `workspace:*` protocol. Published to npm for external consumers.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
### As an npm dependency (external consumer)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @ewqwe/digital-identity
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Inside the pnpm workspace
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# From typescript/ root — build the library
|
|
20
|
+
pnpm --filter @ewqwe/digital-identity build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The library is linked into consumers via `workspace:*` in the workspace root.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### CommonJS
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
const { buildAgeVerificationQuery, generateNonce } = require('@ewqwe/digital-identity');
|
|
31
|
+
|
|
32
|
+
const query = buildAgeVerificationQuery(18);
|
|
33
|
+
const nonce = generateNonce();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### ES Modules
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { buildAgeVerificationQuery, generateNonce } from '@ewqwe/digital-identity';
|
|
40
|
+
|
|
41
|
+
const query = buildAgeVerificationQuery(18);
|
|
42
|
+
const nonce = generateNonce();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- **DCQL Query Builders**: Build Digital Credentials Query Language queries for OpenID4VP.
|
|
48
|
+
- **Credential Types**: Pre-configured credential types (mDL, PID, Proof of Age, etc.) with associated claims.
|
|
49
|
+
- **Protocol Profiles**: HAIP and Annex A profile support.
|
|
50
|
+
- **Attestation Verification**: JWT signature verification using the Web Crypto API in browsers and Node.js.
|
|
51
|
+
- **Type Definitions**: Full TypeScript support with comprehensive type safety.
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Generate an age verification query
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import { buildAgeVerificationQuery } from '@ewqwe/digital-identity';
|
|
59
|
+
|
|
60
|
+
// Generate query for age 18+
|
|
61
|
+
const query = buildAgeVerificationQuery(18);
|
|
62
|
+
console.log(JSON.stringify(query, null, 2));
|
|
63
|
+
|
|
64
|
+
// Generate query for age 21+
|
|
65
|
+
const query21 = buildAgeVerificationQuery(21);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Build an Init Transaction Request
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import { buildInitTransactionRequest } from '@ewqwe/digital-identity';
|
|
72
|
+
|
|
73
|
+
const request = buildInitTransactionRequest(
|
|
74
|
+
'https://example.com',
|
|
75
|
+
'mdl',
|
|
76
|
+
['given_name', 'family_name', 'birth_date']
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Generate a Cryptographic Nonce
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
import { generateNonce } from '@ewqwe/digital-identity';
|
|
84
|
+
|
|
85
|
+
const nonce = generateNonce();
|
|
86
|
+
// Returns a base64url-encoded 32-byte random value
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Validate a DCQL Query
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
import { isValidDCQLQuery, buildAgeVerificationQuery } from '@ewqwe/digital-identity';
|
|
93
|
+
|
|
94
|
+
const query = buildAgeVerificationQuery(18);
|
|
95
|
+
const validation = isValidDCQLQuery(query);
|
|
96
|
+
|
|
97
|
+
if (validation.valid) {
|
|
98
|
+
console.log('Valid DCQL query');
|
|
99
|
+
} else {
|
|
100
|
+
console.error('Invalid:', validation.error);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## API Reference
|
|
105
|
+
|
|
106
|
+
### DCQL Query Functions
|
|
107
|
+
|
|
108
|
+
| Function | Description |
|
|
109
|
+
|----------|-------------|
|
|
110
|
+
| `buildAgeVerificationQuery(ageThreshold)` | Build minimal age verification query |
|
|
111
|
+
| `buildAgeVerificationQueryWithFallback(ageThreshold)` | Build query with mDL fallback |
|
|
112
|
+
| `buildInitTransactionRequest(publicUrl, credentialType, claims)` | Build init transaction request |
|
|
113
|
+
| `getDefaultAgeVerificationDCQL()` | Get default age verification query |
|
|
114
|
+
| `determineProfile(credentialType, explicitProfile)` | Determine protocol profile |
|
|
115
|
+
| `generateNonce()` | Generate cryptographic nonce |
|
|
116
|
+
| `isValidDCQLQuery(query)` | Validate DCQL query structure |
|
|
117
|
+
| `parseDCQLQuery(queryString)` | Parse DCQL from JSON string |
|
|
118
|
+
| `extractAgeThreshold(query)` | Extract age threshold from query |
|
|
119
|
+
|
|
120
|
+
### Credential Types
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
import { CREDENTIAL_TYPES, getClaimsForType } from '@ewqwe/digital-identity';
|
|
124
|
+
|
|
125
|
+
// Get all credential types
|
|
126
|
+
const types = Object.keys(CREDENTIAL_TYPES);
|
|
127
|
+
|
|
128
|
+
// Get claims for a specific type
|
|
129
|
+
const mdlClaims = getClaimsForType('mdl');
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Protocol Profiles
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
import { PROTOCOL_PROFILES, determineProfile } from '@ewqwe/digital-identity';
|
|
136
|
+
|
|
137
|
+
// Get HAIP profile
|
|
138
|
+
const haip = PROTOCOL_PROFILES.haip;
|
|
139
|
+
|
|
140
|
+
// Determine profile for credential type
|
|
141
|
+
const profile = determineProfile('mdl'); // 'haip'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Attestation Verification
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
import { parseAttestation, verifyAttestation } from '@ewqwe/digital-identity';
|
|
148
|
+
|
|
149
|
+
// Parse and verify attestation from backend response
|
|
150
|
+
const attestation = await parseAttestation(jwtToken, '/ewqwe_api/jwks');
|
|
151
|
+
|
|
152
|
+
// Or verify with pre-imported public key
|
|
153
|
+
const publicKey = await importVerifierPublicKey(pemOrSpki);
|
|
154
|
+
const claims = await verifyAttestation(jwtToken, publicKey);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## API Client
|
|
158
|
+
|
|
159
|
+
`@ewqwe/digital-identity` exports `EwqweApiClient` for server-side and browser client code, with a pluggable `fetch` implementation.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { EwqweApiClient } from '@ewqwe/digital-identity';
|
|
163
|
+
|
|
164
|
+
const apiClient = new EwqweApiClient({
|
|
165
|
+
baseUrl: 'https://localhost:5175',
|
|
166
|
+
fetch: window.fetch,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const tx = await apiClient.initOpenID4VPTransaction({
|
|
170
|
+
public_url: 'https://localhost:5174',
|
|
171
|
+
credential_type: 'proof-of-age',
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const status = await apiClient.getOpenID4VPTransactionStatus(tx.transaction_id);
|
|
175
|
+
const verify = await apiClient.verifyPresentation({
|
|
176
|
+
vp_token: '<vp_token>',
|
|
177
|
+
presentation_submission: null,
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### API client methods
|
|
182
|
+
|
|
183
|
+
- `initOpenID4VPTransaction(request: InitTransactionRequest): Promise<InitTransactionResponse>`
|
|
184
|
+
- `getOpenID4VPTransactionStatus(transactionId: string): Promise<TransactionStatusResult>`
|
|
185
|
+
- `getOpenID4VPAuthorizationRequest(transactionId: string): Promise<OpenID4VPRequest>`
|
|
186
|
+
- `postOpenID4VPAuthorizationRequest(transactionId: string, response: OpenID4VPResponse): Promise<void>`
|
|
187
|
+
- `postOpenID4VPDirectPost(response: OpenID4VPResponse): Promise<void>`
|
|
188
|
+
- `getOpenID4VPJwks(): Promise<{ keys: unknown[] }>`
|
|
189
|
+
- `verifyPresentation(args): Promise<VerifyResponse>`
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { InitTransactionRequest, InitTransactionResponse, OpenID4VPRequest, OpenID4VPResponse, TransactionStatusResult, VerifyRequest, VerifyResponse } from "./types.js";
|
|
2
|
+
export type FetchFn = (input: RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
3
|
+
export interface ApiClientOptions {
|
|
4
|
+
fetch?: FetchFn;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class EwqweApiClient {
|
|
8
|
+
private fetchFn;
|
|
9
|
+
private baseUrl;
|
|
10
|
+
constructor(options?: ApiClientOptions);
|
|
11
|
+
private buildUrl;
|
|
12
|
+
private requestJson;
|
|
13
|
+
initOpenID4VPTransaction(request: InitTransactionRequest): Promise<InitTransactionResponse>;
|
|
14
|
+
getOpenID4VPTransactionStatus(transactionId: string): Promise<TransactionStatusResult>;
|
|
15
|
+
getOpenID4VPAuthorizationRequest(transactionId: string): Promise<OpenID4VPRequest>;
|
|
16
|
+
postOpenID4VPAuthorizationRequest(transactionId: string, authResponse: OpenID4VPResponse): Promise<void>;
|
|
17
|
+
postOpenID4VPDirectPost(response: OpenID4VPResponse): Promise<void>;
|
|
18
|
+
getOpenID4VPJwks(): Promise<{
|
|
19
|
+
keys: unknown[];
|
|
20
|
+
}>;
|
|
21
|
+
verifyPresentation(request: VerifyRequest): Promise<VerifyResponse>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attestation JWT parsing and verification utilities.
|
|
3
|
+
*
|
|
4
|
+
* Browser- and Node-compatible — uses the Web Crypto API only (SubtleCrypto).
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Decoded payload of a credential verification attestation JWT.
|
|
10
|
+
*
|
|
11
|
+
* Standard JWT claims are typed; all credential-specific claims (e.g.
|
|
12
|
+
* `age_over_18`, `given_name`) are accessible via index signature because they
|
|
13
|
+
* are format-agnostic and defined by the credential schema, not this library.
|
|
14
|
+
*/
|
|
15
|
+
export interface Attestation {
|
|
16
|
+
/** Issuer — credential verifier identifier. */
|
|
17
|
+
iss: string;
|
|
18
|
+
/** Subject — transaction / attestation event ID. */
|
|
19
|
+
sub: string;
|
|
20
|
+
/** Audience — relying party identifier (client_id). */
|
|
21
|
+
aud: string;
|
|
22
|
+
/** Expiration time (Unix timestamp seconds). */
|
|
23
|
+
exp: number;
|
|
24
|
+
/** Issued at (Unix timestamp seconds). */
|
|
25
|
+
iat: number;
|
|
26
|
+
/** Not before (Unix timestamp seconds). */
|
|
27
|
+
nbf: number;
|
|
28
|
+
/** Unique attestation ID (UUID). */
|
|
29
|
+
jti: string;
|
|
30
|
+
/** Nonce from the OpenID4VP request (replay prevention). */
|
|
31
|
+
nonce?: string;
|
|
32
|
+
/** Credential doc_type (e.g. `"org.iso.18013.5.1.mDL"`). */
|
|
33
|
+
doc_type?: string;
|
|
34
|
+
/** Credential namespace (e.g. `"org.iso.18013.5.1"`). */
|
|
35
|
+
namespace?: string;
|
|
36
|
+
/** All credential-specific claims, keyed by claim name. */
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Base64url-decode a string to a Uint8Array.
|
|
41
|
+
* Handles the standard base64url alphabet (no padding required).
|
|
42
|
+
*/
|
|
43
|
+
export declare function base64urlDecode(input: string): Uint8Array;
|
|
44
|
+
/**
|
|
45
|
+
* Import an ES256 (ECDSA P-256) public key for attestation signature verification.
|
|
46
|
+
*
|
|
47
|
+
* Accepts either:
|
|
48
|
+
* - A raw SPKI DER encoded as a base64 string (no headers), or
|
|
49
|
+
* - A PEM-encoded SubjectPublicKeyInfo certificate (`-----BEGIN PUBLIC KEY-----`), or
|
|
50
|
+
* - A PEM-encoded X.509 certificate (`-----BEGIN CERTIFICATE-----`): the Subject
|
|
51
|
+
* Public Key Info is automatically extracted from the TBSCertificate.
|
|
52
|
+
*/
|
|
53
|
+
export declare function importVerifierPublicKey(pemOrSpki: string): Promise<CryptoKey>;
|
|
54
|
+
/**
|
|
55
|
+
* Decode and verify an attestation JWT signed by the credential verifier.
|
|
56
|
+
*
|
|
57
|
+
* Steps:
|
|
58
|
+
* 1. Fetches the JWKS from `jwksUrl`.
|
|
59
|
+
* 2. Locates the verification key using the `kid` from the JWT header (falls back
|
|
60
|
+
* to the first key carrying an `x5c` certificate chain).
|
|
61
|
+
* 3. Imports the public key from the DER certificate in `x5c[0]`.
|
|
62
|
+
* 4. Verifies the ES256 signature and checks `exp` / `nbf`.
|
|
63
|
+
* 5. Returns the verified {@link Attestation}.
|
|
64
|
+
*
|
|
65
|
+
* @param jwt - Compact-serialized JWT from `VerifyResponse.attestation`.
|
|
66
|
+
* @param jwksUrl - URL of the JWK Set that contains the attestation signing key,
|
|
67
|
+
* e.g. `"/ewqwe_api/openid4vp/.well-known/jwks.json"`.
|
|
68
|
+
* @throws If the JWKS cannot be fetched, no matching key is found, or verification fails.
|
|
69
|
+
*/
|
|
70
|
+
export declare function decodeAttestation(jwt: string): Attestation;
|
|
71
|
+
export declare function getAttestationExpiryStatus(attestation: Attestation): "valid" | "expired" | "not_yet_valid";
|
|
72
|
+
export declare function parseAttestation(jwt: string, jwksUrl: string): Promise<Attestation>;
|
|
73
|
+
/**
|
|
74
|
+
* Verify an ES256 attestation JWT signature and return the decoded claims.
|
|
75
|
+
*
|
|
76
|
+
* @param jwt - The compact-serialized JWT string.
|
|
77
|
+
* @param publicKey - The verifier's public key, obtained from `importVerifierPublicKey`.
|
|
78
|
+
* @throws If the signature is invalid, the JWT is malformed, or the claims are expired.
|
|
79
|
+
*/
|
|
80
|
+
export declare function verifyAttestation(jwt: string, publicKey: CryptoKey): Promise<Attestation>;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ewqwe/digital-identity — Protocol & Credential Configuration
|
|
3
|
+
*
|
|
4
|
+
* Defines protocol profiles (HAIP, Annex A), credential type specifications
|
|
5
|
+
* (mDL, PID, Proof of Age), and helper functions for querying them.
|
|
6
|
+
*
|
|
7
|
+
* Node.js compatible — no platform-specific APIs.
|
|
8
|
+
*
|
|
9
|
+
* @see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
|
|
10
|
+
* @see https://ageverification.dev/Technical%20Specification/annexes/annex-A/annex-A-av-profile
|
|
11
|
+
* @see ISO/IEC 18013-5:2021 for mDL claims
|
|
12
|
+
*/
|
|
13
|
+
import type { ClaimDefinition, CredentialType, CredentialTypeConfig, ProfileId, ProtocolProfile } from "./types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Protocol Profile Configurations
|
|
16
|
+
*
|
|
17
|
+
* **HAIP (High Assurance Interoperability Profile):**
|
|
18
|
+
* - Used for: mDL, PID (National ID)
|
|
19
|
+
* - Client ID Scheme: x509_hash (X.509 certificate SHA-256 hash)
|
|
20
|
+
* - Request Format: JAR (JWT Authorization Request with x5c header)
|
|
21
|
+
* - Response Mode: direct_post.jwt (encrypted/signed response)
|
|
22
|
+
* - URL Schemes: eudi-openid4vp://, openid4vp://
|
|
23
|
+
* - Reference: OpenID4VP HAIP Draft
|
|
24
|
+
*
|
|
25
|
+
* **Annex A (EU Age Verification Profile):**
|
|
26
|
+
* - Used for: Proof of Age attestations
|
|
27
|
+
* - Client ID Scheme: redirect_uri (redirect URI as client identifier)
|
|
28
|
+
* - Request Format: Plain JSON (redirect_uri scheme forbids signed requests)
|
|
29
|
+
* - Response Mode: direct_post (plain VP token)
|
|
30
|
+
* - URL Schemes: av://
|
|
31
|
+
* - Reference: https://ageverification.dev/Technical%20Specification/annexes/annex-A/annex-A-av-profile
|
|
32
|
+
*
|
|
33
|
+
* Note: The key difference is client_id_scheme. HAIP uses x509_hash with signed JARs,
|
|
34
|
+
* while Annex A uses redirect_uri with plain JSON (signed requests are explicitly forbidden).
|
|
35
|
+
* The `x509_hash` scheme is preferred over `x509_san_dns` because it provides a direct
|
|
36
|
+
* cryptographic binding to the verifier's certificate, independent of DNS resolution.
|
|
37
|
+
*/
|
|
38
|
+
export declare const PROTOCOL_PROFILES: Record<ProfileId, ProtocolProfile>;
|
|
39
|
+
/**
|
|
40
|
+
* Credential type configurations based on ISO 18013-5, EU ARF, and EU Age Verification Profile.
|
|
41
|
+
*
|
|
42
|
+
* References:
|
|
43
|
+
* - mDL: ISO/IEC 18013-5:2021
|
|
44
|
+
* - PID: EU ARF Annex 2.02 Topic 3 (PID_04, PID_05)
|
|
45
|
+
* - Proof of Age: EU Age Verification Profile (https://ageverification.dev)
|
|
46
|
+
* - EUDI Wallet document categories: WalletCoreConfig.kt `documentCategories`
|
|
47
|
+
*
|
|
48
|
+
* Credential format identifiers:
|
|
49
|
+
* - `"mso_mdoc"` — ISO/IEC 18013-5 Mobile Documents (CBOR-encoded, namespace-based claims)
|
|
50
|
+
* - `"dc+sd-jwt"` — IETF SD-JWT VC (JSON-encoded, flat or nested claim paths)
|
|
51
|
+
*
|
|
52
|
+
* SD-JWT VC credential types use `vct` (Verifiable Credential Type) as the type
|
|
53
|
+
* identifier instead of `docType`/`namespace`. Claims use JSON paths instead of
|
|
54
|
+
* namespace-based paths. See OpenID4VP 1.0 §B.3 and draft-ietf-oauth-sd-jwt-vc-08.
|
|
55
|
+
*/
|
|
56
|
+
export declare const CREDENTIAL_TYPES: Record<CredentialType, CredentialTypeConfig>;
|
|
57
|
+
/**
|
|
58
|
+
* Get default selected claims for a credential type (first 5 claims).
|
|
59
|
+
*/
|
|
60
|
+
export declare function getDefaultClaims(credentialType: CredentialType): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Get claim definitions for a credential type.
|
|
63
|
+
*/
|
|
64
|
+
export declare function getClaimsForType(credentialType: CredentialType): ClaimDefinition[];
|
|
65
|
+
/**
|
|
66
|
+
* Get the protocol profile for a credential type.
|
|
67
|
+
*/
|
|
68
|
+
export declare function getProfileForType(credentialType: CredentialType): ProtocolProfile | null;
|
|
69
|
+
/**
|
|
70
|
+
* Get the profile ID for a credential type.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getProfileIdForType(credentialType: CredentialType): ProfileId;
|
package/dist/dcql.d.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ewqwe/digital-identity — DCQL Query Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions to build, parse, and convert DCQL (Digital Credentials Query Language)
|
|
5
|
+
* queries for EU Age Verification and OpenID4VP presentations.
|
|
6
|
+
*
|
|
7
|
+
* Node.js compatible — uses Node.js crypto API.
|
|
8
|
+
*
|
|
9
|
+
* @see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html §6–7
|
|
10
|
+
* @see https://ageverification.dev/Technical%20Specification/annexes/annex-A/annex-A-av-profile
|
|
11
|
+
*/
|
|
12
|
+
import type { CredentialType, InitTransactionRequest, ProfileId } from "./types.js";
|
|
13
|
+
/** EU Age Verification namespace. */
|
|
14
|
+
export declare const EU_AV_NAMESPACE = "eu.europa.ec.av.1";
|
|
15
|
+
/** EU Age Verification mDoc document type. */
|
|
16
|
+
export declare const EU_AV_DOCTYPE = "eu.europa.ec.av.1.mdoc";
|
|
17
|
+
/** ISO 18013-5 mDL namespace. */
|
|
18
|
+
export declare const ISO_MDL_NAMESPACE = "org.iso.18013.5.1";
|
|
19
|
+
/** ISO 18013-5 mDL document type. */
|
|
20
|
+
export declare const ISO_MDL_DOCTYPE = "org.iso.18013.5.1.mDL";
|
|
21
|
+
/** EU PID namespace. */
|
|
22
|
+
export declare const EU_PID_NAMESPACE = "eu.europa.ec.eudi.pid.1";
|
|
23
|
+
/** EU PID document type. */
|
|
24
|
+
export declare const EU_PID_DOCTYPE = "eu.europa.ec.eudi.pid.1";
|
|
25
|
+
/**
|
|
26
|
+
* A single entry in `trusted_authorities` — identifies an authority or trust framework
|
|
27
|
+
* that certifies credential issuers the Verifier will accept.
|
|
28
|
+
*
|
|
29
|
+
* A Credential matches if it satisfies **at least one** entry in the array.
|
|
30
|
+
*
|
|
31
|
+
* Type identifiers defined by OpenID4VP 1.0 §6.1.1:
|
|
32
|
+
* - `"aki"` — X.509 Authority Key Identifier, base64url-encoded (§6.1.1.1)
|
|
33
|
+
* - `"etsi_tl"` — ETSI Trusted List identifier (§6.1.1.2)
|
|
34
|
+
* - `"openid_federation"` — OpenID Federation Entity Identifier (§6.1.1.3)
|
|
35
|
+
*
|
|
36
|
+
* @see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-6.1.1
|
|
37
|
+
*/
|
|
38
|
+
export interface TrustedAuthoritiesQuery {
|
|
39
|
+
/** Type identifier for the trust framework. */
|
|
40
|
+
type: "aki" | "etsi_tl" | "openid_federation" | string;
|
|
41
|
+
/** Non-empty array of values interpreted according to `type`. */
|
|
42
|
+
values: string[];
|
|
43
|
+
}
|
|
44
|
+
/** A single claim constraint in a DCQL credential query. */
|
|
45
|
+
export interface DCQLClaimsQuery {
|
|
46
|
+
/** Claim identifier (for referencing in claim_sets). */
|
|
47
|
+
id?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Claims Path Pointer (OpenID4VP 1.0 §7).
|
|
50
|
+
*
|
|
51
|
+
* A **non-empty** array whose elements must be:
|
|
52
|
+
* - `string` — navigate into the named key of an object (§7.1)
|
|
53
|
+
* - `number` (non-negative integer) — select the element at this index in an array (§7.1)
|
|
54
|
+
* - `null` — select **all** elements of the currently selected array(s) (§7.1)
|
|
55
|
+
*
|
|
56
|
+
* For `mso_mdoc` credentials (§7.2) exactly two strings are required:
|
|
57
|
+
* `[namespace, dataElementIdentifier]`.
|
|
58
|
+
*
|
|
59
|
+
* Examples from §7.3:
|
|
60
|
+
* - `["address", "street_address"]` — nested object key
|
|
61
|
+
* - `["degrees", null, "type"]` — all `type` values across the `degrees` array
|
|
62
|
+
* - `["nationalities", 1]` — second element of the `nationalities` array
|
|
63
|
+
*/
|
|
64
|
+
path: (string | number | null)[];
|
|
65
|
+
/** Expected values — if provided, claim must match one of these (§6.3). */
|
|
66
|
+
values?: (string | number | boolean)[];
|
|
67
|
+
/** Whether to retain this claim after verification (mso_mdoc only, §B.2.4). */
|
|
68
|
+
intent_to_retain?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A single credential query in DCQL.
|
|
72
|
+
*
|
|
73
|
+
* Format identifiers per the OpenID4VP 1.0 spec (Appendix B):
|
|
74
|
+
* - `"mso_mdoc"` — ISO/IEC 18013-5 Mobile Documents (§B.2)
|
|
75
|
+
* - `"dc+sd-jwt"` — IETF SD-JWT VC (§B.3), canonical since Nov 2024
|
|
76
|
+
* (was `"vc+sd-jwt"` before Nov 2024; both SHOULD be accepted per
|
|
77
|
+
* draft-ietf-oauth-sd-jwt-vc-08 §3.2.1)
|
|
78
|
+
* - `"jwt_vc_json"` — W3C VC signed as JWT (§B.1.3.1)
|
|
79
|
+
* - `"ldp_vc"` — W3C VC with Linked Data Proofs (§B.1.3.2)
|
|
80
|
+
*
|
|
81
|
+
* @see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#appendix-B
|
|
82
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-08.html#section-3.2.1
|
|
83
|
+
*/
|
|
84
|
+
export interface DCQLCredentialQuery {
|
|
85
|
+
/** Unique identifier for this credential query. */
|
|
86
|
+
id: string;
|
|
87
|
+
/**
|
|
88
|
+
* Credential format identifier.
|
|
89
|
+
*
|
|
90
|
+
* `"mso_mdoc"` is for ISO/IEC 18013-5 Mobile Documents (mDL/mDoc).
|
|
91
|
+
*
|
|
92
|
+
* `"sd-jwt"` formats are for IETF SD-JWT Verifiable Credentials. The current
|
|
93
|
+
* IANA-registered identifier for SD-JWT VC is `"dc+sd-jwt"` (application/dc+sd-jwt),
|
|
94
|
+
* which is the canonical name to use going forward. The older `"vc+sd-jwt"`
|
|
95
|
+
* SHOULD also be accepted during the transitional period per draft-ietf-oauth-sd-jwt-vc-08 §3.2.1.
|
|
96
|
+
*
|
|
97
|
+
* `"jwt_vc_json"` is for W3C Verifiable Credentials signed as JWT without JSON-LD.
|
|
98
|
+
*
|
|
99
|
+
* `"ldp_vc"` is for W3C Verifiable Credentials with Linked Data Proofs.
|
|
100
|
+
*/
|
|
101
|
+
format: "mso_mdoc" | "dc+sd-jwt" | "vc+sd-jwt" | "jwt_vc_json" | "ldp_vc";
|
|
102
|
+
/** Format-specific metadata. */
|
|
103
|
+
meta?: {
|
|
104
|
+
/** Document type for mso_mdoc (e.g. "org.iso.18013.5.1.mDL"). */
|
|
105
|
+
doctype_value?: string;
|
|
106
|
+
/** Verifiable Credential Type values for dc+sd-jwt / vc+sd-jwt. */
|
|
107
|
+
vct_values?: string[];
|
|
108
|
+
/** Type values for jwt_vc_json / ldp_vc. */
|
|
109
|
+
type_values?: string[][];
|
|
110
|
+
};
|
|
111
|
+
/** Claims to request from the credential. */
|
|
112
|
+
claims?: DCQLClaimsQuery[];
|
|
113
|
+
/** Named subsets of claims; the wallet picks one set. */
|
|
114
|
+
claim_sets?: string[][];
|
|
115
|
+
/** Allow the wallet to return multiple matching credentials. */
|
|
116
|
+
multiple?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Expected authorities or trust frameworks that certify issuers the Verifier will accept.
|
|
119
|
+
*
|
|
120
|
+
* OPTIONAL non-empty array. Every Credential returned by the Wallet SHOULD match at least
|
|
121
|
+
* one of the conditions. The Verifier still bears its own responsibility to verify issuer
|
|
122
|
+
* trust; this field is a hint to the Wallet to avoid sending credentials likely to be
|
|
123
|
+
* rejected. See §6.1.1 for matching semantics per type.
|
|
124
|
+
*
|
|
125
|
+
* @see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-6.1.1
|
|
126
|
+
*/
|
|
127
|
+
trusted_authorities?: TrustedAuthoritiesQuery[];
|
|
128
|
+
/**
|
|
129
|
+
* Require cryptographic holder binding in the presentation.
|
|
130
|
+
*
|
|
131
|
+
* OPTIONAL. Default is `true` per §6.1 — a Verifiable Presentation with Cryptographic
|
|
132
|
+
* Holder Binding is required unless explicitly set to `false`.
|
|
133
|
+
*/
|
|
134
|
+
require_cryptographic_holder_binding?: boolean;
|
|
135
|
+
}
|
|
136
|
+
/** A credential set defining alternatives (OR logic). */
|
|
137
|
+
export interface DCQLCredentialSetQuery {
|
|
138
|
+
/** Which credential query options satisfy this set. */
|
|
139
|
+
options: string[][];
|
|
140
|
+
/** Whether this credential set is required (default: true). */
|
|
141
|
+
required?: boolean;
|
|
142
|
+
/** Human-readable purpose for this credential set. */
|
|
143
|
+
purpose?: string;
|
|
144
|
+
}
|
|
145
|
+
/** The complete DCQL query structure — OpenID4VP 1.0 §6. */
|
|
146
|
+
export interface DCQLQuery {
|
|
147
|
+
/** Array of credential queries. */
|
|
148
|
+
credentials: DCQLCredentialQuery[];
|
|
149
|
+
/** Optional credential sets for defining alternatives. */
|
|
150
|
+
credential_sets?: DCQLCredentialSetQuery[];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Build a minimal age verification query using the EU AV profile.
|
|
154
|
+
*
|
|
155
|
+
* @param ageThreshold - The age threshold to verify (e.g. 18, 21)
|
|
156
|
+
* @returns DCQL query requesting age_over_N = true
|
|
157
|
+
*/
|
|
158
|
+
export declare function buildAgeVerificationQuery(ageThreshold?: number): DCQLQuery;
|
|
159
|
+
/**
|
|
160
|
+
* Build an age verification query with mDL fallback.
|
|
161
|
+
*
|
|
162
|
+
* Uses credential_sets to allow either EU AV proof OR mDL.
|
|
163
|
+
*
|
|
164
|
+
* @param ageThreshold - The age threshold to verify (e.g., 18, 21)
|
|
165
|
+
* @returns DCQL query with EU AV primary and mDL fallback
|
|
166
|
+
*/
|
|
167
|
+
export declare function buildAgeVerificationQueryWithFallback(ageThreshold?: number): DCQLQuery;
|
|
168
|
+
/**
|
|
169
|
+
* Default DCQL query requesting age_over_18 from EU PID.
|
|
170
|
+
*/
|
|
171
|
+
export declare function getDefaultAgeVerificationDCQL(): DCQLQuery;
|
|
172
|
+
/**
|
|
173
|
+
* Build an `InitTransactionRequest` directly from a credential type and
|
|
174
|
+
* selected claims — the canonical way to initialize an OpenID4VP transaction.
|
|
175
|
+
*
|
|
176
|
+
* Builds a **DCQL query** directly (never a legacy `PresentationDefinition`),
|
|
177
|
+
* so no server-side conversion is required.
|
|
178
|
+
*
|
|
179
|
+
* @param publicUrl - The public URL the wallet is going to call back to
|
|
180
|
+
* @param credentialType - The type of credential to request
|
|
181
|
+
* @param selectedClaims - Array of claim IDs to include (e.g. `["age_over_18"]`)
|
|
182
|
+
* @returns `InitTransactionRequest` ready to POST to `/ewqwe_api/openid4vp/init`
|
|
183
|
+
*/
|
|
184
|
+
export declare function buildInitTransactionRequest(publicUrl: string, credentialType: CredentialType, selectedClaims: string[], profile?: ProfileId): InitTransactionRequest;
|
|
185
|
+
/**
|
|
186
|
+
* Determine the protocol profile based on credential type or explicit selection.
|
|
187
|
+
*
|
|
188
|
+
* - mDL and PID → HAIP (x509_san_dns, JAR signing, eudi-openid4vp://)
|
|
189
|
+
* - Proof of Age → Annex A (redirect_uri, plain request, av://)
|
|
190
|
+
*/
|
|
191
|
+
export declare function determineProfile(credentialType?: string, explicitProfile?: "haip" | "annex-a"): "haip" | "annex-a";
|
|
192
|
+
/**
|
|
193
|
+
* Generate a cryptographically secure nonce for OpenID4VP requests.
|
|
194
|
+
*
|
|
195
|
+
* @returns Base64URL-encoded random nonce
|
|
196
|
+
*/
|
|
197
|
+
export declare function generateNonce(): string;
|
|
198
|
+
/**
|
|
199
|
+
* Validate the structural rules of a DCQL query per OpenID4VP 1.0 §6 and §6.4.1.
|
|
200
|
+
*
|
|
201
|
+
* Rules enforced:
|
|
202
|
+
*
|
|
203
|
+
* **§6 — Top level**
|
|
204
|
+
* - `credentials` MUST be non-empty.
|
|
205
|
+
* - Credential query `id` values MUST be unique across `credentials`.
|
|
206
|
+
* - `credential_sets`, if present, MUST be non-empty.
|
|
207
|
+
* - Each `credential_sets` option element MUST reference a valid credential query `id`.
|
|
208
|
+
*
|
|
209
|
+
* **§6.1 — Credential Query**
|
|
210
|
+
* - Each credential `id` MUST be a non-empty string of alphanumeric, `-`, or `_`.
|
|
211
|
+
* - `trusted_authorities`, if present, MUST be non-empty.
|
|
212
|
+
*
|
|
213
|
+
* **§6.3 & §6.4.1 — Claims / claim_sets**
|
|
214
|
+
* - `claim_sets` MUST NOT be present when `claims` is absent.
|
|
215
|
+
* - Claim `id` values MUST be unique within a single `claims` array.
|
|
216
|
+
* - When `claim_sets` is present, every claim MUST have a non-empty `id`.
|
|
217
|
+
* - Every identifier referenced in `claim_sets` MUST appear in `claims`.
|
|
218
|
+
*
|
|
219
|
+
* @param query - The DCQL query to validate.
|
|
220
|
+
* @returns An object `{ valid: true }` or `{ valid: false, error: string }`.
|
|
221
|
+
*/
|
|
222
|
+
export declare function isValidDCQLQuery(query: DCQLQuery): {
|
|
223
|
+
valid: true;
|
|
224
|
+
} | {
|
|
225
|
+
valid: false;
|
|
226
|
+
error: string;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Parse a DCQL query from a string (typically from URL parameters).
|
|
230
|
+
*
|
|
231
|
+
* @param queryString - JSON string containing DCQL query
|
|
232
|
+
* @returns Parsed DCQL query or null if invalid
|
|
233
|
+
*/
|
|
234
|
+
export declare function parseDCQLQuery(queryString: string): DCQLQuery | null;
|
|
235
|
+
/**
|
|
236
|
+
* Extract the age threshold from a DCQL query.
|
|
237
|
+
*
|
|
238
|
+
* @param query - DCQL query to analyze
|
|
239
|
+
* @returns Age threshold (e.g. 18, 21) or null if not an age verification query
|
|
240
|
+
*/
|
|
241
|
+
export declare function extractAgeThreshold(query: DCQLQuery): number | null;
|
|
242
|
+
/**
|
|
243
|
+
* Build an OpenID4VP authorization request URL for same-device flow.
|
|
244
|
+
*/
|
|
245
|
+
export declare function buildAuthorizationRequest(params: {
|
|
246
|
+
rpDomain: string;
|
|
247
|
+
redirectUri: string;
|
|
248
|
+
ageThreshold?: number;
|
|
249
|
+
nonce?: string;
|
|
250
|
+
state?: string;
|
|
251
|
+
}): Record<string, string>;
|
|
252
|
+
/**
|
|
253
|
+
* Build an OpenID4VP authorization request for cross-device flow.
|
|
254
|
+
*
|
|
255
|
+
* Note: When using the HAIP profile with the EUDI Wallet, the client_id should be
|
|
256
|
+
* `x509_hash:<cert_hash>` instead of `x509_san_dns:<domain>` for a direct cryptographic
|
|
257
|
+
* binding to the verifier's certificate. The `x509_san_dns` form is maintained here for
|
|
258
|
+
* legacy compatibility and for deployments that still rely on DNS-based identification.
|
|
259
|
+
*/
|
|
260
|
+
export declare function buildCrossDeviceAuthorizationRequest(params: {
|
|
261
|
+
rpDomain: string;
|
|
262
|
+
responseUri: string;
|
|
263
|
+
ageThreshold?: number;
|
|
264
|
+
nonce?: string;
|
|
265
|
+
state?: string;
|
|
266
|
+
}): Record<string, string>;
|