From fa89229de9c9764acd37561076e1711bd5474583 Mon Sep 17 00:00:00 2001 From: Jakub Sydor Date: Thu, 21 Apr 2022 20:04:13 +0200 Subject: [PATCH] docs(claim): improve claim service documentation --- .../classes/modules_claims.ClaimsService.md | 381 ++++++--- ...les_claims.ApproveRolePublishingOptions.md | 35 + ...odules_claims.CreateClaimRequestOptions.md | 43 + ...les_claims.CreateSelfSignedClaimOptions.md | 26 + .../modules_claims.DeleteClaimOptions.md | 17 + ...modules_claims.GetClaimsByIssuerOptions.md | 35 + ...ules_claims.GetClaimsByRequesterOptions.md | 35 + ...odules_claims.GetClaimsBySubjectOptions.md | 35 + .../modules_claims.GetUserClaimsOptions.md | 17 + .../modules_claims.IssueClaimOptions.md | 43 + ...modules_claims.IssueClaimRequestOptions.md | 71 ++ ...aims.IssueVerifiablePresentationOptions.md | 44 + ...odules_claims.PublishPublicClaimOptions.md | 42 + .../modules_claims.RegisterOnchainOptions.md | 71 ++ ...odules_claims.RejectClaimRequestOptions.md | 35 + ...ims.VerifyEnrolmentPrerequisitesOptions.md | 26 + docs/api/modules/modules_claims.md | 17 +- src/modules/claims/claims.service.ts | 758 +++++++++++------- src/modules/claims/claims.types.ts | 203 ++++- 19 files changed, 1527 insertions(+), 407 deletions(-) create mode 100644 docs/api/interfaces/modules_claims.ApproveRolePublishingOptions.md create mode 100644 docs/api/interfaces/modules_claims.CreateClaimRequestOptions.md create mode 100644 docs/api/interfaces/modules_claims.CreateSelfSignedClaimOptions.md create mode 100644 docs/api/interfaces/modules_claims.DeleteClaimOptions.md create mode 100644 docs/api/interfaces/modules_claims.GetClaimsByIssuerOptions.md create mode 100644 docs/api/interfaces/modules_claims.GetClaimsByRequesterOptions.md create mode 100644 docs/api/interfaces/modules_claims.GetClaimsBySubjectOptions.md create mode 100644 docs/api/interfaces/modules_claims.GetUserClaimsOptions.md create mode 100644 docs/api/interfaces/modules_claims.IssueClaimOptions.md create mode 100644 docs/api/interfaces/modules_claims.IssueClaimRequestOptions.md create mode 100644 docs/api/interfaces/modules_claims.IssueVerifiablePresentationOptions.md create mode 100644 docs/api/interfaces/modules_claims.PublishPublicClaimOptions.md create mode 100644 docs/api/interfaces/modules_claims.RegisterOnchainOptions.md create mode 100644 docs/api/interfaces/modules_claims.RejectClaimRequestOptions.md create mode 100644 docs/api/interfaces/modules_claims.VerifyEnrolmentPrerequisitesOptions.md diff --git a/docs/api/classes/modules_claims.ClaimsService.md b/docs/api/classes/modules_claims.ClaimsService.md index 3d79c6de..df216f02 100644 --- a/docs/api/classes/modules_claims.ClaimsService.md +++ b/docs/api/classes/modules_claims.ClaimsService.md @@ -2,6 +2,16 @@ [modules/claims](../modules/modules_claims.md).ClaimsService +Service responsible for handling the request and issuance of claims. +See more information about claims in IAM stack [here](../../../docs/guides/claim.md). + +```typescript +const { connectToCacheServer } = await initWithPrivateKeySigner(privateKey, rpcUrl); +const { connectToDidRegistry } = await connectToCacheServer(); +const { claimsService } = await connectToDidRegistry(); +claimsService.getClaimById(claim.id); +``` + ## Table of contents ### Constructors @@ -52,22 +62,27 @@ ### createClaimRequest -▸ **createClaimRequest**(`__namedParameters`): `Promise`<`void`\> +▸ **createClaimRequest**(`options`): `Promise`<`void`\> + +Allows subject to request for credential by creating and sending a claim request to claim issuer. -**`description`** allows subject to request for credential +```typescript +claimsService.createClaimRequest({ + claim: { + claimType: 'email.roles.energyweb.iam.ewc', + claimTypeVersion: 1, + requestorFields: [{key: 'foo', value: 'bar'}], + }; + subject: 'did:ethr:0x00...0', + registrationTypes: [RegistrationTypes.OnChain, RegistrationTypes.OffChain] +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.claim` | `Object` | -| `__namedParameters.claim.claimType` | `string` | -| `__namedParameters.claim.claimTypeVersion` | `number` | -| `__namedParameters.claim.issuerFields?` | { `key`: `string` ; `value`: `string` \| `number` }[] | -| `__namedParameters.claim.requestorFields?` | { `key`: `string` ; `value`: `string` \| `number` }[] | -| `__namedParameters.registrationTypes?` | [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] | -| `__namedParameters.subject?` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`CreateClaimRequestOptions`](../interfaces/modules_claims.CreateClaimRequestOptions.md) | object containing options | #### Returns @@ -79,21 +94,29 @@ ___ ▸ **createDelegateProof**(`delegateKey`, `identity`, `algorithm?`): `Promise`<`string`\> -**`description`** create a proof of identity delegate +Create a public claim to prove identity. + +```typescript +claimsService.createDelegateProof( + '245a40a9...776071ca57cec', + 'did:ethr:0x00...0', + Algorithms.EIP191, +); +``` #### Parameters | Name | Type | Default value | Description | | :------ | :------ | :------ | :------ | -| `delegateKey` | `string` | `undefined` | private key of the delegate in hexadecimal format | -| `identity` | `string` | `undefined` | Did of the delegate | -| `algorithm` | `Algorithms` | `Algorithms.EIP191` | - | +| `delegateKey` | `string` | `undefined` | Private key of the delegate in hexadecimal format | +| `identity` | `string` | `undefined` | DID of the delegate | +| `algorithm` | `Algorithms` | `Algorithms.EIP191` | Algorithm used to sign the delegate (EIP191 and ES256 available) | #### Returns `Promise`<`string`\> -token of delegate +JWT token of delegate ___ @@ -101,7 +124,11 @@ ___ ▸ **createIdentityProof**(): `Promise`<`string`\> -**`description`** create a public claim to prove identity +Create a public claim to prove identity. + +```typescript +claimsService.createIdentityProof(); +``` #### Returns @@ -113,36 +140,57 @@ ___ ### createSelfSignedClaim -▸ **createSelfSignedClaim**(`__namedParameters`): `Promise`<`string`\> - -**`description`** Creates claim with `data` and adds it to `subject` document. Signer must own or control subject +▸ **createSelfSignedClaim**(`options`): `Promise`<`string`\> + +Creates self signed off-chain claim with `data` and adds it to `subject` document. Signer must own or control subject. + +```typescript +claimsService.createSelfSignedClaim({ + data: { + claimType: 'email.roles.energyweb.iam.ewc', + claimTypeVersion: 1, + issuerFields: [{key: 'foo', value: 'bar'}], + profile: { + name: 'John Doe', + birthdate: '1990-01-01', + address: '123 Main St', + }, + }, + subject: 'did:ethr:volta:0x00...0', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.data` | [`ClaimData`](../interfaces/modules_did_registry.ClaimData.md) | -| `__namedParameters.subject?` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`CreateSelfSignedClaimOptions`](../interfaces/modules_claims.CreateSelfSignedClaimOptions.md) | object containing options | #### Returns `Promise`<`string`\> -claim url +URl to IPFS ___ ### deleteClaim -▸ **deleteClaim**(`__namedParameters`): `Promise`<`void`\> +▸ **deleteClaim**(`options`): `Promise`<`void`\> + +Delete claim request. Works only for pending claims (not issued or rejected). + +```typescript +claimsService.deleteClaim({ + id: '7281a130-e2b1-430d-8c14-201010eae901', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.id` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`DeleteClaimOptions`](../interfaces/modules_claims.DeleteClaimOptions.md) | object containing options | #### Returns @@ -154,18 +202,25 @@ ___ ▸ **getClaimById**(`claimId`): `Promise`<`undefined` \| [`Claim`](../interfaces/modules_claims.Claim.md)\> -**`description`** - Returns claim with the given Id or null if claim does not exist +Retrieve claim with given id. + +```typescript +const claimId = '7281a130-e2b1-430d-8c14-201010eae901'; +claimsService.getClaimById(claimId); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `claimId` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `claimId` | `string` | claim id | #### Returns `Promise`<`undefined` \| [`Claim`](../interfaces/modules_claims.Claim.md)\> +claim with given id + ___ ### getClaimId @@ -187,129 +242,173 @@ ___ ### getClaimsByIssuer -▸ **getClaimsByIssuer**(`__namedParameters`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +▸ **getClaimsByIssuer**(`options`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> -**`description`** - Returns claims for given issuer. Allows filtering by status and parent namespace +Retrieve claims issued by a given issuer with allowing filter by status and parent namespace. + +```typescript +claimsService.getClaimsByIssuer({ + did: 'did:ethr:0x00...0', + isAccepted: false, + namespace: 'energyweb.iam.ewc', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.did` | `string` | -| `__namedParameters.isAccepted?` | `boolean` | -| `__namedParameters.namespace?` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`GetClaimsByIssuerOptions`](../interfaces/modules_claims.GetClaimsByIssuerOptions.md) | object containing options | #### Returns `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +list of claims + ___ ### getClaimsByRequester -▸ **getClaimsByRequester**(`__namedParameters`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +▸ **getClaimsByRequester**(`options`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> + +Retrieve claims requested by a given requester with allowing filter by status and parent namespace. -**`description`** - Returns claims for given requester. Allows filtering by status and parent namespace +```typescript +claimsService.getClaimsByRequester({ + did: 'did:ethr:0x00...0', + isAccepted: false, + namespace: 'energyweb.iam.ewc', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.did` | `string` | -| `__namedParameters.isAccepted?` | `boolean` | -| `__namedParameters.namespace?` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`GetClaimsByRequesterOptions`](../interfaces/modules_claims.GetClaimsByRequesterOptions.md) | object containing options | #### Returns `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +list of claims + ___ ### getClaimsBySubject -▸ **getClaimsBySubject**(`__namedParameters`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +▸ **getClaimsBySubject**(`options`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> + +Retrieve claims for given subject with allowing filter by status and parent namespace. -**`description`** - Returns claims for given subject. Allows filtering by status and parent namespace +```typescript +claimsService.getClaimsBySubject({ + did: 'did:ethr:0x00...0', + isAccepted: false, + namespace: 'energyweb.iam.ewc', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.did` | `string` | -| `__namedParameters.isAccepted?` | `boolean` | -| `__namedParameters.namespace?` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`GetClaimsBySubjectOptions`](../interfaces/modules_claims.GetClaimsBySubjectOptions.md) | object containing options | #### Returns `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +list of claims + ___ ### getClaimsBySubjects ▸ **getClaimsBySubjects**(`subjects`): `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +Retrieve claims related to a given subjects. + +```typescript +claimsService.getClaimsBySubjects(['did:ethr:0x00...0', 'did:ethr:0x00...1', ...]); +``` + #### Parameters -| Name | Type | -| :------ | :------ | -| `subjects` | `string`[] | +| Name | Type | Description | +| :------ | :------ | :------ | +| `subjects` | `string`[] | list of subjects | #### Returns `Promise`<[`Claim`](../interfaces/modules_claims.Claim.md)[]\> +list of claims + ___ ### getNamespaceFromClaimType ▸ **getNamespaceFromClaimType**(`claimType`): `string` -**`description`** get `namespace` from claim type. +Get `namespace` from claim type. + +```typescript +claimsService.getNamespaceFromClaimType( + 'email.roles.energyweb.iam.ewc' +); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `claimType` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `claimType` | `string` | Private key of the delegate in hexadecimal format | #### Returns `string` -namespace +Namespace of given claim type ___ ### getUserClaims -▸ **getUserClaims**(`__namedParameters?`): `Promise`<`IServiceEndpoint` & [`ClaimData`](../interfaces/modules_did_registry.ClaimData.md)[]\> +▸ **getUserClaims**(`options`): `Promise`<`IServiceEndpoint` & [`ClaimData`](../interfaces/modules_did_registry.ClaimData.md)[]\> -getUserClaims +Get published off-chain claims of the given subject. -**`description`** get published offchain claims +```typescript +claimsService.getUserClaims({ + did: 'did:ethr:0x00...0', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `undefined` \| { `did?`: `string` } | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`GetUserClaimsOptions`](../interfaces/modules_claims.GetUserClaimsOptions.md) | object containing options | #### Returns `Promise`<`IServiceEndpoint` & [`ClaimData`](../interfaces/modules_did_registry.ClaimData.md)[]\> +Claims containing DID document service endpoints + ___ ### hasOnChainRole ▸ **hasOnChainRole**(`did`, `role`, `version`): `Promise`<`boolean`\> -A utility function to check the blockchain directly if a DID has a role -TODO: fail if the DID chain ID doesn't match the configured signer network connect +A utility function to check the blockchain directly if a DID has a role. + +```typescript +claimsService.hasOnChainRole('did:ethr:ewc:0x00...0', 'email.roles.iam.ewc', 1); +``` #### Parameters @@ -323,7 +422,7 @@ TODO: fail if the DID chain ID doesn't match the configured signer network conne `Promise`<`boolean`\> -true if DID has role at the version. false if not. +`true` if DID has role at the version. `false` if not. ___ @@ -339,45 +438,61 @@ ___ ### issueClaim -▸ **issueClaim**(`__namedParameters`): `Promise`<`undefined` \| `string`\> +▸ **issueClaim**(`options`): `Promise`<`undefined` \| `string`\> + +Issue claim without previous request. Option available for issuers only. + +```typescript +claimsService.issueClaim({ + claim: { + claimType: 'email.roles.energyweb.iam.ewc', + claimTypeVersion: 1, + issuerFields: [{key: 'foo', value: 'bar'}], + }; + subject: 'did:ethr:0x00...0', + registrationTypes: [RegistrationTypes.OnChain, RegistrationTypes.OffChain] +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.claim` | `Object` | -| `__namedParameters.claim.claimType` | `string` | -| `__namedParameters.claim.claimTypeVersion` | `number` | -| `__namedParameters.claim.issuerFields` | { `key`: `string` ; `value`: `string` \| `number` }[] | -| `__namedParameters.registrationTypes` | [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] | -| `__namedParameters.subject` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`IssueClaimOptions`](../interfaces/modules_claims.IssueClaimOptions.md) | object containing options | #### Returns `Promise`<`undefined` \| `string`\> +Issued token if registrationTypes includes RegistrationTypes.OffChain + ___ ### issueClaimRequest -▸ **issueClaimRequest**(`__namedParameters`): `Promise`<`void`\> +▸ **issueClaimRequest**(`options`): `Promise`<`void`\> Issue a claim request by signing both off-chain and on-chain request and persisting result to the cache-server. Optionally, issue on-chain role can be submitted to the ClaimManager contract as well. +```typescript +const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); +claimsService.issueClaimRequest({ + requester: claim.requester, + token: claim.token, + id: claim.id, + subjectAgreement: claim.subjectAgreement, + registrationTypes: claim.registrationTypes; + issuerFields: [{key: 'foo', value: 'bar'}], + publishOnChain: false, +}); +``` + #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.id` | `string` | -| `__namedParameters.issuerFields?` | { `key`: `string` ; `value`: `string` \| `number` }[] | -| `__namedParameters.publishOnChain?` | `boolean` | -| `__namedParameters.registrationTypes` | [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] | -| `__namedParameters.requester` | `string` | -| `__namedParameters.subjectAgreement` | `string` | -| `__namedParameters.token` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`IssueClaimRequestOptions`](../interfaces/modules_claims.IssueClaimRequestOptions.md) | object containing options | #### Returns @@ -387,26 +502,33 @@ ___ ### publishPublicClaim -▸ **publishPublicClaim**(`token`): `Promise`<`undefined` \| `string`\> +▸ **publishPublicClaim**(`options`): `Promise`<`undefined` \| `string`\> + +Register role to claim manager contract if registrationTypes includes RegistrationTypes.OnChain +Publish role to IPFS and add DID document service if registrationTypes includes RegistrationTypes.OffChain -**`description`** publishes claim off-chain (by storing claim data in ipfs and save url to DID document services) or registering on-chain depending on registrationTypes values. +```typescript +const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); +claimsService.publishPublicClaim({ + claim: { + token: claim.token, + claimType: claim.claimType, + }; + registrationTypes: claim.registrationTypes, +}); +``` #### Parameters | Name | Type | Description | | :------ | :------ | :------ | -| `token` | `Object` | @deprecated - use claim with claimType instead | -| `token.claim` | `Object` | - | -| `token.claim.claimType?` | `string` | - | -| `token.claim.token?` | `string` | - | -| `token.registrationTypes?` | [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] | - | -| `token.token?` | `string` | - | +| `options` | [`PublishPublicClaimOptions`](../interfaces/modules_claims.PublishPublicClaimOptions.md) | object containing options | #### Returns `Promise`<`undefined` \| `string`\> -ulr to ipfs +URl to IPFS if registrationTypes includes RegistrationTypes.OffChain ___ @@ -414,20 +536,25 @@ ___ ▸ **registerOnchain**(`claim`): `Promise`<`void`\> -**`description`** Registers issued onchain claim with Claim manager +Register issued on-chain claim on Claim Manager contract. + +```typescript +const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); +claimsService.registerOnchain({ + claimType: claim.claimType, + claimTypeVersion: claim.claimTypeVersion, + subjectAgreement: claim.subjectAgreement, + onChainProof: claim.onChainProof, + acceptedBy: claim.acceptedBy; + subject: claim.subject, +}); +``` #### Parameters | Name | Type | Description | | :------ | :------ | :------ | -| `claim` | `Object` | id of signed onchain claim. | -| `claim.acceptedBy` | `string` | - | -| `claim.claimType?` | `string` | - | -| `claim.claimTypeVersion?` | `string` | - | -| `claim.onChainProof` | `string` | - | -| `claim.subject?` | `string` | - | -| `claim.subjectAgreement?` | `string` | - | -| `claim.token?` | `string` | - | +| `claim` | [`RegisterOnchainOptions`](../interfaces/modules_claims.RegisterOnchainOptions.md) | object containing options | #### Returns @@ -437,16 +564,24 @@ ___ ### rejectClaimRequest -▸ **rejectClaimRequest**(`__namedParameters`): `Promise`<`void`\> +▸ **rejectClaimRequest**(`options`): `Promise`<`void`\> + +Reject claim request. + +```typescript +const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); +claimsService.rejectClaimRequest({ + id: claim.id, + requesterDID: claim.requester, + rejectionReason: 'some reason', +}); +``` #### Parameters -| Name | Type | -| :------ | :------ | -| `__namedParameters` | `Object` | -| `__namedParameters.id` | `string` | -| `__namedParameters.rejectionReason?` | `string` | -| `__namedParameters.requesterDID` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`RejectClaimRequestOptions`](../interfaces/modules_claims.RejectClaimRequestOptions.md) | object containing options | #### Returns diff --git a/docs/api/interfaces/modules_claims.ApproveRolePublishingOptions.md b/docs/api/interfaces/modules_claims.ApproveRolePublishingOptions.md new file mode 100644 index 00000000..0e6b1670 --- /dev/null +++ b/docs/api/interfaces/modules_claims.ApproveRolePublishingOptions.md @@ -0,0 +1,35 @@ +# Interface: ApproveRolePublishingOptions + +[modules/claims](../modules/modules_claims.md).ApproveRolePublishingOptions + +## Table of contents + +### Properties + +- [role](modules_claims.ApproveRolePublishingOptions.md#role) +- [subject](modules_claims.ApproveRolePublishingOptions.md#subject) +- [version](modules_claims.ApproveRolePublishingOptions.md#version) + +## Properties + +### role + +• **role**: `string` + +Role claim type + +___ + +### subject + +• **subject**: `string` + +DID of the subject + +___ + +### version + +• **version**: `number` + +Role version diff --git a/docs/api/interfaces/modules_claims.CreateClaimRequestOptions.md b/docs/api/interfaces/modules_claims.CreateClaimRequestOptions.md new file mode 100644 index 00000000..fe1533fa --- /dev/null +++ b/docs/api/interfaces/modules_claims.CreateClaimRequestOptions.md @@ -0,0 +1,43 @@ +# Interface: CreateClaimRequestOptions + +[modules/claims](../modules/modules_claims.md).CreateClaimRequestOptions + +## Table of contents + +### Properties + +- [claim](modules_claims.CreateClaimRequestOptions.md#claim) +- [registrationTypes](modules_claims.CreateClaimRequestOptions.md#registrationtypes) +- [subject](modules_claims.CreateClaimRequestOptions.md#subject) + +## Properties + +### claim + +• **claim**: `Object` + +Claim request params + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `claimType` | `string` | Role namespace | +| `claimTypeVersion` | `number` | Version of the role | +| `requestorFields?` | { `key`: `string` ; `value`: `string` \| `number` }[] | Requestor fields that role is requiring | + +___ + +### registrationTypes + +• `Optional` **registrationTypes**: [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] + +Indicates what type of claim registration you are requesting: on-chain and/or off-chain + +___ + +### subject + +• `Optional` **subject**: `string` + +DID of the subject diff --git a/docs/api/interfaces/modules_claims.CreateSelfSignedClaimOptions.md b/docs/api/interfaces/modules_claims.CreateSelfSignedClaimOptions.md new file mode 100644 index 00000000..abc35a11 --- /dev/null +++ b/docs/api/interfaces/modules_claims.CreateSelfSignedClaimOptions.md @@ -0,0 +1,26 @@ +# Interface: CreateSelfSignedClaimOptions + +[modules/claims](../modules/modules_claims.md).CreateSelfSignedClaimOptions + +## Table of contents + +### Properties + +- [data](modules_claims.CreateSelfSignedClaimOptions.md#data) +- [subject](modules_claims.CreateSelfSignedClaimOptions.md#subject) + +## Properties + +### data + +• **data**: [`ClaimData`](modules_did_registry.ClaimData.md) + +Claim data + +___ + +### subject + +• `Optional` **subject**: `string` + +DID of the claim subject diff --git a/docs/api/interfaces/modules_claims.DeleteClaimOptions.md b/docs/api/interfaces/modules_claims.DeleteClaimOptions.md new file mode 100644 index 00000000..2d097aa7 --- /dev/null +++ b/docs/api/interfaces/modules_claims.DeleteClaimOptions.md @@ -0,0 +1,17 @@ +# Interface: DeleteClaimOptions + +[modules/claims](../modules/modules_claims.md).DeleteClaimOptions + +## Table of contents + +### Properties + +- [id](modules_claims.DeleteClaimOptions.md#id) + +## Properties + +### id + +• **id**: `string` + +Claim id diff --git a/docs/api/interfaces/modules_claims.GetClaimsByIssuerOptions.md b/docs/api/interfaces/modules_claims.GetClaimsByIssuerOptions.md new file mode 100644 index 00000000..bd0c8ae8 --- /dev/null +++ b/docs/api/interfaces/modules_claims.GetClaimsByIssuerOptions.md @@ -0,0 +1,35 @@ +# Interface: GetClaimsByIssuerOptions + +[modules/claims](../modules/modules_claims.md).GetClaimsByIssuerOptions + +## Table of contents + +### Properties + +- [did](modules_claims.GetClaimsByIssuerOptions.md#did) +- [isAccepted](modules_claims.GetClaimsByIssuerOptions.md#isaccepted) +- [namespace](modules_claims.GetClaimsByIssuerOptions.md#namespace) + +## Properties + +### did + +• **did**: `string` + +DID of the issuer + +___ + +### isAccepted + +• `Optional` **isAccepted**: `boolean` + +Indicates whether to show only accepted `Claims` + +___ + +### namespace + +• `Optional` **namespace**: `string` + +Indicates what namespace `Claims` should be in diff --git a/docs/api/interfaces/modules_claims.GetClaimsByRequesterOptions.md b/docs/api/interfaces/modules_claims.GetClaimsByRequesterOptions.md new file mode 100644 index 00000000..32233069 --- /dev/null +++ b/docs/api/interfaces/modules_claims.GetClaimsByRequesterOptions.md @@ -0,0 +1,35 @@ +# Interface: GetClaimsByRequesterOptions + +[modules/claims](../modules/modules_claims.md).GetClaimsByRequesterOptions + +## Table of contents + +### Properties + +- [did](modules_claims.GetClaimsByRequesterOptions.md#did) +- [isAccepted](modules_claims.GetClaimsByRequesterOptions.md#isaccepted) +- [namespace](modules_claims.GetClaimsByRequesterOptions.md#namespace) + +## Properties + +### did + +• **did**: `string` + +DID of the requestor + +___ + +### isAccepted + +• `Optional` **isAccepted**: `boolean` + +Indicates whether to show only accepted `Claims` + +___ + +### namespace + +• `Optional` **namespace**: `string` + +Indicates what namespace `Claims` should be in diff --git a/docs/api/interfaces/modules_claims.GetClaimsBySubjectOptions.md b/docs/api/interfaces/modules_claims.GetClaimsBySubjectOptions.md new file mode 100644 index 00000000..1aaaeeed --- /dev/null +++ b/docs/api/interfaces/modules_claims.GetClaimsBySubjectOptions.md @@ -0,0 +1,35 @@ +# Interface: GetClaimsBySubjectOptions + +[modules/claims](../modules/modules_claims.md).GetClaimsBySubjectOptions + +## Table of contents + +### Properties + +- [did](modules_claims.GetClaimsBySubjectOptions.md#did) +- [isAccepted](modules_claims.GetClaimsBySubjectOptions.md#isaccepted) +- [namespace](modules_claims.GetClaimsBySubjectOptions.md#namespace) + +## Properties + +### did + +• **did**: `string` + +DID of the subject + +___ + +### isAccepted + +• `Optional` **isAccepted**: `boolean` + +Indicates whether to show only accepted `Claims` + +___ + +### namespace + +• `Optional` **namespace**: `string` + +Indicates what namespace `Claims` should be in diff --git a/docs/api/interfaces/modules_claims.GetUserClaimsOptions.md b/docs/api/interfaces/modules_claims.GetUserClaimsOptions.md new file mode 100644 index 00000000..0137ed5f --- /dev/null +++ b/docs/api/interfaces/modules_claims.GetUserClaimsOptions.md @@ -0,0 +1,17 @@ +# Interface: GetUserClaimsOptions + +[modules/claims](../modules/modules_claims.md).GetUserClaimsOptions + +## Table of contents + +### Properties + +- [did](modules_claims.GetUserClaimsOptions.md#did) + +## Properties + +### did + +• `Optional` **did**: `string` + +DID of the subject diff --git a/docs/api/interfaces/modules_claims.IssueClaimOptions.md b/docs/api/interfaces/modules_claims.IssueClaimOptions.md new file mode 100644 index 00000000..6c90113d --- /dev/null +++ b/docs/api/interfaces/modules_claims.IssueClaimOptions.md @@ -0,0 +1,43 @@ +# Interface: IssueClaimOptions + +[modules/claims](../modules/modules_claims.md).IssueClaimOptions + +## Table of contents + +### Properties + +- [claim](modules_claims.IssueClaimOptions.md#claim) +- [registrationTypes](modules_claims.IssueClaimOptions.md#registrationtypes) +- [subject](modules_claims.IssueClaimOptions.md#subject) + +## Properties + +### claim + +• **claim**: `Object` + +Claim params + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `claimType` | `string` | Role namespace | +| `claimTypeVersion` | `number` | Version of the role | +| `issuerFields?` | { `key`: `string` ; `value`: `string` \| `number` }[] | Issuers fields that role is requiring | + +___ + +### registrationTypes + +• `Optional` **registrationTypes**: [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] + +Registration types + +___ + +### subject + +• **subject**: `string` + +DID of the claim subject diff --git a/docs/api/interfaces/modules_claims.IssueClaimRequestOptions.md b/docs/api/interfaces/modules_claims.IssueClaimRequestOptions.md new file mode 100644 index 00000000..4266874c --- /dev/null +++ b/docs/api/interfaces/modules_claims.IssueClaimRequestOptions.md @@ -0,0 +1,71 @@ +# Interface: IssueClaimRequestOptions + +[modules/claims](../modules/modules_claims.md).IssueClaimRequestOptions + +## Table of contents + +### Properties + +- [id](modules_claims.IssueClaimRequestOptions.md#id) +- [issuerFields](modules_claims.IssueClaimRequestOptions.md#issuerfields) +- [publishOnChain](modules_claims.IssueClaimRequestOptions.md#publishonchain) +- [registrationTypes](modules_claims.IssueClaimRequestOptions.md#registrationtypes) +- [requester](modules_claims.IssueClaimRequestOptions.md#requester) +- [subjectAgreement](modules_claims.IssueClaimRequestOptions.md#subjectagreement) +- [token](modules_claims.IssueClaimRequestOptions.md#token) + +## Properties + +### id + +• **id**: `string` + +Claim id + +___ + +### issuerFields + +• `Optional` **issuerFields**: { `key`: `string` ; `value`: `string` \| `number` }[] + +Issuer fields that role is requiring + +___ + +### publishOnChain + +• `Optional` **publishOnChain**: `boolean` + +Indicates whether to publish role on-chain or not (default: false) + +___ + +### registrationTypes + +• **registrationTypes**: [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] + +Registration types + +___ + +### requester + +• **requester**: `string` + +DID of the claim requestor + +___ + +### subjectAgreement + +• **subjectAgreement**: `string` + +Subject agreement signature + +___ + +### token + +• **token**: `string` + +JWT token generated by requestor during claim request diff --git a/docs/api/interfaces/modules_claims.IssueVerifiablePresentationOptions.md b/docs/api/interfaces/modules_claims.IssueVerifiablePresentationOptions.md new file mode 100644 index 00000000..f5c9f9cd --- /dev/null +++ b/docs/api/interfaces/modules_claims.IssueVerifiablePresentationOptions.md @@ -0,0 +1,44 @@ +# Interface: IssueVerifiablePresentationOptions + +[modules/claims](../modules/modules_claims.md).IssueVerifiablePresentationOptions + +## Table of contents + +### Properties + +- [issuerFields](modules_claims.IssueVerifiablePresentationOptions.md#issuerfields) +- [namespace](modules_claims.IssueVerifiablePresentationOptions.md#namespace) +- [subject](modules_claims.IssueVerifiablePresentationOptions.md#subject) +- [version](modules_claims.IssueVerifiablePresentationOptions.md#version) + +## Properties + +### issuerFields + +• `Optional` **issuerFields**: { `key`: `string` ; `value`: `string` \| `number` }[] + +Issuers fields that role is requiring + +___ + +### namespace + +• **namespace**: `string` + +Role claim type + +___ + +### subject + +• **subject**: `string` + +DID of the subject + +___ + +### version + +• **version**: `string` + +Role version diff --git a/docs/api/interfaces/modules_claims.PublishPublicClaimOptions.md b/docs/api/interfaces/modules_claims.PublishPublicClaimOptions.md new file mode 100644 index 00000000..45c50927 --- /dev/null +++ b/docs/api/interfaces/modules_claims.PublishPublicClaimOptions.md @@ -0,0 +1,42 @@ +# Interface: PublishPublicClaimOptions + +[modules/claims](../modules/modules_claims.md).PublishPublicClaimOptions + +## Table of contents + +### Properties + +- [claim](modules_claims.PublishPublicClaimOptions.md#claim) +- [registrationTypes](modules_claims.PublishPublicClaimOptions.md#registrationtypes) +- [token](modules_claims.PublishPublicClaimOptions.md#token) + +## Properties + +### claim + +• **claim**: `Object` + +Claim params + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `claimType?` | `string` | Role namespace | +| `token?` | `string` | JWT token generated by requestor during claim request | + +___ + +### registrationTypes + +• `Optional` **registrationTypes**: [`RegistrationTypes`](../enums/modules_claims.RegistrationTypes.md)[] + +Registration types + +___ + +### token + +• `Optional` **token**: `string` + +**`deprecated`** diff --git a/docs/api/interfaces/modules_claims.RegisterOnchainOptions.md b/docs/api/interfaces/modules_claims.RegisterOnchainOptions.md new file mode 100644 index 00000000..e21087f8 --- /dev/null +++ b/docs/api/interfaces/modules_claims.RegisterOnchainOptions.md @@ -0,0 +1,71 @@ +# Interface: RegisterOnchainOptions + +[modules/claims](../modules/modules_claims.md).RegisterOnchainOptions + +## Table of contents + +### Properties + +- [acceptedBy](modules_claims.RegisterOnchainOptions.md#acceptedby) +- [claimType](modules_claims.RegisterOnchainOptions.md#claimtype) +- [claimTypeVersion](modules_claims.RegisterOnchainOptions.md#claimtypeversion) +- [onChainProof](modules_claims.RegisterOnchainOptions.md#onchainproof) +- [subject](modules_claims.RegisterOnchainOptions.md#subject) +- [subjectAgreement](modules_claims.RegisterOnchainOptions.md#subjectagreement) +- [token](modules_claims.RegisterOnchainOptions.md#token) + +## Properties + +### acceptedBy + +• **acceptedBy**: `string` + +DID of the issuer + +___ + +### claimType + +• `Optional` **claimType**: `string` + +Role namespace + +___ + +### claimTypeVersion + +• `Optional` **claimTypeVersion**: `string` + +Version of the claim type + +___ + +### onChainProof + +• **onChainProof**: `string` + +On-chain proof signature + +___ + +### subject + +• `Optional` **subject**: `string` + +DID of the claim subject + +___ + +### subjectAgreement + +• `Optional` **subjectAgreement**: `string` + +Subject agreement signature + +___ + +### token + +• `Optional` **token**: `string` + +**`deprecated`** diff --git a/docs/api/interfaces/modules_claims.RejectClaimRequestOptions.md b/docs/api/interfaces/modules_claims.RejectClaimRequestOptions.md new file mode 100644 index 00000000..c00cd16f --- /dev/null +++ b/docs/api/interfaces/modules_claims.RejectClaimRequestOptions.md @@ -0,0 +1,35 @@ +# Interface: RejectClaimRequestOptions + +[modules/claims](../modules/modules_claims.md).RejectClaimRequestOptions + +## Table of contents + +### Properties + +- [id](modules_claims.RejectClaimRequestOptions.md#id) +- [rejectionReason](modules_claims.RejectClaimRequestOptions.md#rejectionreason) +- [requesterDID](modules_claims.RejectClaimRequestOptions.md#requesterdid) + +## Properties + +### id + +• **id**: `string` + +Claim id + +___ + +### rejectionReason + +• `Optional` **rejectionReason**: `string` + +Reason for rejection + +___ + +### requesterDID + +• **requesterDID**: `string` + +DID of the claim requestor diff --git a/docs/api/interfaces/modules_claims.VerifyEnrolmentPrerequisitesOptions.md b/docs/api/interfaces/modules_claims.VerifyEnrolmentPrerequisitesOptions.md new file mode 100644 index 00000000..8307f24b --- /dev/null +++ b/docs/api/interfaces/modules_claims.VerifyEnrolmentPrerequisitesOptions.md @@ -0,0 +1,26 @@ +# Interface: VerifyEnrolmentPrerequisitesOptions + +[modules/claims](../modules/modules_claims.md).VerifyEnrolmentPrerequisitesOptions + +## Table of contents + +### Properties + +- [role](modules_claims.VerifyEnrolmentPrerequisitesOptions.md#role) +- [subject](modules_claims.VerifyEnrolmentPrerequisitesOptions.md#subject) + +## Properties + +### role + +• **role**: `string` + +Role claim type + +___ + +### subject + +• **subject**: `string` + +DID of the subject diff --git a/docs/api/modules/modules_claims.md b/docs/api/modules/modules_claims.md index 2d928436..cab36b29 100644 --- a/docs/api/modules/modules_claims.md +++ b/docs/api/modules/modules_claims.md @@ -13,10 +13,25 @@ ### Interfaces +- [ApproveRolePublishingOptions](../interfaces/modules_claims.ApproveRolePublishingOptions.md) - [Claim](../interfaces/modules_claims.Claim.md) +- [CreateClaimRequestOptions](../interfaces/modules_claims.CreateClaimRequestOptions.md) +- [CreateSelfSignedClaimOptions](../interfaces/modules_claims.CreateSelfSignedClaimOptions.md) +- [DeleteClaimOptions](../interfaces/modules_claims.DeleteClaimOptions.md) +- [GetClaimsByIssuerOptions](../interfaces/modules_claims.GetClaimsByIssuerOptions.md) +- [GetClaimsByRequesterOptions](../interfaces/modules_claims.GetClaimsByRequesterOptions.md) +- [GetClaimsBySubjectOptions](../interfaces/modules_claims.GetClaimsBySubjectOptions.md) +- [GetUserClaimsOptions](../interfaces/modules_claims.GetUserClaimsOptions.md) - [IClaimIssuance](../interfaces/modules_claims.IClaimIssuance.md) - [IClaimRejection](../interfaces/modules_claims.IClaimRejection.md) - [IClaimRequest](../interfaces/modules_claims.IClaimRequest.md) +- [IssueClaimOptions](../interfaces/modules_claims.IssueClaimOptions.md) +- [IssueClaimRequestOptions](../interfaces/modules_claims.IssueClaimRequestOptions.md) +- [IssueVerifiablePresentationOptions](../interfaces/modules_claims.IssueVerifiablePresentationOptions.md) +- [PublishPublicClaimOptions](../interfaces/modules_claims.PublishPublicClaimOptions.md) +- [RegisterOnchainOptions](../interfaces/modules_claims.RegisterOnchainOptions.md) +- [RejectClaimRequestOptions](../interfaces/modules_claims.RejectClaimRequestOptions.md) +- [VerifyEnrolmentPrerequisitesOptions](../interfaces/modules_claims.VerifyEnrolmentPrerequisitesOptions.md) ### Type aliases @@ -115,7 +130,7 @@ ___ | Name | Type | | :------ | :------ | -| `claim` | `Record`<`string`, `unknown`\> | +| `claim` | `unknown` | #### Returns diff --git a/src/modules/claims/claims.service.ts b/src/modules/claims/claims.service.ts index cb547aad..624cb1c9 100644 --- a/src/modules/claims/claims.service.ts +++ b/src/modules/claims/claims.service.ts @@ -32,6 +32,22 @@ import { erc712_type_hash, proof_type_hash, typedMsgPrefix, + Claim, + GetClaimsByRequesterOptions, + GetClaimsByIssuerOptions, + GetClaimsBySubjectOptions, + CreateClaimRequestOptions, + IssueClaimRequestOptions, + RegisterOnchainOptions, + RejectClaimRequestOptions, + DeleteClaimOptions, + IssueClaimOptions, + PublishPublicClaimOptions, + CreateSelfSignedClaimOptions, + GetUserClaimsOptions, + VerifyEnrolmentPrerequisitesOptions, + IssueVerifiablePresentationOptions, + ApproveRolePublishingOptions, } from './claims.types'; import { DidRegistry } from '../did-registry/did-registry.service'; import { ClaimData } from '../did-registry/did.types'; @@ -51,6 +67,17 @@ const { arrayify, } = utils; +/** + * Service responsible for handling the request and issuance of claims. + * See more information about claims in IAM stack [here](../../../docs/guides/claim.md). + * + * ```typescript + * const { connectToCacheServer } = await initWithPrivateKeySigner(privateKey, rpcUrl); + * const { connectToDidRegistry } = await connectToCacheServer(); + * const { claimsService } = await connectToDidRegistry(); + * claimsService.getClaimById(claim.id); + * ``` + */ export class ClaimsService { private _claimManager: string; private _claimManagerInterface = ClaimManager__factory.createInterface(); @@ -89,18 +116,23 @@ export class ClaimsService { } /** - * A utility function to check the blockchain directly if a DID has a role - * TODO: fail if the DID chain ID doesn't match the configured signer network connect - * @param did The ethr DID to check - * @param role The role to check (the full namespace) - * @param version The version to check - * @returns true if DID has role at the version. false if not. + * A utility function to check the blockchain directly if a DID has a role. + * + * ```typescript + * claimsService.hasOnChainRole('did:ethr:ewc:0x00...0', 'email.roles.iam.ewc', 1); + * ``` + * + * @param {string} did The ethr DID to check + * @param {string} role The role to check (the full namespace) + * @param {number} version The version to check + * @return `true` if DID has role at the version. `false` if not. */ async hasOnChainRole( did: string, role: string, version: number ): Promise { + // TODO: fail if the DID chain ID doesn't match the configured signer network connection const data = this._claimManagerInterface.encodeFunctionData('hasRole', [ addressOf(did), namehash(role), @@ -117,22 +149,39 @@ export class ClaimsService { return Boolean(intFromHexString); } - async getClaimsBySubjects(subjects: string[]) { + /** + * Retrieve claims related to a given subjects. + * + * ```typescript + * claimsService.getClaimsBySubjects(['did:ethr:0x00...0', 'did:ethr:0x00...1', ...]); + * ``` + * + * @param {Array} subjects list of subjects + * @returns list of claims + */ + async getClaimsBySubjects(subjects: string[]): Promise { return this._cacheClient.getClaimsBySubjects(subjects); } /** - * @description - Returns claims for given requester. Allows filtering by status and parent namespace + * Retrieve claims requested by a given requester with allowing filter by status and parent namespace. + * + * ```typescript + * claimsService.getClaimsByRequester({ + * did: 'did:ethr:0x00...0', + * isAccepted: false, + * namespace: 'energyweb.iam.ewc', + * }); + * ``` + * + * @param {GetClaimsByRequesterOptions} options object containing options + * @returns list of claims */ async getClaimsByRequester({ did, isAccepted, namespace, - }: { - did: string; - isAccepted?: boolean; - namespace?: string; - }) { + }: GetClaimsByRequesterOptions): Promise { return this._cacheClient.getClaimsByRequester(did, { isAccepted, namespace, @@ -140,59 +189,86 @@ export class ClaimsService { } /** - * @description - Returns claims for given issuer. Allows filtering by status and parent namespace + * Retrieve claims issued by a given issuer with allowing filter by status and parent namespace. + * + * ```typescript + * claimsService.getClaimsByIssuer({ + * did: 'did:ethr:0x00...0', + * isAccepted: false, + * namespace: 'energyweb.iam.ewc', + * }); + * ``` + * + * @param {GetClaimsByIssuerOptions} options object containing options + * @returns list of claims */ async getClaimsByIssuer({ did, isAccepted, namespace, - }: { - did: string; - isAccepted?: boolean; - namespace?: string; - }) { + }: GetClaimsByIssuerOptions): Promise { return this._cacheClient.getClaimsByIssuer(did, { isAccepted, namespace }); } /** - * @description - Returns claims for given subject. Allows filtering by status and parent namespace + * Retrieve claims for given subject with allowing filter by status and parent namespace. + * + * ```typescript + * claimsService.getClaimsBySubject({ + * did: 'did:ethr:0x00...0', + * isAccepted: false, + * namespace: 'energyweb.iam.ewc', + * }); + * ``` + * + * @param {GetClaimsBySubjectOptions} options object containing options + * @returns list of claims */ async getClaimsBySubject({ did, isAccepted, namespace, - }: { - did: string; - isAccepted?: boolean; - namespace?: string; - }) { + }: GetClaimsBySubjectOptions): Promise { return this._cacheClient.getClaimsBySubject(did, { isAccepted, namespace }); } /** - * @description - Returns claim with the given Id or null if claim does not exist + * Retrieve claim with given id. + * + * ```typescript + * const claimId = '7281a130-e2b1-430d-8c14-201010eae901'; + * claimsService.getClaimById(claimId); + * ``` + * + * @param {string} claimId claim id + * @return claim with given id */ - async getClaimById(claimId: string) { + async getClaimById(claimId: string): Promise { return this._cacheClient.getClaimById(claimId); } /** - * @description allows subject to request for credential + * Allows subject to request for credential by creating and sending a claim request to claim issuer. + * + * ```typescript + * claimsService.createClaimRequest({ + * claim: { + * claimType: 'email.roles.energyweb.iam.ewc', + * claimTypeVersion: 1, + * requestorFields: [{key: 'foo', value: 'bar'}], + * }; + * subject: 'did:ethr:0x00...0', + * registrationTypes: [RegistrationTypes.OnChain, RegistrationTypes.OffChain] + * }); + * ``` + * + * @param {CreateClaimRequestOptions} options object containing options */ async createClaimRequest({ claim, subject = this._signerService.did, registrationTypes = [RegistrationTypes.OffChain], - }: { - claim: { - claimType: string; - claimTypeVersion: number; - requestorFields?: { key: string; value: string | number }[]; - issuerFields?: { key: string; value: string | number }[]; - }; - subject?: string; - registrationTypes?: RegistrationTypes[]; - }) { + }: CreateClaimRequestOptions): Promise { const { claimType: role, claimTypeVersion: version } = claim; const token = await this._didRegistry.createPublicClaim({ data: claim, @@ -235,7 +311,21 @@ export class ClaimsService { /** * Issue a claim request by signing both off-chain and on-chain request and persisting result to the cache-server. * Optionally, issue on-chain role can be submitted to the ClaimManager contract as well. - * @param params.publishOnChain If issuing an on-chain role, then if true then will submit role to chain (incurring tx cost). Default is true + * + * ```typescript + * const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); + * claimsService.issueClaimRequest({ + * requester: claim.requester, + * token: claim.token, + * id: claim.id, + * subjectAgreement: claim.subjectAgreement, + * registrationTypes: claim.registrationTypes; + * issuerFields: [{key: 'foo', value: 'bar'}], + * publishOnChain: false, + * }); + * ``` + * + * @param {IssueClaimRequestOptions} options object containing options */ async issueClaimRequest({ requester, @@ -245,15 +335,7 @@ export class ClaimsService { registrationTypes, issuerFields, publishOnChain = true, - }: { - requester: string; - token: string; - id: string; - subjectAgreement: string; - registrationTypes: RegistrationTypes[]; - issuerFields?: { key: string; value: string | number }[]; - publishOnChain?: boolean; - }) { + }: IssueClaimRequestOptions): Promise { const { claimData, sub } = this._didRegistry.jwt.decode(token) as { claimData: { claimType: string; claimTypeVersion: number }; sub: string; @@ -320,53 +402,24 @@ export class ClaimsService { await this._cacheClient.issueClaim(this._signerService.did, message); } - private async issueVerifiablePresentation(options: { - subject: string; - namespace: string; - version: string; - issuerFields?: { key: string; value: string | number }[]; - }) { - const vc = await this._verifiableCredentialService.createRoleVC({ - id: options.subject, - namespace: options.namespace, - version: options.version, - issuerFields: options.issuerFields, - }); - const vp = - await this._verifiableCredentialService.createVerifiablePresentation([ - vc, - ]); - return JSON.stringify(vp); - } - /** + * Register issued on-chain claim on Claim Manager contract. * - * @param token optional token containing claimType, version and subject - * @returns claim params obtained from token - */ - private extractClaimRequest(token: string) { - const { claimData, sub } = this._didRegistry.jwt.decode(token) as { - claimData: { claimType: string; claimTypeVersion: string }; - sub: string; - }; - return { ...claimData, subject: sub }; - } - - /** - * @description Registers issued onchain claim with Claim manager + * ```typescript + * const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); + * claimsService.registerOnchain({ + * claimType: claim.claimType, + * claimTypeVersion: claim.claimTypeVersion, + * subjectAgreement: claim.subjectAgreement, + * onChainProof: claim.onChainProof, + * acceptedBy: claim.acceptedBy; + * subject: claim.subject, + * }); + * ``` * - * @param claim - id of signed onchain claim. - * @param token - @deprecated use subject, claimType, claimTypeVersion instead. Token should get removed and subject, claimType, claimTypeVersion should be required. + * @param {RegisterOnchainOptions} claim object containing options */ - async registerOnchain(claim: { - claimType?: string; - claimTypeVersion?: string; - token?: string; - subjectAgreement?: string; - onChainProof: string; - acceptedBy: string; - subject?: string; - }) { + async registerOnchain(claim: RegisterOnchainOptions): Promise { // backward compatibility with token if (claim.token) claim = { ...claim, ...this.extractClaimRequest(claim.token) }; @@ -412,15 +465,25 @@ export class ClaimsService { }); } + /** + * Reject claim request. + * + * ```typescript + * const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); + * claimsService.rejectClaimRequest({ + * id: claim.id, + * requesterDID: claim.requester, + * rejectionReason: 'some reason', + * }); + * ``` + * + * @param {RejectClaimRequestOptions} options object containing options + */ async rejectClaimRequest({ id, requesterDID, rejectionReason, - }: { - id: string; - requesterDID: string; - rejectionReason?: string; - }) { + }: RejectClaimRequestOptions): Promise { const message: IClaimRejection = { id, requester: requesterDID, @@ -432,23 +495,44 @@ export class ClaimsService { return this._cacheClient.rejectClaim(this._signerService.did, message); } - async deleteClaim({ id }: { id: string }) { + /** + * Delete claim request. Works only for pending claims (not issued or rejected). + * + * ```typescript + * claimsService.deleteClaim({ + * id: '7281a130-e2b1-430d-8c14-201010eae901', + * }); + * ``` + * + * @param {DeleteClaimOptions} options object containing options + */ + async deleteClaim({ id }: DeleteClaimOptions): Promise { await this._cacheClient.deleteClaim(id); } + /** + * Issue claim without previous request. Option available for issuers only. + * + * ```typescript + * claimsService.issueClaim({ + * claim: { + * claimType: 'email.roles.energyweb.iam.ewc', + * claimTypeVersion: 1, + * issuerFields: [{key: 'foo', value: 'bar'}], + * }; + * subject: 'did:ethr:0x00...0', + * registrationTypes: [RegistrationTypes.OnChain, RegistrationTypes.OffChain] + * }); + * ``` + * + * @param {IssueClaimOptions} options object containing options + * @return Issued token if registrationTypes includes RegistrationTypes.OffChain + */ async issueClaim({ subject, registrationTypes = [RegistrationTypes.OffChain], claim, - }: { - subject: string; - registrationTypes: RegistrationTypes[]; - claim: { - claimType: string; - claimTypeVersion: number; - issuerFields: { key: string; value: string | number }[]; - }; - }) { + }: IssueClaimOptions): Promise { await this.verifyIssuer(claim.claimType); await this.verifyEnrolmentPrerequisites({ subject, role: claim.claimType }); @@ -488,6 +572,8 @@ export class ClaimsService { return message.issuedToken; } + + // TODO: create docs annotations async getClaimId({ claimData }: { claimData: ClaimData }) { const { service = [] } = await this._didRegistry.getDidDocument(); const { id, claimTypeVersion } = @@ -513,46 +599,28 @@ export class ClaimsService { } /** + * Register role to claim manager contract if registrationTypes includes RegistrationTypes.OnChain + * Publish role to IPFS and add DID document service if registrationTypes includes RegistrationTypes.OffChain * - * @description validates publish public claim parameters depending on off or on chain registration type. Throws relevant error on invalid data. - * - */ - private validatePublishPublicClaimRequest( - registrationTypes: RegistrationTypes[], - claim: { token?: string; claimType?: string } - ) { - if ( - registrationTypes.includes(RegistrationTypes.OnChain) && - !claim.claimType - ) { - throw new Error( - ERROR_MESSAGES.CLAIM_TYPE_REQUIRED_FOR_ON_CHAIN_REGISTRATION - ); - } - if ( - registrationTypes.includes(RegistrationTypes.OffChain) && - !claim.token - ) { - throw new Error(ERROR_MESSAGES.TOKEN_REQUIRED_FOR_OFF_CHAIN_REGISTRATION); - } - } - - /** - * - * @description publishes claim off-chain (by storing claim data in ipfs and save url to DID document services) or registering on-chain depending on registrationTypes values. - * @returns ulr to ipfs - * @param token - @deprecated - use claim with claimType instead + * ```typescript + * const claim: Claim = await claimsService.getClaimById('7281a130-e2b1-430d-8c14-201010eae901'); + * claimsService.publishPublicClaim({ + * claim: { + * token: claim.token, + * claimType: claim.claimType, + * }; + * registrationTypes: claim.registrationTypes, + * }); + * ``` * + * @param {PublishPublicClaimOptions} options object containing options + * @return URl to IPFS if registrationTypes includes RegistrationTypes.OffChain */ async publishPublicClaim({ token, // backward compatibility registrationTypes = [RegistrationTypes.OffChain], claim, - }: { - token?: string; - registrationTypes?: RegistrationTypes[]; - claim: { token?: string; claimType?: string }; - }) { + }: PublishPublicClaimOptions): Promise { claim.token = claim.token || token; this.validatePublishPublicClaimRequest(registrationTypes, claim); let url: string | undefined = undefined; @@ -619,40 +687,58 @@ export class ClaimsService { } /** - * @description Creates claim with `data` and adds it to `subject` document. Signer must own or control subject + * Creates self signed off-chain claim with `data` and adds it to `subject` document. Signer must own or control subject. * - * @param data claim payload - * @param subject DID of claim subject + * ```typescript + * claimsService.createSelfSignedClaim({ + * data: { + * claimType: 'email.roles.energyweb.iam.ewc', + * claimTypeVersion: 1, + * issuerFields: [{key: 'foo', value: 'bar'}], + * profile: { + * name: 'John Doe', + * birthdate: '1990-01-01', + * address: '123 Main St', + * }, + * }, + * subject: 'did:ethr:volta:0x00...0', + * }); + * ``` * - * @returns claim url + * @param {CreateSelfSignedClaimOptions} options object containing options + * @return URl to IPFS */ async createSelfSignedClaim({ data, subject, - }: { - data: ClaimData; - subject?: string; - }) { + }: CreateSelfSignedClaimOptions): Promise { const token = await this._didRegistry.createPublicClaim({ data, subject }); return (await this.publishPublicClaim({ claim: { token } })) as string; } /** - * getUserClaims + * Get published off-chain claims of the given subject. * - * @description get published offchain claims + * ```typescript + * claimsService.getUserClaims({ + * did: 'did:ethr:0x00...0', + * }); + * ``` * + * @param {GetUserClaimsOptions} options object containing options + * @returns Claims containing DID document service endpoints */ async getUserClaims({ did = this._signerService.did, - }: { did?: string } | undefined = {}): Promise< - (IServiceEndpoint & ClaimData)[] - > { - const { service } = await this._didRegistry.getDidDocument({ did }); - const issuedClaims = await this.getClaimsBySubject({ - did, - isAccepted: true, - }); + }: GetUserClaimsOptions): Promise<(IServiceEndpoint & ClaimData)[]> { + const [{ service }, issuedClaims] = await Promise.all([ + this._didRegistry.getDidDocument({ did }), + this.getClaimsBySubject({ + did, + isAccepted: true, + }), + ]); + if (service.length === 0 || issuedClaims.length === 0) return []; const issuedClaimsTypes = issuedClaims @@ -664,13 +750,153 @@ export class ClaimsService { ); } + /** + * Create a public claim to prove identity. + * + * ```typescript + * claimsService.createIdentityProof(); + * ``` + * + * @return JWT token of created identity + */ + async createIdentityProof(): Promise { + const blockNumber = await this._signerService.provider.getBlockNumber(); + return this._didRegistry.createPublicClaim({ + data: { + blockNumber, + }, + }); + } + + /** + * Create a public claim to prove identity. + * + * ```typescript + * claimsService.createDelegateProof( + * '245a40a9...776071ca57cec', + * 'did:ethr:0x00...0', + * Algorithms.EIP191, + * ); + * ``` + * + * @param {String} delegateKey Private key of the delegate in hexadecimal format + * @param {String} identity DID of the delegate + * @param {String} algorithm Algorithm used to sign the delegate (EIP191 and ES256 available) + * + * @return JWT token of delegate + */ + async createDelegateProof( + delegateKey: string, + identity: string, + algorithm: Algorithms = Algorithms.EIP191 + ): Promise { + const provider = this._signerService.provider; + const blockNumber = (await provider.getBlockNumber()).toString(); + + const payload = { + iss: identity, + claimData: { + blockNumber, + }, + }; + if (algorithm === Algorithms.EIP191) { + return new JWT(new Wallet(delegateKey)).sign(payload, { + issuer: identity, + }); + } else if (algorithm === Algorithms.ES256) { + /** @todo move to @ew-did-registry/jwt */ + return jsonwebtoken.sign( + payload, + privToPem(delegateKey, KeyType.Secp256r1), + { + issuer: identity, + } + ); + } else { + throw new Error(ERROR_MESSAGES.JWT_ALGORITHM_NOT_SUPPORTED); + } + } + + /** + * Get `namespace` from claim type. + * + * ```typescript + * claimsService.getNamespaceFromClaimType( + * 'email.roles.energyweb.iam.ewc' + * ); + * ``` + * + * @param {String} claimType Private key of the delegate in hexadecimal format + * + * @return Namespace of given claim type + */ + getNamespaceFromClaimType(claimType: string): string { + return claimType.split('.roles.')[1]; + } + + /** + * Remove `fields` from claim data. + * + * @param {ClaimData} data Claim data to remove fields from + * @return Claim data without fields + */ + private stripClaimData(data: ClaimData): ClaimData { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { fields, ...claimData } = data; + + return claimData; + } + + /** + * Validates publish public claim parameters depending on off-chain or on-chain registration type. Throws relevant error on invalid data. + * + * @param {Array} registrationTypes Registration types of the claim + * @param {Object} claim `token` and `claimType` of the claim + */ + private validatePublishPublicClaimRequest( + registrationTypes: RegistrationTypes[], + claim: { token?: string; claimType?: string } + ): void { + if ( + registrationTypes.includes(RegistrationTypes.OnChain) && + !claim.claimType + ) { + throw new Error( + ERROR_MESSAGES.CLAIM_TYPE_REQUIRED_FOR_ON_CHAIN_REGISTRATION + ); + } + if ( + registrationTypes.includes(RegistrationTypes.OffChain) && + !claim.token + ) { + throw new Error(ERROR_MESSAGES.TOKEN_REQUIRED_FOR_OFF_CHAIN_REGISTRATION); + } + } + + /** + * Verify if the user is able to issue the given role. Throws an error when the user is not able to issue the given role. + * + * @param {String} role Registration types of the claim + */ + private async verifyIssuer(role: string): Promise { + if ( + !( + await this._cacheClient.getAllowedRolesByIssuer(this._signerService.did) + ).some((r) => r.namespace === role) + ) { + throw new NotAuthorizedIssuer(this._signerService.did, role); + } + } + + /** + * Verify claim request prerequisites for given role and subject. Throws relevant error on invalid data. + * + * @param {VerifyEnrolmentPrerequisitesOptions} options object containing options + */ private async verifyEnrolmentPrerequisites({ subject, role, - }: { - subject: string; - role: string; - }) { + }: VerifyEnrolmentPrerequisitesOptions): Promise { const roleDefinition = await this._domainsService.getDefinition({ type: NamespaceType.Role, namespace: role, @@ -700,73 +926,53 @@ export class ClaimsService { } } - private async verifyIssuer(role: string) { - if ( - !( - await this._cacheClient.getAllowedRolesByIssuer(this._signerService.did) - ).some((r) => r.namespace === role) - ) { - throw new NotAuthorizedIssuer(this._signerService.did, role); - } - } - - private async createOnChainProof( - role: string, - version: number, - expiry: number, - subject: string + /** + * Create verifiable credential and wrap it into verifiable presentation. + * + * @param {VerifyEnrolmentPrerequisitesOptions} options object containing options + * @return JSON representation of verifiable presentation + */ + private async issueVerifiablePresentation( + options: IssueVerifiablePresentationOptions ): Promise { - const messageId = Buffer.from(typedMsgPrefix, 'hex'); - - const chainId = this._signerService.chainId; - const domainSeparator = utils.keccak256( - defaultAbiCoder.encode( - ['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'], - [ - erc712_type_hash, - utils.id('Claim Manager'), - utils.id('1.0'), - chainId, - this._claimManager, - ] - ) - ); - - const proofHash = solidityKeccak256( - ['bytes', 'bytes32', 'bytes32'], - [ - messageId, - domainSeparator, - utils.keccak256( - defaultAbiCoder.encode( - ['bytes32', 'address', 'bytes32', 'uint', 'uint', 'address'], - [ - proof_type_hash, - addressOf(subject), - namehash(role), - version, - expiry, - this._signerService.address, - ] - ) - ), - ] - ); + const vc = await this._verifiableCredentialService.createRoleVC({ + id: options.subject, + namespace: options.namespace, + version: options.version, + issuerFields: options.issuerFields, + }); + const vp = + await this._verifiableCredentialService.createVerifiablePresentation([ + vc, + ]); + return JSON.stringify(vp); + } - return canonizeSig( - await this._signerService.signMessage(arrayify(proofHash)) - ); + /** + * Extract data from claim token. + * + * @param {String} token JWT token containing claimType, version and subject + * @return Claim data + */ + private extractClaimRequest(token: string) { + const { claimData, sub } = this._didRegistry.jwt.decode(token) as { + claimData: { claimType: string; claimTypeVersion: string }; + sub: string; + }; + return { ...claimData, subject: sub }; } + /** + * Create subject agreement signature. + * + * @param {ApproveRolePublishingOptions} options object containing options + * @return subject agreement signature + */ private async approveRolePublishing({ subject, role, version, - }: { - subject: string; - role: string; - version: number; - }) { + }: ApproveRolePublishingOptions): Promise { const erc712_type_hash = id( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ); @@ -810,72 +1016,60 @@ export class ClaimsService { } /** - * @description create a public claim to prove identity - * @returns JWT token of created identity - */ - async createIdentityProof() { - const blockNumber = await this._signerService.provider.getBlockNumber(); - return this._didRegistry.createPublicClaim({ - data: { - blockNumber, - }, - }); - } - - /** - * @description create a proof of identity delegate - * @param delegateKey private key of the delegate in hexadecimal format - * @param rpcUrl the url of the blockchain provider - * @param identity Did of the delegate - * @returns token of delegate - */ - async createDelegateProof( - delegateKey: string, - identity: string, - algorithm: Algorithms = Algorithms.EIP191 - ): Promise { - const provider = this._signerService.provider; - const blockNumber = await provider.getBlockNumber(); - - const payload = { - iss: identity, - claimData: { - blockNumber, - }, - }; - if (algorithm === Algorithms.EIP191) { - return new JWT(new Wallet(delegateKey)).sign(payload, { - issuer: identity, - }); - } else if (algorithm === Algorithms.ES256) { - /** @todo move to @ew-did-registry/jwt */ - return jsonwebtoken.sign( - payload, - privToPem(delegateKey, KeyType.Secp256r1), - { - issuer: identity, - } - ); - } else { - throw new Error(ERROR_MESSAGES.JWT_ALGORITHM_NOT_SUPPORTED); - } - } - - /** + * Create on-chain proof signature. * - * @description get `namespace` from claim type. - * @returns namespace - * @param {string} claimType + * @param {String} role role claim type + * @param {Number} version role version + * @param {Number} expiry time when the claim expires + * @param {String} subject DID of the subject * + * @return on-chain proof signature */ - getNamespaceFromClaimType(claimType: string) { - return claimType.split('.roles.')[1]; - } + private async createOnChainProof( + role: string, + version: number, + expiry: number, + subject: string + ): Promise { + const messageId = Buffer.from(typedMsgPrefix, 'hex'); - private stripClaimData(data: ClaimData): ClaimData { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { fields, ...claimData } = data; + const chainId = this._signerService.chainId; + const domainSeparator = utils.keccak256( + defaultAbiCoder.encode( + ['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'], + [ + erc712_type_hash, + utils.id('Claim Manager'), + utils.id('1.0'), + chainId, + this._claimManager, + ] + ) + ); - return claimData; + const proofHash = solidityKeccak256( + ['bytes', 'bytes32', 'bytes32'], + [ + messageId, + domainSeparator, + utils.keccak256( + defaultAbiCoder.encode( + ['bytes32', 'address', 'bytes32', 'uint', 'uint', 'address'], + [ + proof_type_hash, + addressOf(subject), + namehash(role), + version, + expiry, + this._signerService.address, + ] + ) + ), + ] + ); + + return canonizeSig( + await this._signerService.signMessage(arrayify(proofHash)) + ); } } diff --git a/src/modules/claims/claims.types.ts b/src/modules/claims/claims.types.ts index e9085672..d3cd65c7 100644 --- a/src/modules/claims/claims.types.ts +++ b/src/modules/claims/claims.types.ts @@ -1,4 +1,5 @@ import { utils } from 'ethers'; +import { ClaimData } from '../did-registry'; import { IMessage } from '../messaging/messaging.types'; export interface IClaimRequest extends IMessage { @@ -56,7 +57,7 @@ export interface Claim { } export const readyToBeRegisteredOnchain = ( - claim: Record + claim: unknown ): claim is Required< Pick< Claim, @@ -68,6 +69,8 @@ export const readyToBeRegisteredOnchain = ( | 'subjectAgreement' > > => { + if (!claim) return false; + if (typeof claim !== 'object') return false; const requiredProps = [ 'claimType', 'claimTypeVersion', @@ -96,3 +99,201 @@ export const defaultClaimExpiry = Number.MAX_SAFE_INTEGER - 1; // constraint of export type RequestClaim = { requester: string; message: IClaimRequest }; export type IssueClaim = { issuer: string; message: IClaimIssuance }; export type RejectClaim = { issuer: string; message: IClaimRejection }; + +export interface GetClaimsByRequesterOptions { + /** DID of the requestor */ + did: string; + + /** Indicates whether to show only accepted `Claims` */ + isAccepted?: boolean; + + /** Indicates what namespace `Claims` should be in */ + namespace?: string; +} + +export interface GetClaimsByIssuerOptions { + /** DID of the issuer */ + did: string; + + /** Indicates whether to show only accepted `Claims` */ + isAccepted?: boolean; + + /** Indicates what namespace `Claims` should be in */ + namespace?: string; +} + +export interface GetClaimsBySubjectOptions { + /** DID of the subject */ + did: string; + + /** Indicates whether to show only accepted `Claims` */ + isAccepted?: boolean; + + /** Indicates what namespace `Claims` should be in */ + namespace?: string; +} + +export interface CreateClaimRequestOptions { + /** Claim request params */ + claim: { + /** Role namespace */ + claimType: string; + + /** Version of the role */ + claimTypeVersion: number; + + /** Requestor fields that role is requiring */ + requestorFields?: { key: string; value: string | number }[]; + }; + + /** DID of the subject */ + subject?: string; + + /** Indicates what type of claim registration you are requesting: on-chain and/or off-chain */ + registrationTypes?: RegistrationTypes[]; +} + +export interface IssueClaimRequestOptions { + /** DID of the claim requestor */ + requester: string; + + /** JWT token generated by requestor during claim request */ + token: string; + + /** Claim id */ + id: string; + + /** Subject agreement signature */ + subjectAgreement: string; + + /** Registration types */ + registrationTypes: RegistrationTypes[]; + + /** Issuer fields that role is requiring */ + issuerFields?: { key: string; value: string | number }[]; + + /** Indicates whether to publish role on-chain or not (default: false) */ + publishOnChain?: boolean; +} + +export interface RegisterOnchainOptions { + /** Role namespace */ + claimType?: string; + + /** Version of the claim type */ + claimTypeVersion?: string; + + /** @deprecated */ + token?: string; + + /** Subject agreement signature */ + subjectAgreement?: string; + + /** On-chain proof signature */ + onChainProof: string; + + /** DID of the issuer */ + acceptedBy: string; + + /** DID of the claim subject */ + subject?: string; +} + +export interface RejectClaimRequestOptions { + /** Claim id */ + id: string; + + /** DID of the claim requestor */ + requesterDID: string; + + /** Reason for rejection */ + rejectionReason?: string; +} + +export interface DeleteClaimOptions { + /** Claim id */ + id: string; +} + +export interface IssueClaimOptions { + /** DID of the claim subject */ + subject: string; + + /** Registration types */ + registrationTypes?: RegistrationTypes[]; + + /** Claim params */ + claim: { + /** Role namespace */ + claimType: string; + + /** Version of the role */ + claimTypeVersion: number; + + /** Issuers fields that role is requiring */ + issuerFields?: { key: string; value: string | number }[]; + }; +} + +export interface PublishPublicClaimOptions { + /** @deprecated */ + token?: string; + + /** Registration types */ + registrationTypes?: RegistrationTypes[]; + + /** Claim params */ + claim: { + /** JWT token generated by requestor during claim request */ + token?: string; + + /** Role namespace */ + claimType?: string; + }; +} + +export interface CreateSelfSignedClaimOptions { + /** Claim data */ + data: ClaimData; + + /** DID of the claim subject */ + subject?: string; +} + +export interface GetUserClaimsOptions { + /** DID of the subject */ + did?: string; +} + +export interface VerifyEnrolmentPrerequisitesOptions { + /** DID of the subject */ + subject: string; + + /** Role claim type */ + role: string; +} + +export interface IssueVerifiablePresentationOptions { + /** DID of the subject */ + subject: string; + + /** Role claim type */ + namespace: string; + + /** Role version */ + version: string; + + /** Issuers fields that role is requiring */ + issuerFields?: { key: string; value: string | number }[]; +} + +export interface ApproveRolePublishingOptions { + /** DID of the subject */ + subject: string; + + /** Role claim type */ + role: string; + + /** Role version */ + version: number; +}