Skip to content

Commit

Permalink
Merge branch 'main' into feature/auth-response-handler
Browse files Browse the repository at this point in the history
  • Loading branch information
volodymyr-basiuk committed Jan 30, 2024
2 parents 86fce2b + 6737fe4 commit f1cdb5a
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 130 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@0xpolygonid/js-sdk",
"version": "1.7.2",
"version": "1.7.3",
"description": "SDK to work with Polygon ID",
"main": "dist/node/cjs/index.js",
"module": "dist/node/esm/index.js",
Expand Down
140 changes: 133 additions & 7 deletions src/iden3comm/handlers/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ import { MediaType } from '../constants';
import { PROTOCOL_MESSAGE_TYPE } from '../constants';

import {
CredentialFetchRequestMessage,
CredentialIssuanceMessage,
CredentialsOfferMessage,
IPackageManager,
JWSPackerParams,
MessageFetchRequestMessage
} from '../types';

import * as uuid from 'uuid';
import { W3CCredential } from '../../verifiable';
import { ICredentialWallet, getUserDIDFromCredential } from '../../credentials';

import { byteDecoder, byteEncoder } from '../../utils';
import { proving } from '@iden3/js-jwz';
import { DID } from '@iden3/js-iden3-core';
import * as uuid from 'uuid';

/**
*
Expand Down Expand Up @@ -48,6 +52,24 @@ export interface IFetchHandler {
}>`
*/
handleCredentialOffer(offer: Uint8Array, opts?: FetchHandlerOptions): Promise<W3CCredential[]>;

/**
* Handles a credential fetch request.
*
* @param basicMessage - The basic message containing the fetch request.
* @returns A promise that resolves to the response message.
* @throws An error if the request is invalid or if the credential is not found.
*/
handleCredentialFetchRequest(basicMessage: Uint8Array): Promise<Uint8Array>;

/**
* Handles the issuance response message.
*
* @param basicMessage - The basic message containing the issuance response.
* @returns A promise that resolves to a Uint8Array.
* @throws An error if the credential wallet is not provided in the options or if the credential is missing in the issuance response message.
*/
handleIssuanceResponseMessage(basicMessage: Uint8Array): Promise<Uint8Array>;
}
/**
*
Expand All @@ -63,7 +85,12 @@ export class FetchHandler implements IFetchHandler {
* Creates an instance of FetchHandler.
* @param {IPackageManager} _packerMgr - package manager to unpack message envelope
*/
constructor(private readonly _packerMgr: IPackageManager) {}
constructor(
private readonly _packerMgr: IPackageManager,
private readonly opts?: {
credentialWallet: ICredentialWallet;
}
) {}

/**
* Handles only messages with credentials/1.0/offer type
Expand All @@ -88,11 +115,12 @@ export class FetchHandler implements IFetchHandler {
throw new Error(`jws packer options are required for ${MediaType.SignedMessage}`);
}

const { unpackedMessage: message } = await this._packerMgr.unpack(offer);
const offerMessage = message as unknown as CredentialsOfferMessage;
if (message.type !== PROTOCOL_MESSAGE_TYPE.CREDENTIAL_OFFER_MESSAGE_TYPE) {
throw new Error('Invalid media type');
}
const offerMessage = await FetchHandler.unpackMessage<CredentialsOfferMessage>(
this._packerMgr,
offer,
PROTOCOL_MESSAGE_TYPE.CREDENTIAL_OFFER_MESSAGE_TYPE
);

const credentials: W3CCredential[] = [];

for (let index = 0; index < offerMessage.body.credentials.length; index++) {
Expand Down Expand Up @@ -155,4 +183,102 @@ export class FetchHandler implements IFetchHandler {
}
return credentials;
}

/**
* @inheritdoc IFetchHandler#handleCredentialFetchRequest
*/
async handleCredentialFetchRequest(envelope: Uint8Array): Promise<Uint8Array> {
const msgRequest = await FetchHandler.unpackMessage<CredentialFetchRequestMessage>(
this._packerMgr,
envelope,
PROTOCOL_MESSAGE_TYPE.CREDENTIAL_FETCH_REQUEST_MESSAGE_TYPE
);

if (!msgRequest.to) {
throw new Error("failed request. empty 'to' field");
}

if (!msgRequest.from) {
throw new Error("failed request. empty 'from' field");
}

const issuerDID = DID.parse(msgRequest.to);
const userDID = DID.parse(msgRequest.from);

const credId = msgRequest.body?.id;
if (!credId) {
throw new Error('nvalid credential id in fetch request body');
}

if (!this.opts?.credentialWallet) {
throw new Error('please, provide credential wallet in options');
}

const cred = await this.opts.credentialWallet.findById(credId);

if (!cred) {
throw new Error('credential not found');
}

const userToVerifyDID = getUserDIDFromCredential(issuerDID, cred);

if (userToVerifyDID.string() !== userDID.string()) {
throw new Error('credential subject is not a sender DID');
}

return this._packerMgr.pack(
MediaType.PlainMessage,
byteEncoder.encode(
JSON.stringify({
id: uuid.v4(),
type: PROTOCOL_MESSAGE_TYPE.CREDENTIAL_ISSUANCE_RESPONSE_MESSAGE_TYPE,
typ: msgRequest.typ ?? MediaType.PlainMessage,
thid: msgRequest.thid ?? uuid.v4(),
body: { credential: cred },
from: msgRequest.to,
to: msgRequest.from
})
),
{}
);
}

/**
* @inheritdoc IFetchHandler#handleIssuanceResponseMessage
*/
async handleIssuanceResponseMessage(envelop: Uint8Array): Promise<Uint8Array> {
const issuanceMsg = await FetchHandler.unpackMessage<CredentialIssuanceMessage>(
this._packerMgr,
envelop,
PROTOCOL_MESSAGE_TYPE.CREDENTIAL_ISSUANCE_RESPONSE_MESSAGE_TYPE
);

if (!this.opts?.credentialWallet) {
throw new Error('please provide credential wallet in options');
}

if (!issuanceMsg.body?.credential) {
throw new Error('credential is missing in issuance response message');
}

await this.opts.credentialWallet.save(W3CCredential.fromJSON(issuanceMsg.body.credential));

return Promise.resolve(Uint8Array.from([]));
}

/**
* @inheritdoc IFetchHandler#unpackMessage
*/
static async unpackMessage<T>(
packerMgr: IPackageManager,
envelope: Uint8Array,
messageType: string
): Promise<T> {
const { unpackedMessage: message } = await packerMgr.unpack(envelope);
const msgRequest = message as unknown as T;
if (message.type !== messageType) {
throw new Error('Invalid message type');
}
return msgRequest;
}
}
5 changes: 5 additions & 0 deletions src/verifiable/credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ export class W3CCredential {
}

const revocationNonce = BigInt(proof.issuerData.credentialStatus.revocationNonce || 0);
if (revocationNonce !== proof.issuerData.authCoreClaim.getRevocationNonce()) {
throw new Error(
`revocation nonce mismatch: revocation nonce from core representation of auth credential is not the same as in its credential`
);
}
const proofValid = await verifyProof(
Hash.fromHex(credStatus.issuer.revocationTreeRoot),
credStatus.mtp,
Expand Down
Loading

0 comments on commit f1cdb5a

Please sign in to comment.