UNPKG

@0xpolygonid/js-sdk

Version:
367 lines (340 loc) 12.5 kB
import { DID } from '@iden3/js-iden3-core'; import { IDataStorage } from '../storage/interfaces'; import { W3CCredential, ProofQuery, VerifiableConstants, CredentialStatus, RevocationStatus, CredentialStatusType, State, DisplayMethodType } from './../verifiable'; import { JSONSchema } from '../schema-processor'; import * as uuid from 'uuid'; import { CredentialStatusResolverRegistry } from './status/resolver'; import { IssuerResolver } from './status/sparse-merkle-tree'; import { AgentResolver } from './status/agent-revocation'; import { CredentialStatusResolveOptions } from './status/resolver'; import { getUserDIDFromCredential } from './utils'; import { CredentialRequest } from './models'; /** * Interface to work with credential wallets * * @public * @interface ICredentialWallet */ export interface ICredentialWallet { /** * List of W3C Credential * * @returns `Promise<W3CCredential[]` */ list(): Promise<W3CCredential[]>; /** * saves W3C credential (upsert) * @param {W3CCredential} credential - credential to save * @returns `Promise<void>` * */ save(credential: W3CCredential): Promise<void>; /** * saves the batch of W3C credentials (upsert) * @param {W3CCredential[]} credentials - credentials to save * @returns `Promise<void>` */ saveAll(credentials: W3CCredential[]): Promise<void>; /** * * removes W3C credentials from data storage * @param {string} id * @returns `Promise<void>` */ remove(id: string): Promise<void>; /** * Find credential using iden3 query language * * @param {ProofQuery} query - protocol query to find credential * @returns `Promise<W3CCredential[]>` */ findByQuery(query: ProofQuery): Promise<W3CCredential[]>; /** * Finds the credential by its id * * @param {string} id - id of credential * @returns `Promise<W3CCredential | undefined>` */ findById(id: string): Promise<W3CCredential | undefined>; /** * Finds credentials by JSON-LD schema and type * * @param {string} context - the URL of JSON-LD schema where type is defined * @returns `Promise<W3CCredential[]>` */ findByContextType(context: string, type: string): Promise<W3CCredential[]>; /** * Filters given credentials with given credential subject * * @param {W3CCredential[]} credentials - credentials to filter * @param {DID} subject - credential subject id * @returns `Promise<W3CCredential[]>` */ filterByCredentialSubject(credentials: W3CCredential[], subject: DID): Promise<W3CCredential[]>; /** * Finds first non-revoked Auth BJJ credential for given user * * @param {DID} did - the issuer of Auth BJJ credential * @returns `Promise<W3CCredential>` W3CCredential with AuthBJJCredential type */ getAuthBJJCredential(did: DID): Promise<W3CCredential>; /** * Finds all Auth BJJ credentials for given user * * @param {DID} did - the issuer of Auth BJJ credential * @returns `Promise<W3CCredential[]>` W3CCredentials with AuthBJJCredential type */ getAllAuthBJJCredentials(did: DID): Promise<W3CCredential[]>; /** * Fetches or Builds a revocation status for a given credential * Supported types for credentialStatus field: SparseMerkleTreeProof, Iden3ReverseSparseMerkleTreeProof * * @param {W3CCredential} cred - credential for which lib should build revocation status * @returns `Promise<RevocationStatus>` */ getRevocationStatusFromCredential(cred: W3CCredential): Promise<RevocationStatus>; /** * Fetches Revocation status depended on type * * @param {(CredentialStatus )} credStatus - credentialStatus field of the Verifiable Credential. Supported types for credentialStatus field: SparseMerkleTreeProof, Iden3ReverseSparseMerkleTreeProof * @param {CredentialStatusResolveOptions} credentialStatusResolveOptions - options to resolve credential status * @returns `Promise<RevocationStatus>` */ getRevocationStatus( credStatus: CredentialStatus, credentialStatusResolveOptions?: CredentialStatusResolveOptions ): Promise<RevocationStatus>; /** * Creates a W3C verifiable Credential object * * @param {string} hostUrl - URL that will be used as a prefix for credential identifier * @param {DID} issuer - issuer identity * @param {CredentialRequest} request - specification of claim creation parameters * @param {JSONSchema} schema - JSON schema for W3C Verifiable Credential * @returns W3CCredential */ createCredential(issuer: DID, request: CredentialRequest, schema: JSONSchema): W3CCredential; /** * Finds non-revoked credential from a given list by resolving their credential status * * @param {W3CCredential[]} creds * @returns `{Promise<{ * cred: W3CCredential; * revStatus: RevocationStatus; * }>` not revoked credential and it's revocation status */ findNonRevokedCredential(creds: W3CCredential[]): Promise<{ cred: W3CCredential; revStatus: RevocationStatus; }>; /** * getCredentialStatusResolverRegistry * @returns {CredentialStatusResolverRegistry | undefined} - returns credential status resolver registry or undefined */ getCredentialStatusResolverRegistry(): CredentialStatusResolverRegistry | undefined; } /** * * Wallet instance is a wrapper of CRUD logic for W3C credentials, * also it allows to fetch revocation statuses. * * @public * @class CredentialWallet * @implements implements ICredentialWallet interface */ export class CredentialWallet implements ICredentialWallet { /** * Creates an instance of CredentialWallet. * @param {IDataStorage} _storage - data storage to access credential / identity / Merkle tree data * @param {CredentialStatusResolverRegistry} _credentialStatusResolverRegistry - list of credential status resolvers * if _credentialStatusResolverRegistry is not provided, default resolvers will be used */ constructor( private readonly _storage: IDataStorage, private readonly _credentialStatusResolverRegistry?: CredentialStatusResolverRegistry ) { // if no credential status resolvers are provided // register default issuer resolver if (!this._credentialStatusResolverRegistry) { this._credentialStatusResolverRegistry = new CredentialStatusResolverRegistry(); this._credentialStatusResolverRegistry.register( CredentialStatusType.SparseMerkleTreeProof, new IssuerResolver() ); this._credentialStatusResolverRegistry.register( CredentialStatusType.Iden3commRevocationStatusV1, new AgentResolver() ); } } /** * {@inheritDoc ICredentialWallet.getCredentialStatusResolverRegistry} */ getCredentialStatusResolverRegistry(): CredentialStatusResolverRegistry | undefined { return this._credentialStatusResolverRegistry; } /** * {@inheritDoc ICredentialWallet.getAuthBJJCredential} */ async getAuthBJJCredential(did: DID): Promise<W3CCredential> { // filter where the issuer of auth credential is given did const authBJJCredsOfIssuer = await this._storage.credential.findCredentialsByQuery({ context: VerifiableConstants.AUTH.AUTH_BJJ_CREDENTIAL_SCHEMA_JSONLD_URL, type: VerifiableConstants.AUTH.AUTH_BJJ_CREDENTIAL_TYPE, allowedIssuers: [did.string()] }); if (!authBJJCredsOfIssuer.length) { throw new Error(VerifiableConstants.ERRORS.NO_AUTH_CRED_FOUND); } for (let index = 0; index < authBJJCredsOfIssuer.length; index++) { const authCred = authBJJCredsOfIssuer[index]; const revocationStatus = await this.getRevocationStatusFromCredential(authCred); if (!revocationStatus.mtp.existence) { return authCred; } } throw new Error('all auth bjj credentials are revoked'); } /** * {@inheritDoc ICredentialWallet.getAllAuthBJJCredentials} */ async getAllAuthBJJCredentials(did: DID): Promise<W3CCredential[]> { return this._storage.credential.findCredentialsByQuery({ context: VerifiableConstants.AUTH.AUTH_BJJ_CREDENTIAL_SCHEMA_JSONLD_URL, type: VerifiableConstants.AUTH.AUTH_BJJ_CREDENTIAL_TYPE, allowedIssuers: [did.string()] }); } /** * {@inheritDoc ICredentialWallet.getRevocationStatusFromCredential} */ async getRevocationStatusFromCredential(cred: W3CCredential): Promise<RevocationStatus> { const mtpProof = cred.getIden3SparseMerkleTreeProof(); const sigProof = cred.getBJJSignature2021Proof(); const stateInfo: State | undefined = mtpProof ? mtpProof.issuerData.state : sigProof?.issuerData.state; const issuerDID = DID.parse(cred.issuer); const userDID = getUserDIDFromCredential(issuerDID, cred); const opts: CredentialStatusResolveOptions = { issuerGenesisState: stateInfo, issuerDID, userDID }; return this.getRevocationStatus(cred.credentialStatus, opts); } /** * {@inheritDoc ICredentialWallet.getRevocationStatus} */ async getRevocationStatus( credStatus: CredentialStatus, credentialStatusResolveOptions?: CredentialStatusResolveOptions ): Promise<RevocationStatus> { const statusResolver = this._credentialStatusResolverRegistry?.get(credStatus.type); if (!statusResolver) { throw new Error(`credential status resolver does not exist for ${credStatus.type} type`); } return statusResolver.resolve(credStatus, credentialStatusResolveOptions); } /** * {@inheritDoc ICredentialWallet.createCredential} */ createCredential = ( issuer: DID, request: CredentialRequest, schema: JSONSchema ): W3CCredential => { if (!schema.$metadata.uris['jsonLdContext']) { throw new Error('jsonLdContext is missing is the schema'); } // do copy of request to avoid mutation const r = { ...request }; r.context = r.context ?? []; if ( r.displayMethod?.type === DisplayMethodType.Iden3BasicDisplayMethodV1 && !r.context.includes(VerifiableConstants.JSONLD_SCHEMA.IDEN3_DISPLAY_METHOD) ) { r.context.push(VerifiableConstants.JSONLD_SCHEMA.IDEN3_DISPLAY_METHOD); } r.context.push(schema.$metadata.uris['jsonLdContext']); r.expiration = r.expiration ? r.expiration * 1000 : undefined; r.id = r.id ? r.id : `urn:${uuid.v4()}`; r.issuanceDate = r.issuanceDate ? r.issuanceDate * 1000 : Date.now(); return W3CCredential.fromCredentialRequest(issuer, r); }; /** * {@inheritDoc ICredentialWallet.findById} */ async findById(id: string): Promise<W3CCredential | undefined> { return this._storage.credential.findCredentialById(id); } /** * {@inheritDoc ICredentialWallet.findByContextType} */ async findByContextType(context: string, type: string): Promise<W3CCredential[]> { return this._storage.credential.findCredentialsByQuery({ context, type }); } /** * {@inheritDoc ICredentialWallet.save} */ async save(credential: W3CCredential): Promise<void> { return this._storage.credential.saveCredential(credential); } /** * {@inheritDoc ICredentialWallet.saveAll} */ async saveAll(credentials: W3CCredential[]): Promise<void> { return this._storage.credential.saveAllCredentials(credentials); } /** * {@inheritDoc ICredentialWallet.remove} */ async remove(id: string): Promise<void> { return this._storage.credential.removeCredential(id); } /** * {@inheritDoc ICredentialWallet.list} */ async list(): Promise<W3CCredential[]> { return this._storage.credential.listCredentials(); } /** * {@inheritDoc ICredentialWallet.findByQuery} */ async findByQuery(query: ProofQuery): Promise<W3CCredential[]> { return this._storage.credential.findCredentialsByQuery(query); } /** * {@inheritDoc ICredentialWallet.filterByCredentialSubject} */ async filterByCredentialSubject( credentials: W3CCredential[], subject: DID ): Promise<W3CCredential[]> { return credentials.filter((cred: W3CCredential) => { return cred.credentialSubject['id'] === subject.string(); }); } async findNonRevokedCredential(creds: W3CCredential[]): Promise<{ cred: W3CCredential; revStatus: RevocationStatus; }> { for (const cred of creds) { const revStatus = await this.getRevocationStatusFromCredential(cred); if (revStatus.mtp.existence) { continue; } return { cred, revStatus }; } throw new Error(VerifiableConstants.ERRORS.CREDENTIAL_WALLET_ALL_CREDENTIALS_ARE_REVOKED); } }