antifraud-sdk-core-ts 0.0.0-stage → 0.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/CHANGELOG.md +23 -0
- package/LICENSE +167 -0
- package/README.md +91 -2
- package/dist/credential-issuance-controller.d.ts +18 -0
- package/dist/credential-issuance-controller.d.ts.map +1 -0
- package/dist/credential-issuance-controller.js +42 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/openid4vp.d.ts +58 -0
- package/dist/openid4vp.d.ts.map +1 -0
- package/dist/openid4vp.js +76 -0
- package/dist/presentation-controller.d.ts +57 -0
- package/dist/presentation-controller.d.ts.map +1 -0
- package/dist/presentation-controller.js +37 -0
- package/dist/presentation-session.d.ts +16 -0
- package/dist/presentation-session.d.ts.map +1 -0
- package/dist/presentation-session.js +32 -0
- package/dist/qualification-evidence-controller.d.ts +121 -0
- package/dist/qualification-evidence-controller.d.ts.map +1 -0
- package/dist/qualification-evidence-controller.js +126 -0
- package/package.json +37 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.1 - 2026-10-17
|
|
6
|
+
|
|
7
|
+
- Add runtime-neutral authoritative credential issuance orchestration using
|
|
8
|
+
canonical data-utils contracts, opaque polling continuity and terminal-state
|
|
9
|
+
enforcement.
|
|
10
|
+
|
|
11
|
+
- Add runtime-neutral employer qualification-evidence orchestration, issuer
|
|
12
|
+
discovery, privacy-minimized confirmation requests, issuer-owned decisions
|
|
13
|
+
and reverse decision notifications.
|
|
14
|
+
- Document canonical Schema.org `MediaObject` evidence, its OpenID Identity
|
|
15
|
+
Assurance attachment projection, SHA3-384 CID anchoring, tenant-to-tenant
|
|
16
|
+
delivery, ledger recovery and W3C credential rendering.
|
|
17
|
+
|
|
18
|
+
- Add runtime-neutral OpenID4VP DCQL profiles, opaque presentation-session storage and presentation orchestration.
|
|
19
|
+
- Document reusable workforce authorization as active group membership plus a
|
|
20
|
+
controller-managed group authorization, with optional subject authorization
|
|
21
|
+
kept separate and no employee-specific Consent.
|
|
22
|
+
- Keep awarding-organization resolution, employer attestation, and
|
|
23
|
+
source-issued qualification proof as distinct orchestration states.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Copyright 2026 Antifraud Services Inc.
|
|
2
|
+
|
|
3
|
+
Apache License
|
|
4
|
+
Version 2.0, January 2004
|
|
5
|
+
http://www.apache.org/licenses/
|
|
6
|
+
|
|
7
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
8
|
+
|
|
9
|
+
1. Definitions.
|
|
10
|
+
|
|
11
|
+
"License" shall mean the terms and conditions for use, reproduction, and
|
|
12
|
+
distribution as defined by Sections 1 through 9 of this document.
|
|
13
|
+
|
|
14
|
+
"Licensor" shall mean the copyright owner or entity authorized by the
|
|
15
|
+
copyright owner that is granting the License.
|
|
16
|
+
|
|
17
|
+
"Legal Entity" shall mean the union of the acting entity and all other
|
|
18
|
+
entities that control, are controlled by, or are under common control with
|
|
19
|
+
that entity. For the purposes of this definition, "control" means (i) the
|
|
20
|
+
power, direct or indirect, to cause the direction or management of such
|
|
21
|
+
entity, whether by contract or otherwise, or (ii) ownership of fifty percent
|
|
22
|
+
(50%) or more of the outstanding shares, or (iii) beneficial ownership of
|
|
23
|
+
such entity.
|
|
24
|
+
|
|
25
|
+
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
|
26
|
+
permissions granted by this License.
|
|
27
|
+
|
|
28
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
29
|
+
including but not limited to software source code, documentation source,
|
|
30
|
+
and configuration files.
|
|
31
|
+
|
|
32
|
+
"Object" form shall mean any form resulting from mechanical transformation
|
|
33
|
+
or translation of a Source form, including but not limited to compiled
|
|
34
|
+
object code, generated documentation, and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or Object form,
|
|
37
|
+
made available under the License, as indicated by a copyright notice that is
|
|
38
|
+
included in or attached to the work (an example is provided in the Appendix
|
|
39
|
+
below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object form,
|
|
42
|
+
that is based on (or derived from) the Work and for which the editorial
|
|
43
|
+
revisions, annotations, elaborations, or other modifications represent, as
|
|
44
|
+
a whole, an original work of authorship. For the purposes of this License,
|
|
45
|
+
Derivative Works shall not include works that remain separable from, or
|
|
46
|
+
merely link (or bind by name) to the interfaces of, the Work and Derivative
|
|
47
|
+
Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including the original
|
|
50
|
+
version of the Work and any modifications or additions to that Work or
|
|
51
|
+
Derivative Works thereof, that is intentionally submitted to Licensor for
|
|
52
|
+
inclusion in the Work by the copyright owner or by an individual or Legal
|
|
53
|
+
Entity authorized to submit on behalf of the copyright owner. For the
|
|
54
|
+
purposes of this definition, "submitted" means any form of electronic,
|
|
55
|
+
verbal, or written communication sent to the Licensor or its representatives,
|
|
56
|
+
including but not limited to communication on electronic mailing lists,
|
|
57
|
+
source code control systems, and issue tracking systems that are managed by,
|
|
58
|
+
or on behalf of, the Licensor for the purpose of discussing and improving
|
|
59
|
+
the Work, but excluding communication that is conspicuously marked or
|
|
60
|
+
otherwise designated in writing by the copyright owner as "Not a
|
|
61
|
+
Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity on
|
|
64
|
+
behalf of whom a Contribution has been received by Licensor and subsequently
|
|
65
|
+
incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of this
|
|
68
|
+
License, each Contributor hereby grants to You a perpetual, worldwide,
|
|
69
|
+
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
|
|
70
|
+
reproduce, prepare Derivative Works of, publicly display, publicly perform,
|
|
71
|
+
sublicense, and distribute the Work and such Derivative Works in Source or
|
|
72
|
+
Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of this
|
|
75
|
+
License, each Contributor hereby grants to You a perpetual, worldwide,
|
|
76
|
+
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
|
|
77
|
+
this section) patent license to make, have made, use, offer to sell, sell,
|
|
78
|
+
import, and otherwise transfer the Work, where such license applies only to
|
|
79
|
+
those patent claims licensable by such Contributor that are necessarily
|
|
80
|
+
infringed by their Contribution(s) alone or by combination of their
|
|
81
|
+
Contribution(s) with the Work to which such Contribution(s) was submitted.
|
|
82
|
+
If You institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
|
84
|
+
Contribution incorporated within the Work constitutes direct or
|
|
85
|
+
contributory patent infringement, then any patent licenses granted to You
|
|
86
|
+
under this License for that Work shall terminate as of the date such
|
|
87
|
+
litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the Work or
|
|
90
|
+
Derivative Works thereof in any medium, with or without modifications, and
|
|
91
|
+
in Source or Object form, provided that You meet the following conditions:
|
|
92
|
+
|
|
93
|
+
(a) You must give any other recipients of the Work or Derivative Works a copy
|
|
94
|
+
of this License; and
|
|
95
|
+
|
|
96
|
+
(b) You must cause any modified files to carry prominent notices stating that
|
|
97
|
+
You changed the files; and
|
|
98
|
+
|
|
99
|
+
(c) You must retain, in the Source form of any Derivative Works that You
|
|
100
|
+
distribute, all copyright, patent, trademark, and attribution notices from
|
|
101
|
+
the Source form of the Work, excluding those notices that do not pertain to
|
|
102
|
+
any part of the Derivative Works; and
|
|
103
|
+
|
|
104
|
+
(d) If the Work includes a "NOTICE" text file as part of its distribution,
|
|
105
|
+
then any Derivative Works that You distribute must include a readable copy of
|
|
106
|
+
the attribution notices contained within such NOTICE file, excluding those
|
|
107
|
+
notices that do not pertain to any part of the Derivative Works, in at least
|
|
108
|
+
one of the following places: within a NOTICE text file distributed as part
|
|
109
|
+
of the Derivative Works; within the Source form or documentation, if provided
|
|
110
|
+
along with the Derivative Works; or, within a display generated by the
|
|
111
|
+
Derivative Works, if and wherever such third-party notices normally appear.
|
|
112
|
+
The contents of the NOTICE file are for informational purposes only and do
|
|
113
|
+
not modify the License. You may add Your own attribution notices within
|
|
114
|
+
Derivative Works that You distribute, alongside or as an addendum to the
|
|
115
|
+
NOTICE text from the Work, provided that such additional attribution notices
|
|
116
|
+
cannot be construed as modifying the License.
|
|
117
|
+
|
|
118
|
+
You may add Your own copyright statement to Your modifications and may
|
|
119
|
+
provide additional or different license terms and conditions for use,
|
|
120
|
+
reproduction, or distribution of Your modifications, or for any such
|
|
121
|
+
Derivative Works as a whole, provided Your use, reproduction, and
|
|
122
|
+
distribution of the Work otherwise complies with the conditions stated in
|
|
123
|
+
this License.
|
|
124
|
+
|
|
125
|
+
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
|
126
|
+
Contribution intentionally submitted for inclusion in the Work by You to the
|
|
127
|
+
Licensor shall be under the terms and conditions of this License, without
|
|
128
|
+
any additional terms or conditions. Notwithstanding the above, nothing
|
|
129
|
+
herein shall supersede or modify the terms of any separate license agreement
|
|
130
|
+
you may have executed with Licensor regarding such Contributions.
|
|
131
|
+
|
|
132
|
+
6. Trademarks. This License does not grant permission to use the trade names,
|
|
133
|
+
trademarks, service marks, or product names of the Licensor, except as
|
|
134
|
+
required for reasonable and customary use in describing the origin of the
|
|
135
|
+
Work and reproducing the content of the NOTICE file.
|
|
136
|
+
|
|
137
|
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
|
|
138
|
+
writing, Licensor provides the Work (and each Contributor provides its
|
|
139
|
+
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
140
|
+
KIND, either express or implied, including, without limitation, any
|
|
141
|
+
warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
|
|
142
|
+
FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
|
|
143
|
+
the appropriateness of using or redistributing the Work and assume any risks
|
|
144
|
+
associated with Your exercise of permissions under this License.
|
|
145
|
+
|
|
146
|
+
8. Limitation of Liability. In no event and under no legal theory, whether
|
|
147
|
+
in tort (including negligence), contract, or otherwise, unless required by
|
|
148
|
+
applicable law (such as deliberate and grossly negligent acts) or agreed to
|
|
149
|
+
in writing, shall any Contributor be liable to You for damages, including any
|
|
150
|
+
direct, indirect, special, incidental, or consequential damages of any
|
|
151
|
+
character arising as a result of this License or out of the use or inability
|
|
152
|
+
to use the Work (including but not limited to damages for loss of goodwill,
|
|
153
|
+
work stoppage, computer failure or malfunction, or any and all other
|
|
154
|
+
commercial damages or losses), even if such Contributor has been advised of
|
|
155
|
+
the possibility of such damages.
|
|
156
|
+
|
|
157
|
+
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
|
158
|
+
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
|
159
|
+
acceptance of support, warranty, indemnity, or other liability obligations
|
|
160
|
+
and/or rights consistent with this License. However, in accepting such
|
|
161
|
+
obligations, You may act only on Your own behalf and on Your sole
|
|
162
|
+
responsibility, not on behalf of any other Contributor, and only if You agree
|
|
163
|
+
to indemnify, defend, and hold each Contributor harmless for any liability
|
|
164
|
+
incurred by, or claims asserted against, such Contributor by reason of your
|
|
165
|
+
accepting any such warranty or additional liability.
|
|
166
|
+
|
|
167
|
+
END OF TERMS AND CONDITIONS
|
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Antifraud SDK Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime-neutral orchestration contracts for identity verification, subject and relationship attestation, and provider-issued credentials.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Public implementation foundation. No package has been published and no production readiness is claimed yet.
|
|
8
|
+
|
|
9
|
+
## Responsibility
|
|
10
|
+
|
|
11
|
+
This package will coordinate application flows without depending on Node.js, React, a database, a ledger, a tenant, or a product.
|
|
12
|
+
|
|
13
|
+
It will reuse the canonical claims, JSON Schemas, mappings, and capability identifiers from [antifraud-data-utils-ts](https://github.com/antifraudservices/antifraud-data-utils-ts). It must not duplicate those literals.
|
|
14
|
+
|
|
15
|
+
The intended flow is:
|
|
16
|
+
|
|
17
|
+
1. A user interface or backend for frontend starts an identity presentation.
|
|
18
|
+
2. A trusted verifier validates the presentation and returns an opaque transaction result.
|
|
19
|
+
3. An authorized index provider reads the subject or relationship again from its authoritative source.
|
|
20
|
+
4. The index provider issues and signs the resulting credential.
|
|
21
|
+
5. The caller receives the credential or a stable reference, but never the provider signing key.
|
|
22
|
+
|
|
23
|
+
OpenID for Verifiable Presentations starts and verifies a wallet presentation. Credential issuance is a separate provider operation and may use OpenID for Verifiable Credential Issuance or another explicit issuer protocol. The two operations must never be described as one.
|
|
24
|
+
|
|
25
|
+
## Planned public API
|
|
26
|
+
|
|
27
|
+
- verification and attestation use-case ports;
|
|
28
|
+
- wallet-presentation session orchestration;
|
|
29
|
+
- authoritative-readback request and result types;
|
|
30
|
+
- credential-issuance request orchestration;
|
|
31
|
+
- deterministic flow states and errors;
|
|
32
|
+
- no transport, storage, framework, or signing implementation.
|
|
33
|
+
|
|
34
|
+
## Security boundary
|
|
35
|
+
|
|
36
|
+
No secrets, tenant configuration, trust anchors, personal data, signing keys, or operational endpoints belong in this repository.
|
|
37
|
+
|
|
38
|
+
The browser and a generic backend for frontend are untrusted for credential signing. Only the configured credential provider may sign after an authoritative readback and authorization check.
|
|
39
|
+
|
|
40
|
+
## Workforce group authorization
|
|
41
|
+
|
|
42
|
+
An employee does not receive a separate Consent merely by joining an
|
|
43
|
+
operational group. Orchestration must receive and keep distinct:
|
|
44
|
+
|
|
45
|
+
1. the employee's active assignment and active membership in the exact group;
|
|
46
|
+
2. the controller-managed authorization for that group and its exact
|
|
47
|
+
capabilities;
|
|
48
|
+
3. when the use case requires it, a separate authorization from the subject or
|
|
49
|
+
lawful representative.
|
|
50
|
+
|
|
51
|
+
Adding or removing one employee changes membership only. It must not recreate
|
|
52
|
+
or revoke the group authorization for the remaining members. Invitation,
|
|
53
|
+
acceptance, department or group reassignment, suspension, and reactivation are
|
|
54
|
+
separate assignment events that applications notify through Communication.
|
|
55
|
+
Those notifications are not authorization records.
|
|
56
|
+
|
|
57
|
+
The same orchestration contract can serve education, insurance, human health,
|
|
58
|
+
animal care, or other domains. Product applications select their own group
|
|
59
|
+
purposes and subject-authorization rules; this package must not embed them.
|
|
60
|
+
|
|
61
|
+
For employee qualification evidence, orchestration keeps organization
|
|
62
|
+
resolution, employer attestation, and source-issued credential proof as
|
|
63
|
+
separate states. An awarding organization is selected by legal identifier and
|
|
64
|
+
jurisdiction. If no source credential exists, the employer may sign its own
|
|
65
|
+
attestation but must not be represented as the awarding organization's issuer.
|
|
66
|
+
|
|
67
|
+
The implemented qualification-evidence controller records the employer's proof
|
|
68
|
+
through a trusted port, resolves the claimed issuer by legal identifier and
|
|
69
|
+
jurisdiction, anchors only opaque continuity, and queues a tenant-to-tenant
|
|
70
|
+
confirmation request. See [Qualification evidence confirmation prototype](docs/QUALIFICATION_EVIDENCE_CONFIRMATION.md)
|
|
71
|
+
for attachment, issuer notification, ledger recovery and credential-rendering
|
|
72
|
+
boundaries.
|
|
73
|
+
|
|
74
|
+
## Dependency direction
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
antifraud-data-utils-ts
|
|
78
|
+
↓
|
|
79
|
+
antifraud-sdk-core-ts
|
|
80
|
+
↓
|
|
81
|
+
antifraud-sdk-node-ts / antifraud-portal-components-react
|
|
82
|
+
↓
|
|
83
|
+
applications and services
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Implementation gate
|
|
87
|
+
|
|
88
|
+
Implementation begins with executable contract tests and proceeds in dependency order. A repository, type name, mock, or successful HTTP response is not evidence that a credential was verified or issued.
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
Apache-2.0.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type CredentialIssuanceReceipt, type CredentialIssuanceRequest } from 'antifraud-data-utils-ts';
|
|
2
|
+
export interface CredentialIssuanceProviderPort {
|
|
3
|
+
requestIssuance(request: CredentialIssuanceRequest): Promise<CredentialIssuanceReceipt>;
|
|
4
|
+
getIssuance(issuanceId: string): Promise<CredentialIssuanceReceipt>;
|
|
5
|
+
}
|
|
6
|
+
export interface CredentialIssuanceControllerOptions {
|
|
7
|
+
provider: CredentialIssuanceProviderPort;
|
|
8
|
+
wait(milliseconds: number): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface CredentialIssuancePollingOptions {
|
|
11
|
+
maxPollAttempts?: number;
|
|
12
|
+
pollIntervalMilliseconds?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CredentialIssuanceController {
|
|
15
|
+
issue(request: CredentialIssuanceRequest & Record<string, unknown>, polling?: CredentialIssuancePollingOptions): Promise<CredentialIssuanceReceipt>;
|
|
16
|
+
}
|
|
17
|
+
export declare function createCredentialIssuanceController(options: CredentialIssuanceControllerOptions): CredentialIssuanceController;
|
|
18
|
+
//# sourceMappingURL=credential-issuance-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-issuance-controller.d.ts","sourceRoot":"","sources":["../src/credential-issuance-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC/B,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,8BAA8B;IAC7C,eAAe,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;IACvF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;CACpE;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,EAAE,8BAA8B,CAAA;IACxC,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,gCAAgC;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,wBAAwB,CAAC,EAAE,MAAM,CAAA;CAClC;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CACH,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,OAAO,CAAC,EAAE,gCAAgC,GACzC,OAAO,CAAC,yBAAyB,CAAC,CAAA;CACtC;AAwBD,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,mCAAmC,GAC3C,4BAA4B,CAgC9B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CredentialIssuanceStatus, defineCredentialIssuanceRequest, } from 'antifraud-data-utils-ts';
|
|
2
|
+
function isTerminal(receipt) {
|
|
3
|
+
return receipt.status === CredentialIssuanceStatus.Issued
|
|
4
|
+
|| receipt.status === CredentialIssuanceStatus.Denied
|
|
5
|
+
|| receipt.status === CredentialIssuanceStatus.Failed;
|
|
6
|
+
}
|
|
7
|
+
function positiveInteger(value, fallback, name) {
|
|
8
|
+
const resolved = value ?? fallback;
|
|
9
|
+
if (!Number.isSafeInteger(resolved) || resolved <= 0) {
|
|
10
|
+
throw new TypeError(`${name} must be a positive safe integer`);
|
|
11
|
+
}
|
|
12
|
+
return resolved;
|
|
13
|
+
}
|
|
14
|
+
function nonNegativeInteger(value, fallback) {
|
|
15
|
+
const resolved = value ?? fallback;
|
|
16
|
+
if (!Number.isSafeInteger(resolved) || resolved < 0) {
|
|
17
|
+
throw new TypeError('pollIntervalMilliseconds must be a non-negative safe integer');
|
|
18
|
+
}
|
|
19
|
+
return resolved;
|
|
20
|
+
}
|
|
21
|
+
export function createCredentialIssuanceController(options) {
|
|
22
|
+
return Object.freeze({
|
|
23
|
+
async issue(request, polling = {}) {
|
|
24
|
+
const canonicalRequest = defineCredentialIssuanceRequest(request);
|
|
25
|
+
const initial = await options.provider.requestIssuance(canonicalRequest);
|
|
26
|
+
if (isTerminal(initial))
|
|
27
|
+
return initial;
|
|
28
|
+
const maxPollAttempts = positiveInteger(polling.maxPollAttempts, 8, 'maxPollAttempts');
|
|
29
|
+
const pollIntervalMilliseconds = nonNegativeInteger(polling.pollIntervalMilliseconds, 1_000);
|
|
30
|
+
for (let attempt = 0; attempt < maxPollAttempts; attempt += 1) {
|
|
31
|
+
await options.wait(pollIntervalMilliseconds);
|
|
32
|
+
const current = await options.provider.getIssuance(initial.issuanceId);
|
|
33
|
+
if (current.issuanceId !== initial.issuanceId) {
|
|
34
|
+
throw new Error('Credential provider changed issuance identifier during polling');
|
|
35
|
+
}
|
|
36
|
+
if (isTerminal(current))
|
|
37
|
+
return current;
|
|
38
|
+
}
|
|
39
|
+
throw new Error('Credential issuance remained pending after the configured poll limit');
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './openid4vp.js';
|
|
2
|
+
export * from './credential-issuance-controller.js';
|
|
3
|
+
export * from './presentation-controller.js';
|
|
4
|
+
export * from './presentation-session.js';
|
|
5
|
+
export * from './qualification-evidence-controller.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qCAAqC,CAAA;AACnD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,wCAAwC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const OPENID4VP_FORMAT: {
|
|
2
|
+
readonly msoMdoc: "mso_mdoc";
|
|
3
|
+
readonly sdJwtVc: "dc+sd-jwt";
|
|
4
|
+
};
|
|
5
|
+
export declare const EUDI_PID: {
|
|
6
|
+
readonly mdocType: "eu.europa.ec.eudi.pid.1";
|
|
7
|
+
readonly sdJwtVcType: "urn:eudi:pid:1";
|
|
8
|
+
readonly givenName: "given_name";
|
|
9
|
+
readonly familyName: "family_name";
|
|
10
|
+
readonly birthDate: "birth_date";
|
|
11
|
+
};
|
|
12
|
+
export declare const ISO_MDL: {
|
|
13
|
+
readonly documentType: "org.iso.18013.5.1.mDL";
|
|
14
|
+
readonly namespace: "org.iso.18013.5.1";
|
|
15
|
+
readonly givenName: "given_name";
|
|
16
|
+
readonly familyName: "family_name";
|
|
17
|
+
readonly birthDate: "birth_date";
|
|
18
|
+
};
|
|
19
|
+
export type OpenId4VpProfileId = 'eudi-pid' | 'mdl';
|
|
20
|
+
export interface OpenId4VpProfile {
|
|
21
|
+
id: OpenId4VpProfileId;
|
|
22
|
+
label: string;
|
|
23
|
+
claimLabels: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export declare const OPENID4VP_PROFILES: {
|
|
26
|
+
readonly eudiPid: {
|
|
27
|
+
readonly id: "eudi-pid";
|
|
28
|
+
readonly label: "EUDI Personal Identification Data";
|
|
29
|
+
readonly claimLabels: readonly ["Given name", "Family name", "Date of birth"];
|
|
30
|
+
};
|
|
31
|
+
readonly mobileDrivingLicence: {
|
|
32
|
+
readonly id: "mdl";
|
|
33
|
+
readonly label: "Mobile Driving Licence";
|
|
34
|
+
readonly claimLabels: readonly ["Given name", "Family name", "Date of birth"];
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export interface DcqlClaim {
|
|
38
|
+
path: readonly string[];
|
|
39
|
+
intent_to_retain?: false;
|
|
40
|
+
}
|
|
41
|
+
export interface DcqlCredential {
|
|
42
|
+
id: string;
|
|
43
|
+
format: (typeof OPENID4VP_FORMAT)[keyof typeof OPENID4VP_FORMAT];
|
|
44
|
+
meta: {
|
|
45
|
+
doctype_value: string;
|
|
46
|
+
} | {
|
|
47
|
+
vct_values: readonly string[];
|
|
48
|
+
};
|
|
49
|
+
claims: readonly DcqlClaim[];
|
|
50
|
+
}
|
|
51
|
+
export interface DcqlQuery {
|
|
52
|
+
credentials: readonly DcqlCredential[];
|
|
53
|
+
credential_sets?: readonly {
|
|
54
|
+
options: readonly (readonly string[])[];
|
|
55
|
+
}[];
|
|
56
|
+
}
|
|
57
|
+
export declare function buildDcqlQuery(profile: OpenId4VpProfile): DcqlQuery;
|
|
58
|
+
//# sourceMappingURL=openid4vp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openid4vp.d.ts","sourceRoot":"","sources":["../src/openid4vp.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB;;;CAGnB,CAAA;AAEV,eAAO,MAAM,QAAQ;;;;;;CAMX,CAAA;AAEV,eAAO,MAAM,OAAO;;;;;;CAMV,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,KAAK,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,kBAAkB,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CAC/B;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;CAWsB,CAAA;AAErD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,gBAAgB,CAAC,EAAE,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAA;IAChE,IAAI,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;IACnE,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,SAAS,cAAc,EAAE,CAAA;IACtC,eAAe,CAAC,EAAE,SAAS;QAAE,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAA;KAAE,EAAE,CAAA;CACzE;AAMD,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA2CnE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const OPENID4VP_FORMAT = {
|
|
2
|
+
msoMdoc: 'mso_mdoc',
|
|
3
|
+
sdJwtVc: 'dc+sd-jwt',
|
|
4
|
+
};
|
|
5
|
+
export const EUDI_PID = {
|
|
6
|
+
mdocType: 'eu.europa.ec.eudi.pid.1',
|
|
7
|
+
sdJwtVcType: 'urn:eudi:pid:1',
|
|
8
|
+
givenName: 'given_name',
|
|
9
|
+
familyName: 'family_name',
|
|
10
|
+
birthDate: 'birth_date',
|
|
11
|
+
};
|
|
12
|
+
export const ISO_MDL = {
|
|
13
|
+
documentType: 'org.iso.18013.5.1.mDL',
|
|
14
|
+
namespace: 'org.iso.18013.5.1',
|
|
15
|
+
givenName: 'given_name',
|
|
16
|
+
familyName: 'family_name',
|
|
17
|
+
birthDate: 'birth_date',
|
|
18
|
+
};
|
|
19
|
+
export const OPENID4VP_PROFILES = {
|
|
20
|
+
eudiPid: {
|
|
21
|
+
id: 'eudi-pid',
|
|
22
|
+
label: 'EUDI Personal Identification Data',
|
|
23
|
+
claimLabels: ['Given name', 'Family name', 'Date of birth'],
|
|
24
|
+
},
|
|
25
|
+
mobileDrivingLicence: {
|
|
26
|
+
id: 'mdl',
|
|
27
|
+
label: 'Mobile Driving Licence',
|
|
28
|
+
claimLabels: ['Given name', 'Family name', 'Date of birth'],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
function mdocClaim(namespace, claim) {
|
|
32
|
+
return { path: [namespace, claim], intent_to_retain: false };
|
|
33
|
+
}
|
|
34
|
+
export function buildDcqlQuery(profile) {
|
|
35
|
+
if (profile.id === 'eudi-pid') {
|
|
36
|
+
return {
|
|
37
|
+
credentials: [
|
|
38
|
+
{
|
|
39
|
+
id: 'pid_mdoc',
|
|
40
|
+
format: OPENID4VP_FORMAT.msoMdoc,
|
|
41
|
+
meta: { doctype_value: EUDI_PID.mdocType },
|
|
42
|
+
claims: [
|
|
43
|
+
mdocClaim(EUDI_PID.mdocType, EUDI_PID.givenName),
|
|
44
|
+
mdocClaim(EUDI_PID.mdocType, EUDI_PID.familyName),
|
|
45
|
+
mdocClaim(EUDI_PID.mdocType, EUDI_PID.birthDate),
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'pid_sd_jwt',
|
|
50
|
+
format: OPENID4VP_FORMAT.sdJwtVc,
|
|
51
|
+
meta: { vct_values: [EUDI_PID.sdJwtVcType] },
|
|
52
|
+
claims: [
|
|
53
|
+
{ path: [EUDI_PID.givenName] },
|
|
54
|
+
{ path: [EUDI_PID.familyName] },
|
|
55
|
+
{ path: [EUDI_PID.birthDate] },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
credential_sets: [{ options: [['pid_mdoc'], ['pid_sd_jwt']] }],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
credentials: [
|
|
64
|
+
{
|
|
65
|
+
id: 'mdl',
|
|
66
|
+
format: OPENID4VP_FORMAT.msoMdoc,
|
|
67
|
+
meta: { doctype_value: ISO_MDL.documentType },
|
|
68
|
+
claims: [
|
|
69
|
+
mdocClaim(ISO_MDL.namespace, ISO_MDL.givenName),
|
|
70
|
+
mdocClaim(ISO_MDL.namespace, ISO_MDL.familyName),
|
|
71
|
+
mdocClaim(ISO_MDL.namespace, ISO_MDL.birthDate),
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OpenId4VpProfile } from './openid4vp.js';
|
|
2
|
+
import type { PresentationSession } from './presentation-session.js';
|
|
3
|
+
export interface PresentationTransaction {
|
|
4
|
+
transaction_id: string;
|
|
5
|
+
client_id: string;
|
|
6
|
+
request_uri: string;
|
|
7
|
+
request_uri_method: 'get' | 'post' | 'post_get';
|
|
8
|
+
}
|
|
9
|
+
export type PresentationResult = {
|
|
10
|
+
outcome: 'pending';
|
|
11
|
+
} | {
|
|
12
|
+
outcome: 'verified';
|
|
13
|
+
} | {
|
|
14
|
+
outcome: 'denied';
|
|
15
|
+
reason?: string;
|
|
16
|
+
} | {
|
|
17
|
+
outcome: 'failed';
|
|
18
|
+
reason?: string;
|
|
19
|
+
};
|
|
20
|
+
interface PresentationVerifier {
|
|
21
|
+
initialize(profile: OpenId4VpProfile): Promise<PresentationTransaction>;
|
|
22
|
+
getResult(transactionId: string): Promise<PresentationResult>;
|
|
23
|
+
}
|
|
24
|
+
interface SessionRepository {
|
|
25
|
+
save(session: PresentationSession): Promise<void>;
|
|
26
|
+
clear(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
interface PresentationControllerOptions {
|
|
29
|
+
verifier: PresentationVerifier;
|
|
30
|
+
sessionRepository: SessionRepository;
|
|
31
|
+
openWallet(uri: string): Promise<void>;
|
|
32
|
+
now(): string;
|
|
33
|
+
}
|
|
34
|
+
export type PresentationState = {
|
|
35
|
+
phase: 'idle';
|
|
36
|
+
} | {
|
|
37
|
+
phase: 'waiting';
|
|
38
|
+
transactionId: string;
|
|
39
|
+
} | {
|
|
40
|
+
phase: 'verified';
|
|
41
|
+
transactionId: string;
|
|
42
|
+
} | {
|
|
43
|
+
phase: 'denied';
|
|
44
|
+
transactionId: string;
|
|
45
|
+
reason?: string;
|
|
46
|
+
} | {
|
|
47
|
+
phase: 'failed';
|
|
48
|
+
transactionId: string;
|
|
49
|
+
reason?: string;
|
|
50
|
+
};
|
|
51
|
+
export declare function buildHaipAuthorizationUri(transaction: PresentationTransaction): string;
|
|
52
|
+
export declare function createPresentationController(options: PresentationControllerOptions): {
|
|
53
|
+
start(profile: OpenId4VpProfile): Promise<PresentationState>;
|
|
54
|
+
refresh(transactionId: string): Promise<PresentationState>;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=presentation-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation-controller.d.ts","sourceRoot":"","sources":["../src/presentation-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAEpE,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,CAAA;CAChD;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,UAAU,CAAA;CAAE,GACvB;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1C,UAAU,oBAAoB;IAC5B,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;IACvE,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CAC9D;AAED,UAAU,iBAAiB;IACzB,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,UAAU,6BAA6B;IACrC,QAAQ,EAAE,oBAAoB,CAAA;IAC9B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,GAAG,IAAI,MAAM,CAAA;CACd;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,uBAAuB,GAAG,MAAM,CAOtF;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,6BAA6B;mBAE1D,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;2BAWrC,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;EAiBnE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function buildHaipAuthorizationUri(transaction) {
|
|
2
|
+
const parameters = new URLSearchParams({
|
|
3
|
+
client_id: transaction.client_id,
|
|
4
|
+
request_uri: transaction.request_uri,
|
|
5
|
+
request_uri_method: transaction.request_uri_method,
|
|
6
|
+
});
|
|
7
|
+
return `haip-vp://?${parameters.toString()}`;
|
|
8
|
+
}
|
|
9
|
+
export function createPresentationController(options) {
|
|
10
|
+
return {
|
|
11
|
+
async start(profile) {
|
|
12
|
+
const transaction = await options.verifier.initialize(profile);
|
|
13
|
+
await options.sessionRepository.save({
|
|
14
|
+
transactionId: transaction.transaction_id,
|
|
15
|
+
requestUri: transaction.request_uri,
|
|
16
|
+
createdAt: options.now(),
|
|
17
|
+
});
|
|
18
|
+
await options.openWallet(buildHaipAuthorizationUri(transaction));
|
|
19
|
+
return { phase: 'waiting', transactionId: transaction.transaction_id };
|
|
20
|
+
},
|
|
21
|
+
async refresh(transactionId) {
|
|
22
|
+
const result = await options.verifier.getResult(transactionId);
|
|
23
|
+
if (result.outcome === 'pending') {
|
|
24
|
+
return { phase: 'waiting', transactionId };
|
|
25
|
+
}
|
|
26
|
+
await options.sessionRepository.clear();
|
|
27
|
+
if (result.outcome === 'verified') {
|
|
28
|
+
return { phase: 'verified', transactionId };
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
phase: result.outcome,
|
|
32
|
+
transactionId,
|
|
33
|
+
...(result.reason === undefined ? {} : { reason: result.reason }),
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface PresentationSessionStorage {
|
|
2
|
+
getItem(key: string): Promise<string | null>;
|
|
3
|
+
setItem(key: string, value: string): Promise<void>;
|
|
4
|
+
removeItem(key: string): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface PresentationSession {
|
|
7
|
+
transactionId: string;
|
|
8
|
+
requestUri: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createPresentationSessionRepository(storage: PresentationSessionStorage): {
|
|
12
|
+
load(): Promise<PresentationSession | null>;
|
|
13
|
+
save(session: PresentationSession): Promise<void>;
|
|
14
|
+
clear(): Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=presentation-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation-session.d.ts","sourceRoot":"","sources":["../src/presentation-session.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC5C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAeD,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,0BAA0B;YAErE,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;kBAU7B,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;aAGxC,OAAO,CAAC,IAAI,CAAC;EAI/B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const SESSION_KEY = 'openid4vp.presentation.session';
|
|
2
|
+
function isPresentationSession(value) {
|
|
3
|
+
if (!value || typeof value !== 'object')
|
|
4
|
+
return false;
|
|
5
|
+
const candidate = value;
|
|
6
|
+
return typeof candidate.transactionId === 'string'
|
|
7
|
+
&& typeof candidate.requestUri === 'string'
|
|
8
|
+
&& typeof candidate.createdAt === 'string'
|
|
9
|
+
&& Object.keys(candidate).every((key) => (key === 'transactionId' || key === 'requestUri' || key === 'createdAt'));
|
|
10
|
+
}
|
|
11
|
+
export function createPresentationSessionRepository(storage) {
|
|
12
|
+
return {
|
|
13
|
+
async load() {
|
|
14
|
+
const serialized = await storage.getItem(SESSION_KEY);
|
|
15
|
+
if (!serialized)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const value = JSON.parse(serialized);
|
|
19
|
+
return isPresentationSession(value) ? value : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
async save(session) {
|
|
26
|
+
await storage.setItem(SESSION_KEY, JSON.stringify(session));
|
|
27
|
+
},
|
|
28
|
+
async clear() {
|
|
29
|
+
await storage.removeItem(SESSION_KEY);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export type QualificationIssuerQuery = Readonly<{
|
|
2
|
+
legalIdentifier: string;
|
|
3
|
+
jurisdiction: string;
|
|
4
|
+
}>;
|
|
5
|
+
export type ResolvedQualificationIssuer = QualificationIssuerQuery & Readonly<{
|
|
6
|
+
issuerTenantReference: string;
|
|
7
|
+
issuerOrganizationReference: string;
|
|
8
|
+
confirmationServiceReference: string;
|
|
9
|
+
}>;
|
|
10
|
+
export type QualificationEvidenceRequest = Readonly<{
|
|
11
|
+
trustedContext: Readonly<{
|
|
12
|
+
controllerAuthorizationReference: string;
|
|
13
|
+
employerTenantReference: string;
|
|
14
|
+
employerOrganizationReference: string;
|
|
15
|
+
employeeReference: string;
|
|
16
|
+
groupAuthorizationReference: string;
|
|
17
|
+
}>;
|
|
18
|
+
qualificationClaimReference: string;
|
|
19
|
+
claimedIssuer: QualificationIssuerQuery;
|
|
20
|
+
evidence: Readonly<{
|
|
21
|
+
reference: string;
|
|
22
|
+
contentId: string;
|
|
23
|
+
mediaType: 'application/pdf' | 'image/png' | 'image/jpeg' | 'image/tiff';
|
|
24
|
+
fileName: string;
|
|
25
|
+
}>;
|
|
26
|
+
}>;
|
|
27
|
+
export type QualificationConfirmationRequestMessage = Readonly<{
|
|
28
|
+
messageType: 'qualification-evidence-confirmation-request';
|
|
29
|
+
messageId: string;
|
|
30
|
+
fromTenantReference: string;
|
|
31
|
+
toTenantReference: string;
|
|
32
|
+
serviceReference: string;
|
|
33
|
+
evidenceReference: string;
|
|
34
|
+
qualificationClaimReference: string;
|
|
35
|
+
employerOrganizationReference: string;
|
|
36
|
+
claimedIssuerOrganizationReference: string;
|
|
37
|
+
evidenceContentId: string;
|
|
38
|
+
evidenceMediaType: QualificationEvidenceRequest['evidence']['mediaType'];
|
|
39
|
+
createdAt: string;
|
|
40
|
+
}>;
|
|
41
|
+
export type QualificationEvidenceControllerPorts = Readonly<{
|
|
42
|
+
issuerDirectory: Readonly<{
|
|
43
|
+
resolve(query: QualificationIssuerQuery): Promise<ResolvedQualificationIssuer | null>;
|
|
44
|
+
}>;
|
|
45
|
+
evidenceRecorder: Readonly<{
|
|
46
|
+
recordEmployerAttestation(request: QualificationEvidenceRequest): Promise<Readonly<{
|
|
47
|
+
evidenceReference: string;
|
|
48
|
+
employerProofReference: string;
|
|
49
|
+
anchor: Readonly<{
|
|
50
|
+
anchorReference: string;
|
|
51
|
+
contentHash: string;
|
|
52
|
+
}>;
|
|
53
|
+
}>>;
|
|
54
|
+
}>;
|
|
55
|
+
ledger: Readonly<{
|
|
56
|
+
recordAnchor(anchor: Readonly<{
|
|
57
|
+
anchorReference: string;
|
|
58
|
+
evidenceReference: string;
|
|
59
|
+
contentHash: string;
|
|
60
|
+
issuerTenantReference: string;
|
|
61
|
+
lifecycleStatus: 'awaiting-issuer-confirmation';
|
|
62
|
+
}>): Promise<void>;
|
|
63
|
+
}>;
|
|
64
|
+
outbox: Readonly<{
|
|
65
|
+
queue(message: QualificationConfirmationRequestMessage): Promise<Readonly<{
|
|
66
|
+
messageReference: string;
|
|
67
|
+
}>>;
|
|
68
|
+
}>;
|
|
69
|
+
now(): string;
|
|
70
|
+
}>;
|
|
71
|
+
export declare function createQualificationEvidenceController(ports: QualificationEvidenceControllerPorts): {
|
|
72
|
+
submit(request: QualificationEvidenceRequest): Promise<Readonly<{
|
|
73
|
+
phase: "issuer-not-resolved";
|
|
74
|
+
}> | Readonly<{
|
|
75
|
+
phase: "issuer-confirmation-queued";
|
|
76
|
+
evidenceReference: string;
|
|
77
|
+
employerProofReference: string;
|
|
78
|
+
ledgerAnchorReference: string;
|
|
79
|
+
issuerTenantReference: string;
|
|
80
|
+
issuerOrganizationReference: string;
|
|
81
|
+
requestMessageReference: string;
|
|
82
|
+
}>>;
|
|
83
|
+
};
|
|
84
|
+
export type QualificationIssuerDecision = Readonly<{
|
|
85
|
+
evidenceReference: string;
|
|
86
|
+
issuerTenantReference: string;
|
|
87
|
+
issuerOrganizationReference: string;
|
|
88
|
+
decision: 'confirmed' | 'rejected' | 'revoked';
|
|
89
|
+
issuerProofReference: string;
|
|
90
|
+
decidedAt: string;
|
|
91
|
+
}>;
|
|
92
|
+
export type QualificationIssuerOutcome = Readonly<{
|
|
93
|
+
evidenceReference: string;
|
|
94
|
+
status: 'issuer-confirmed' | 'issuer-rejected' | 'issuer-revoked';
|
|
95
|
+
issuerProofReference: string;
|
|
96
|
+
decidedAt: string;
|
|
97
|
+
}>;
|
|
98
|
+
export declare function applyQualificationIssuerDecision(current: Readonly<{
|
|
99
|
+
evidenceReference: string;
|
|
100
|
+
expectedIssuerTenantReference: string;
|
|
101
|
+
expectedIssuerOrganizationReference: string;
|
|
102
|
+
}>, decision: QualificationIssuerDecision): QualificationIssuerOutcome;
|
|
103
|
+
export type QualificationIssuerDecisionNotice = Readonly<{
|
|
104
|
+
messageType: 'qualification-evidence-issuer-decision';
|
|
105
|
+
messageId: string;
|
|
106
|
+
fromTenantReference: string;
|
|
107
|
+
toTenantReference: string;
|
|
108
|
+
serviceReference: string;
|
|
109
|
+
evidenceReference: string;
|
|
110
|
+
status: QualificationIssuerOutcome['status'];
|
|
111
|
+
issuerProofReference: string;
|
|
112
|
+
createdAt: string;
|
|
113
|
+
}>;
|
|
114
|
+
export declare function buildQualificationIssuerDecisionNotice(input: Readonly<{
|
|
115
|
+
outcome: QualificationIssuerOutcome;
|
|
116
|
+
employerTenantReference: string;
|
|
117
|
+
issuerTenantReference: string;
|
|
118
|
+
employerNotificationServiceReference: string;
|
|
119
|
+
createdAt: string;
|
|
120
|
+
}>): QualificationIssuerDecisionNotice;
|
|
121
|
+
//# sourceMappingURL=qualification-evidence-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qualification-evidence-controller.d.ts","sourceRoot":"","sources":["../src/qualification-evidence-controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
function requireReference(value, path) {
|
|
3
|
+
if (value.trim().length === 0)
|
|
4
|
+
throw new TypeError(`${path} must not be empty`);
|
|
5
|
+
}
|
|
6
|
+
const BASE58BTC_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
7
|
+
function decodeBase58btc(value) {
|
|
8
|
+
let decoded = 0n;
|
|
9
|
+
for (const character of value) {
|
|
10
|
+
const index = BASE58BTC_ALPHABET.indexOf(character);
|
|
11
|
+
if (index < 0)
|
|
12
|
+
throw new TypeError('Invalid base58btc character');
|
|
13
|
+
decoded = decoded * 58n + BigInt(index);
|
|
14
|
+
}
|
|
15
|
+
const bytes = [];
|
|
16
|
+
while (decoded > 0n) {
|
|
17
|
+
bytes.unshift(Number(decoded & 0xffn));
|
|
18
|
+
decoded >>= 8n;
|
|
19
|
+
}
|
|
20
|
+
for (const character of value) {
|
|
21
|
+
if (character !== '1')
|
|
22
|
+
break;
|
|
23
|
+
bytes.unshift(0);
|
|
24
|
+
}
|
|
25
|
+
return Uint8Array.from(bytes);
|
|
26
|
+
}
|
|
27
|
+
function assertRawSha3_384CidV1(contentId) {
|
|
28
|
+
if (!/^z[1-9A-HJ-NP-Za-km-z]+$/u.test(contentId)) {
|
|
29
|
+
throw new TypeError('evidence.contentId must be a base58btc multibase CID');
|
|
30
|
+
}
|
|
31
|
+
const bytes = decodeBase58btc(contentId.slice(1));
|
|
32
|
+
if (bytes.length !== 52
|
|
33
|
+
|| bytes[0] !== 0x01
|
|
34
|
+
|| bytes[1] !== 0x55
|
|
35
|
+
|| bytes[2] !== 0x15
|
|
36
|
+
|| bytes[3] !== 0x30) {
|
|
37
|
+
throw new TypeError('evidence.contentId must be CIDv1 raw with a SHA3-384 multihash');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function validateRequest(request) {
|
|
41
|
+
requireReference(request.trustedContext.controllerAuthorizationReference, 'controllerAuthorizationReference');
|
|
42
|
+
requireReference(request.trustedContext.employerTenantReference, 'employerTenantReference');
|
|
43
|
+
requireReference(request.trustedContext.employerOrganizationReference, 'employerOrganizationReference');
|
|
44
|
+
requireReference(request.trustedContext.employeeReference, 'employeeReference');
|
|
45
|
+
requireReference(request.trustedContext.groupAuthorizationReference, 'groupAuthorizationReference');
|
|
46
|
+
requireReference(request.qualificationClaimReference, 'qualificationClaimReference');
|
|
47
|
+
requireReference(request.claimedIssuer.legalIdentifier, 'claimedIssuer.legalIdentifier');
|
|
48
|
+
requireReference(request.claimedIssuer.jurisdiction, 'claimedIssuer.jurisdiction');
|
|
49
|
+
requireReference(request.evidence.reference, 'evidence.reference');
|
|
50
|
+
assertRawSha3_384CidV1(request.evidence.contentId);
|
|
51
|
+
}
|
|
52
|
+
export function createQualificationEvidenceController(ports) {
|
|
53
|
+
return {
|
|
54
|
+
async submit(request) {
|
|
55
|
+
validateRequest(request);
|
|
56
|
+
const issuer = await ports.issuerDirectory.resolve(request.claimedIssuer);
|
|
57
|
+
if (!issuer) {
|
|
58
|
+
return Object.freeze({ phase: 'issuer-not-resolved' });
|
|
59
|
+
}
|
|
60
|
+
const recorded = await ports.evidenceRecorder.recordEmployerAttestation(request);
|
|
61
|
+
await ports.ledger.recordAnchor({
|
|
62
|
+
anchorReference: recorded.anchor.anchorReference,
|
|
63
|
+
evidenceReference: recorded.evidenceReference,
|
|
64
|
+
contentHash: recorded.anchor.contentHash,
|
|
65
|
+
issuerTenantReference: issuer.issuerTenantReference,
|
|
66
|
+
lifecycleStatus: 'awaiting-issuer-confirmation',
|
|
67
|
+
});
|
|
68
|
+
const message = Object.freeze({
|
|
69
|
+
messageType: 'qualification-evidence-confirmation-request',
|
|
70
|
+
messageId: `urn:message:qualification-evidence:${recorded.evidenceReference}`,
|
|
71
|
+
fromTenantReference: request.trustedContext.employerTenantReference,
|
|
72
|
+
toTenantReference: issuer.issuerTenantReference,
|
|
73
|
+
serviceReference: issuer.confirmationServiceReference,
|
|
74
|
+
evidenceReference: recorded.evidenceReference,
|
|
75
|
+
qualificationClaimReference: request.qualificationClaimReference,
|
|
76
|
+
employerOrganizationReference: request.trustedContext.employerOrganizationReference,
|
|
77
|
+
claimedIssuerOrganizationReference: issuer.issuerOrganizationReference,
|
|
78
|
+
evidenceContentId: request.evidence.contentId,
|
|
79
|
+
evidenceMediaType: request.evidence.mediaType,
|
|
80
|
+
createdAt: ports.now(),
|
|
81
|
+
});
|
|
82
|
+
const delivery = await ports.outbox.queue(message);
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
phase: 'issuer-confirmation-queued',
|
|
85
|
+
evidenceReference: recorded.evidenceReference,
|
|
86
|
+
employerProofReference: recorded.employerProofReference,
|
|
87
|
+
ledgerAnchorReference: recorded.anchor.anchorReference,
|
|
88
|
+
issuerTenantReference: issuer.issuerTenantReference,
|
|
89
|
+
issuerOrganizationReference: issuer.issuerOrganizationReference,
|
|
90
|
+
requestMessageReference: delivery.messageReference,
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function applyQualificationIssuerDecision(current, decision) {
|
|
96
|
+
if (decision.evidenceReference !== current.evidenceReference) {
|
|
97
|
+
throw new TypeError('Issuer decision evidence does not match');
|
|
98
|
+
}
|
|
99
|
+
if (decision.issuerTenantReference !== current.expectedIssuerTenantReference) {
|
|
100
|
+
throw new TypeError('Issuer tenant does not match');
|
|
101
|
+
}
|
|
102
|
+
if (decision.issuerOrganizationReference !== current.expectedIssuerOrganizationReference) {
|
|
103
|
+
throw new TypeError('Issuer organization does not match');
|
|
104
|
+
}
|
|
105
|
+
requireReference(decision.issuerProofReference, 'issuerProofReference');
|
|
106
|
+
requireReference(decision.decidedAt, 'decidedAt');
|
|
107
|
+
return Object.freeze({
|
|
108
|
+
evidenceReference: decision.evidenceReference,
|
|
109
|
+
status: `issuer-${decision.decision}`,
|
|
110
|
+
issuerProofReference: decision.issuerProofReference,
|
|
111
|
+
decidedAt: decision.decidedAt,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export function buildQualificationIssuerDecisionNotice(input) {
|
|
115
|
+
return Object.freeze({
|
|
116
|
+
messageType: 'qualification-evidence-issuer-decision',
|
|
117
|
+
messageId: `urn:message:qualification-evidence-decision:${input.outcome.evidenceReference}:${input.outcome.status}`,
|
|
118
|
+
fromTenantReference: input.issuerTenantReference,
|
|
119
|
+
toTenantReference: input.employerTenantReference,
|
|
120
|
+
serviceReference: input.employerNotificationServiceReference,
|
|
121
|
+
evidenceReference: input.outcome.evidenceReference,
|
|
122
|
+
status: input.outcome.status,
|
|
123
|
+
issuerProofReference: input.outcome.issuerProofReference,
|
|
124
|
+
createdAt: input.createdAt,
|
|
125
|
+
});
|
|
126
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antifraud-sdk-core-ts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Runtime-neutral orchestration for identity verification and provider-issued credentials",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
23
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=24.0.0"
|
|
27
|
+
},
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/antifraudservices/antifraud-sdk-core-ts.git"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"antifraud-data-utils-ts": "0.1.1"
|
|
38
|
+
}
|
|
39
|
+
}
|