qredential 0.2.0

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.
@@ -0,0 +1,141 @@
1
+ export type Alg = 'ES256' | 'EdDSA';
2
+ export interface Jwk {
3
+ kty: string;
4
+ crv?: string;
5
+ x?: string;
6
+ y?: string;
7
+ d?: string;
8
+ kid?: string;
9
+ alg?: string;
10
+ key_ops?: string[];
11
+ [key: string]: unknown;
12
+ }
13
+ export interface IssuerKey {
14
+ kid: string;
15
+ alg: Alg;
16
+ jwk: Jwk;
17
+ }
18
+ /**
19
+ * The set of issuers a verifier is willing to believe. This is the one thing that has to reach the
20
+ * device out of band. It changes rarely, so shipping it with the app and refreshing it weekly is a
21
+ * perfectly reasonable distribution strategy.
22
+ */
23
+ export interface TrustList {
24
+ issuers: Record<string, {
25
+ name?: string;
26
+ keys: IssuerKey[];
27
+ }>;
28
+ }
29
+ export interface StatusPointer {
30
+ /** Index of this credential's bit inside the issuer's status list. */
31
+ idx: number;
32
+ /** Where a verifier with connectivity can refresh the list. */
33
+ uri: string;
34
+ }
35
+ export interface IssueOptions {
36
+ issuer: string;
37
+ /** Private key in JWK form. It never leaves this call. */
38
+ key: Jwk;
39
+ kid: string;
40
+ alg?: Alg;
41
+ claims: Record<string, unknown>;
42
+ /** Claim names the holder may withhold at presentation time. Everything else is always visible. */
43
+ disclose?: string[];
44
+ /** Credential type, the `vct` of SD-JWT VC. */
45
+ vct?: string;
46
+ subject?: string;
47
+ /** Seconds, or a duration string like '30d'. */
48
+ expiresIn?: number | string;
49
+ notBefore?: number | string;
50
+ status?: StatusPointer;
51
+ /**
52
+ * The holder's **public** key, written into the `cnf` claim.
53
+ *
54
+ * This is what makes key binding possible later. Without it a presentation proves the issuer
55
+ * signed the claims but not that the person presenting is the subject, so a photograph of
56
+ * someone else's code works. Omit it only for credentials that are inherently static, such as one
57
+ * printed on a card, where no device can sign at scan time.
58
+ */
59
+ holderKey?: Jwk;
60
+ }
61
+ export interface IssueResult {
62
+ /** The SD-JWT combined form. Store this in the wallet. */
63
+ credential: string;
64
+ /** The scannable envelope. This is what goes into the QR code. */
65
+ qr: string;
66
+ /** Size of `qr` in characters, which is what the QR encoder actually has to fit. */
67
+ bytes: number;
68
+ /** Claim names that the holder is able to withhold. */
69
+ disclosable: string[];
70
+ }
71
+ export type FailReason = 'malformed'
72
+ /** The presentation carries no holder proof and the caller did not opt into accepting that. */
73
+ | 'holder_proof_missing'
74
+ /** A holder proof is present but does not hold up: wrong key, nonce, audience, age or contents. */
75
+ | 'holder_proof_invalid' | 'unsupported_alg' | 'unknown_issuer' | 'unknown_key' | 'bad_signature' | 'expired' | 'not_yet_valid' | 'digest_mismatch' | 'revoked' | 'status_list_stale' | 'status_unavailable';
76
+ /** What the holder's wallet signs at presentation time to prove the credential is theirs. */
77
+ export interface KeyBindingRequest {
78
+ /** The holder's **private** key. Must match the `cnf` key the issuer wrote in. */
79
+ key: Jwk;
80
+ /** Who is asking. Echoed into `aud` and checked by that verifier. */
81
+ audience: string;
82
+ /** The verifier's fresh challenge. This is what stops a recorded presentation being replayed. */
83
+ nonce: string;
84
+ alg?: Alg;
85
+ }
86
+ export interface VerifyOptions {
87
+ trust: TrustList;
88
+ /**
89
+ * The challenge this verifier issued for this scan. Required to accept a holder proof.
90
+ */
91
+ nonce?: string;
92
+ /** This verifier's own identifier, checked against the proof's `aud`. */
93
+ audience?: string;
94
+ /**
95
+ * Accept a presentation with no holder proof.
96
+ *
97
+ * Say yes only for credentials that are inherently static, such as one printed on a card, and
98
+ * know what it costs: anyone who photographs the code can present it. The result reports
99
+ * `holderVerified: false` either way, so the fact never disappears.
100
+ */
101
+ acceptWithoutHolderProof?: boolean;
102
+ /** How old a holder proof may be. Seconds or a duration string. Defaults to 5 minutes. */
103
+ maxKeyBindingAge?: number | string;
104
+ /** A cached status list token. Without it, a credential that points at one cannot be cleared. */
105
+ status?: string;
106
+ /** Refuse to answer from a status list older than this. Seconds or a duration string. */
107
+ maxStatusAge?: number | string;
108
+ /** Tolerance for clock drift between issuer and verifier, in seconds. Defaults to 60. */
109
+ clockSkew?: number;
110
+ /** Override the current time, in seconds since the epoch. Exists for tests and replay analysis. */
111
+ now?: number;
112
+ }
113
+ export interface VerifiedCredential {
114
+ ok: true;
115
+ claims: Record<string, unknown>;
116
+ issuer: string;
117
+ subject?: string;
118
+ /** Credential type from the `vct` claim, when the issuer set one. */
119
+ vct?: string;
120
+ issuedAt?: number;
121
+ expiresAt?: number;
122
+ /** Names of the selectively disclosable claims the holder chose to reveal. */
123
+ disclosed: string[];
124
+ /** How many disclosable claims were withheld. Useful for policy, never for identifying them. */
125
+ withheld: number;
126
+ /** False when no status list was consulted, so the caller knows revocation was not checked. */
127
+ revocationChecked: boolean;
128
+ /**
129
+ * True when the holder proved possession of the key the issuer bound to this credential.
130
+ *
131
+ * False means the credential is authentic but anyone holding a copy could have presented it.
132
+ * That is a legitimate state for a static credential, and it is reported rather than hidden.
133
+ */
134
+ holderVerified: boolean;
135
+ }
136
+ export interface RejectedCredential {
137
+ ok: false;
138
+ reason: FailReason;
139
+ message: string;
140
+ }
141
+ export type VerifyResult = VerifiedCredential | RejectedCredential;
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "qredential",
3
+ "version": "0.2.0",
4
+ "description": "Verify a digital credential from a QR code with no network connection. SD-JWT selective disclosure, offline revocation, zero dependencies.",
5
+ "homepage": "https://qredential.js.org/",
6
+ "bugs": {
7
+ "url": "https://github.com/george-veras/qredential/issues"
8
+ },
9
+ "type": "module",
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.build.json",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "typecheck": "tsc --noEmit",
28
+ "build:site": "node scripts/build-site.mjs",
29
+ "check:site": "node scripts/build-site.mjs && git diff --exit-code docs/",
30
+ "prepack": "npm run build",
31
+ "prepublishOnly": "npm run typecheck && npm test",
32
+ "test:browser": "vitest run --config vitest.browser.config.ts",
33
+ "check:translations": "CHECK_TRANSLATIONS=1 node scripts/build-site.mjs",
34
+ "build:og": "node scripts/build-og.mjs",
35
+ "check:playground": "node scripts/check-playground.mjs",
36
+ "check:seo": "node scripts/check-seo.mjs",
37
+ "check:a11y": "node scripts/check-a11y.mjs"
38
+ },
39
+ "keywords": [
40
+ "verifiable-credentials",
41
+ "sd-jwt",
42
+ "selective-disclosure",
43
+ "qr",
44
+ "offline",
45
+ "mdl",
46
+ "age-verification",
47
+ "identity",
48
+ "base45",
49
+ "status-list",
50
+ "rfc9901",
51
+ "key-binding",
52
+ "token-status-list",
53
+ "eudi-wallet",
54
+ "kb-jwt",
55
+ "jwt"
56
+ ],
57
+ "author": "George Veras Valentim",
58
+ "license": "MIT",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/george-veras/qredential.git"
62
+ },
63
+ "engines": {
64
+ "node": ">=20"
65
+ },
66
+ "devDependencies": {
67
+ "@sd-jwt/core": "^0.19.0",
68
+ "@sd-jwt/crypto-nodejs": "^0.19.0",
69
+ "@types/node": "^22.10.0",
70
+ "@vitest/browser": "^5.0.1",
71
+ "@vitest/browser-playwright": "^5.0.1",
72
+ "axe-core": "^4.13.0",
73
+ "esbuild": "^0.28.2",
74
+ "fast-check": "^3.23.2",
75
+ "marked": "^15.0.12",
76
+ "playwright": "^1.63.0",
77
+ "qrcode-generator": "^1.5.2",
78
+ "typescript": "^5.7.0",
79
+ "vitest": "^5.0.1"
80
+ }
81
+ }