gdc-common-utils-ts 2.9.5 → 2.9.6
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/README.md +7 -0
- package/dist/models/subject-identifier-ledger.d.ts +7 -24
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/smart-authorization-profile.d.ts +46 -0
- package/dist/utils/smart-authorization-profile.js +88 -0
- package/dist/utils/subject-identity.d.ts +11 -0
- package/dist/utils/subject-identity.js +27 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -240,6 +240,13 @@ The `utils` export exposes reusable helpers for DID and message handling, such a
|
|
|
240
240
|
|
|
241
241
|
These helpers support DIDComm-style message construction and related transport/data-shaping workflows.
|
|
242
242
|
|
|
243
|
+
The high-level [federated SMART authorization profile](docs/101-SMART_AUTHORIZATION_PROFILE.md)
|
|
244
|
+
defines the two-step emergency flow: resolve the individual's index provider
|
|
245
|
+
from an opaque subject-identifier ledger key, then ask any available trusted
|
|
246
|
+
tenant to issue one token whose `aud` remains that resolved index provider.
|
|
247
|
+
The ledger value is only `{ indexProviderDid: 'did:web:...' }`; the same opaque
|
|
248
|
+
key is presented later to that provider to retrieve the protected card/index.
|
|
249
|
+
|
|
243
250
|
Example:
|
|
244
251
|
|
|
245
252
|
```ts
|
|
@@ -1,27 +1,10 @@
|
|
|
1
|
-
/** Public provider pointer stored for one opaque subject-identifier asset. */
|
|
2
|
-
export type SubjectIdentifierProviderPointer = Readonly<{
|
|
3
|
-
identifier: Readonly<{
|
|
4
|
-
value: string;
|
|
5
|
-
}>;
|
|
6
|
-
/** Bare DNS domain: no URL scheme, path, DID or tenant identifier. */
|
|
7
|
-
url: string;
|
|
8
|
-
}>;
|
|
9
|
-
/** Public unified-card pointer resolved from an exact known identifier. */
|
|
10
|
-
export type SubjectIdentifierCardPointer = Readonly<{
|
|
11
|
-
identifier: Readonly<{
|
|
12
|
-
value: string;
|
|
13
|
-
}>;
|
|
14
|
-
subjectKind: 'person' | 'animal' | 'property';
|
|
15
|
-
}>;
|
|
16
|
-
/** Ledger payload. It deliberately contains no raw private identifier. */
|
|
17
|
-
export type SubjectIdentifierLedgerPayload = Readonly<{
|
|
18
|
-
card: SubjectIdentifierCardPointer;
|
|
19
|
-
provider: SubjectIdentifierProviderPointer;
|
|
20
|
-
}>;
|
|
21
1
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
2
|
+
* Minimal public payload stored for one opaque subject-identifier lookup key.
|
|
3
|
+
*
|
|
4
|
+
* `indexProviderDid` is the resolvable `did:web` of the provider that owns the
|
|
5
|
+
* protected subject index. The ledger payload contains no card, subject,
|
|
6
|
+
* contact identifier, provider code, domain duplicate or authorization data.
|
|
24
7
|
*/
|
|
25
|
-
export type
|
|
26
|
-
|
|
8
|
+
export type SubjectIdentifierLedgerPayload = Readonly<{
|
|
9
|
+
indexProviderDid: string;
|
|
27
10
|
}>;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -107,6 +107,7 @@ export * from './object-sanitize';
|
|
|
107
107
|
export * from './unified-health-id';
|
|
108
108
|
export * from './permission-templates';
|
|
109
109
|
export * from './smart-scope';
|
|
110
|
+
export * from './smart-authorization-profile';
|
|
110
111
|
export * from './service-act-reasons';
|
|
111
112
|
export * from './same-as';
|
|
112
113
|
export * from './subject-identity';
|
package/dist/utils/index.js
CHANGED
|
@@ -107,6 +107,7 @@ export * from './object-sanitize.js';
|
|
|
107
107
|
export * from './unified-health-id.js';
|
|
108
108
|
export * from './permission-templates.js';
|
|
109
109
|
export * from './smart-scope.js';
|
|
110
|
+
export * from './smart-authorization-profile.js';
|
|
110
111
|
export * from './service-act-reasons.js';
|
|
111
112
|
export * from './same-as.js';
|
|
112
113
|
export * from './subject-identity.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CommunicationTopicCodes } from '../constants/communication';
|
|
2
|
+
/** Extension members carried inside an RFC 7662/9701 introspection result. */
|
|
3
|
+
export declare const SmartIntrospectionExtensionFields: Readonly<{
|
|
4
|
+
/** OpenID4VP presentation transport member; its value retains its native format. */
|
|
5
|
+
readonly VpToken: "vp_token";
|
|
6
|
+
}>;
|
|
7
|
+
/** VC Data Model 2.0 media types for JOSE-secured presentations. */
|
|
8
|
+
export declare const VerifiablePresentationMediaTypes: Readonly<{
|
|
9
|
+
readonly Jwt: "application/vp+jwt";
|
|
10
|
+
}>;
|
|
11
|
+
export type FhirEmergencyAuthorizationDetail = Readonly<{
|
|
12
|
+
type: string;
|
|
13
|
+
actions: readonly ['read'];
|
|
14
|
+
datatypes: readonly string[];
|
|
15
|
+
identifier: string;
|
|
16
|
+
privileges: readonly [typeof CommunicationTopicCodes.BreakTheGlass.attributeValue];
|
|
17
|
+
}>;
|
|
18
|
+
export type BuildFhirEmergencyAuthorizationDetailsInput = Readonly<{
|
|
19
|
+
/** Collision-resistant RFC 9396 type URI owned by the applicable domain profile. */
|
|
20
|
+
authorizationDetailType: string;
|
|
21
|
+
/** Stable subject identifier governed by the emergency decision. */
|
|
22
|
+
subject: string;
|
|
23
|
+
/** Granted read-only SMART scopes from which FHIR resource types are projected. */
|
|
24
|
+
scopes: readonly string[];
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the RFC 9396 representation of one read-only FHIR emergency grant.
|
|
28
|
+
*
|
|
29
|
+
* Only RFC 9396 common member names are emitted. Incident, Consent and audit
|
|
30
|
+
* identifiers remain server-side evidence correlated by the access-token
|
|
31
|
+
* `jti`; they are deliberately not copied into private flat JWT claims.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildFhirEmergencyAuthorizationDetails(input: BuildFhirEmergencyAuthorizationDetailsInput): readonly [FhirEmergencyAuthorizationDetail];
|
|
34
|
+
export type VerifiablePresentationJwtPayloadResult = Readonly<{
|
|
35
|
+
presentation: Readonly<Record<string, unknown>>;
|
|
36
|
+
format: 'vc-data-model-2.0' | 'legacy-jwt-vp';
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Reads a verified JOSE payload without confusing transport and data model.
|
|
40
|
+
*
|
|
41
|
+
* VC Data Model 2.0 `application/vp+jwt` encodes the presentation directly as
|
|
42
|
+
* the JWT Claims Set and forbids a wrapper claim named `vp`. The historical
|
|
43
|
+
* JWT-VP wrapper remains readable only as an explicitly reported compatibility
|
|
44
|
+
* format so callers can measure and eventually remove it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function readVerifiablePresentationJwtPayload(payload: unknown): VerifiablePresentationJwtPayloadResult;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { CommunicationTopicCodes } from '../constants/communication.js';
|
|
3
|
+
/** Extension members carried inside an RFC 7662/9701 introspection result. */
|
|
4
|
+
export const SmartIntrospectionExtensionFields = Object.freeze({
|
|
5
|
+
/** OpenID4VP presentation transport member; its value retains its native format. */
|
|
6
|
+
VpToken: 'vp_token',
|
|
7
|
+
});
|
|
8
|
+
/** VC Data Model 2.0 media types for JOSE-secured presentations. */
|
|
9
|
+
export const VerifiablePresentationMediaTypes = Object.freeze({
|
|
10
|
+
Jwt: 'application/vp+jwt',
|
|
11
|
+
});
|
|
12
|
+
function readScopeDatatype(scope) {
|
|
13
|
+
const head = String(scope || '').trim().split('?', 1)[0] || '';
|
|
14
|
+
const slash = head.indexOf('/');
|
|
15
|
+
const dot = head.lastIndexOf('.');
|
|
16
|
+
if (slash < 0 || dot <= slash + 1)
|
|
17
|
+
return undefined;
|
|
18
|
+
const permission = head.slice(dot + 1).toLowerCase();
|
|
19
|
+
if (permission !== 'r' && permission !== 'rs')
|
|
20
|
+
return undefined;
|
|
21
|
+
const datatype = head.slice(slash + 1, dot).trim();
|
|
22
|
+
return datatype || undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds the RFC 9396 representation of one read-only FHIR emergency grant.
|
|
26
|
+
*
|
|
27
|
+
* Only RFC 9396 common member names are emitted. Incident, Consent and audit
|
|
28
|
+
* identifiers remain server-side evidence correlated by the access-token
|
|
29
|
+
* `jti`; they are deliberately not copied into private flat JWT claims.
|
|
30
|
+
*/
|
|
31
|
+
export function buildFhirEmergencyAuthorizationDetails(input) {
|
|
32
|
+
const authorizationDetailType = String(input.authorizationDetailType || '').trim();
|
|
33
|
+
try {
|
|
34
|
+
if (!authorizationDetailType || !new URL(authorizationDetailType).protocol)
|
|
35
|
+
throw new Error();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new Error('authorization_detail_type_uri_required');
|
|
39
|
+
}
|
|
40
|
+
const subject = String(input.subject || '').trim();
|
|
41
|
+
if (!subject)
|
|
42
|
+
throw new Error('emergency_subject_required');
|
|
43
|
+
const datatypes = Array.from(new Set(input.scopes.map(readScopeDatatype).filter((value) => Boolean(value))));
|
|
44
|
+
if (datatypes.length === 0 || datatypes.length !== input.scopes.length) {
|
|
45
|
+
throw new Error('read_only_smart_scope_required');
|
|
46
|
+
}
|
|
47
|
+
return [{
|
|
48
|
+
type: authorizationDetailType,
|
|
49
|
+
actions: ['read'],
|
|
50
|
+
datatypes,
|
|
51
|
+
identifier: subject,
|
|
52
|
+
privileges: [CommunicationTopicCodes.BreakTheGlass.attributeValue],
|
|
53
|
+
}];
|
|
54
|
+
}
|
|
55
|
+
function isPresentation(value) {
|
|
56
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
57
|
+
return false;
|
|
58
|
+
const type = value.type;
|
|
59
|
+
const types = Array.isArray(type) ? type : [type];
|
|
60
|
+
return types.some((entry) => String(entry || '') === 'VerifiablePresentation');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Reads a verified JOSE payload without confusing transport and data model.
|
|
64
|
+
*
|
|
65
|
+
* VC Data Model 2.0 `application/vp+jwt` encodes the presentation directly as
|
|
66
|
+
* the JWT Claims Set and forbids a wrapper claim named `vp`. The historical
|
|
67
|
+
* JWT-VP wrapper remains readable only as an explicitly reported compatibility
|
|
68
|
+
* format so callers can measure and eventually remove it.
|
|
69
|
+
*/
|
|
70
|
+
export function readVerifiablePresentationJwtPayload(payload) {
|
|
71
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
72
|
+
throw new Error('invalid_verifiable_presentation_payload');
|
|
73
|
+
}
|
|
74
|
+
const record = payload;
|
|
75
|
+
const legacyVp = record.vp;
|
|
76
|
+
const hasDirectPresentation = isPresentation(record);
|
|
77
|
+
const hasLegacyPresentation = isPresentation(legacyVp);
|
|
78
|
+
if (hasDirectPresentation && 'vp' in record) {
|
|
79
|
+
throw new Error('ambiguous_verifiable_presentation_payload');
|
|
80
|
+
}
|
|
81
|
+
if (hasDirectPresentation) {
|
|
82
|
+
return { presentation: record, format: 'vc-data-model-2.0' };
|
|
83
|
+
}
|
|
84
|
+
if (hasLegacyPresentation) {
|
|
85
|
+
return { presentation: legacyVp, format: 'legacy-jwt-vp' };
|
|
86
|
+
}
|
|
87
|
+
throw new Error('invalid_verifiable_presentation_payload');
|
|
88
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BundleEntry } from '../models/bundle';
|
|
2
2
|
import type { SubjectIdentityAssociation, SubjectIdentityBundleEntry, SubjectIdentityInput } from '../models/subject-identity';
|
|
3
|
+
import type { SubjectIdentifierLedgerPayload } from '../models/subject-identifier-ledger';
|
|
3
4
|
/**
|
|
4
5
|
* Builds the exact UTF-8 token used by the distributed Subject lookup.
|
|
5
6
|
*
|
|
@@ -18,6 +19,16 @@ export declare function buildSubjectIdentifierToken(input: Pick<SubjectIdentityI
|
|
|
18
19
|
* included in the Fabric payload.
|
|
19
20
|
*/
|
|
20
21
|
export declare function buildSubjectIdentifierAssetId(input: Pick<SubjectIdentityInput, 'codingSystem' | 'jurisdiction' | 'codeValue'>): string;
|
|
22
|
+
/**
|
|
23
|
+
* Builds the complete public value stored under an opaque subject identifier.
|
|
24
|
+
*
|
|
25
|
+
* The provider DID is deliberately the only payload field. DID resolution
|
|
26
|
+
* discovers its service endpoint; the protected provider lookup then uses the
|
|
27
|
+
* same opaque asset id to resolve the subject/card and authorized index.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildSubjectIdentifierLedgerPayload(indexProviderDid: string): SubjectIdentifierLedgerPayload;
|
|
30
|
+
/** Reads the strict provider-only subject-identifier ledger payload. */
|
|
31
|
+
export declare function readSubjectIdentifierLedgerPayload(payload: unknown): string;
|
|
21
32
|
/**
|
|
22
33
|
* Builds one semantic identity resource for the neutral Subject collection.
|
|
23
34
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { UrnPrefixes } from '../constants/urn.js';
|
|
2
2
|
import { encodeMultibaseSha3 } from './multibasehash.js';
|
|
3
3
|
import { normalizeIndividualIdentifierType } from './individual-identifier.js';
|
|
4
|
+
import { normalizeDidWeb } from './did.js';
|
|
4
5
|
const SUBJECT_RESOURCE_BY_KIND = {
|
|
5
6
|
person: 'Person',
|
|
6
7
|
animal: 'Animal',
|
|
@@ -13,6 +14,7 @@ const SUBJECT_KIND_BY_RESOURCE = {
|
|
|
13
14
|
};
|
|
14
15
|
const STABLE_CARD_ID_PATTERN = /^(?:did|urn|https):\S+$/i;
|
|
15
16
|
const ISO_3166_JURISDICTION_PATTERN = /^[A-Z]{2}(?:-[A-Z0-9]{1,3})?$/;
|
|
17
|
+
const DID_WEB_PATTERN = /^did:web:[^\s#?]+$/;
|
|
16
18
|
/**
|
|
17
19
|
* Builds the exact UTF-8 token used by the distributed Subject lookup.
|
|
18
20
|
*
|
|
@@ -38,6 +40,31 @@ export function buildSubjectIdentifierToken(input) {
|
|
|
38
40
|
export function buildSubjectIdentifierAssetId(input) {
|
|
39
41
|
return `${UrnPrefixes.Multibase}${encodeMultibaseSha3(buildSubjectIdentifierToken(input), 384)}`;
|
|
40
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Builds the complete public value stored under an opaque subject identifier.
|
|
45
|
+
*
|
|
46
|
+
* The provider DID is deliberately the only payload field. DID resolution
|
|
47
|
+
* discovers its service endpoint; the protected provider lookup then uses the
|
|
48
|
+
* same opaque asset id to resolve the subject/card and authorized index.
|
|
49
|
+
*/
|
|
50
|
+
export function buildSubjectIdentifierLedgerPayload(indexProviderDid) {
|
|
51
|
+
const normalized = normalizeDidWeb(String(indexProviderDid || '').trim());
|
|
52
|
+
if (!DID_WEB_PATTERN.test(normalized)) {
|
|
53
|
+
throw new TypeError('indexProviderDid must be a did:web identifier');
|
|
54
|
+
}
|
|
55
|
+
return { indexProviderDid: normalized };
|
|
56
|
+
}
|
|
57
|
+
/** Reads the strict provider-only subject-identifier ledger payload. */
|
|
58
|
+
export function readSubjectIdentifierLedgerPayload(payload) {
|
|
59
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
60
|
+
throw new TypeError('subject_identifier_payload_must_contain_only_index_provider_did');
|
|
61
|
+
}
|
|
62
|
+
const record = payload;
|
|
63
|
+
if (Object.keys(record).length !== 1 || !Object.hasOwn(record, 'indexProviderDid')) {
|
|
64
|
+
throw new TypeError('subject_identifier_payload_must_contain_only_index_provider_did');
|
|
65
|
+
}
|
|
66
|
+
return buildSubjectIdentifierLedgerPayload(String(record.indexProviderDid || '')).indexProviderDid;
|
|
67
|
+
}
|
|
41
68
|
/**
|
|
42
69
|
* Builds one semantic identity resource for the neutral Subject collection.
|
|
43
70
|
*
|