Skip to content

Commit

Permalink
feat: updating DIDComm protocols closer to specification
Browse files Browse the repository at this point in the history
Signed-off-by: Curtish <[email protected]>
  • Loading branch information
curtis-h committed Feb 12, 2025
1 parent e2bf9bc commit 9e18487
Show file tree
Hide file tree
Showing 20 changed files with 651 additions and 657 deletions.
2 changes: 1 addition & 1 deletion src/edge-agent/didcomm/HandleOfferCredential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class HandleOfferCredential extends Task<RequestCredential, Args> {
attach_id: response.id,
format: `${response.format}`,
}],
goalCode: offer.body.goalCode,
goal_code: offer.body.goal_code,
comment: offer.body.comment,
},
[response],
Expand Down
92 changes: 3 additions & 89 deletions src/edge-agent/helpers/ProtocolHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { AttachmentDescriptor, Message } from "../../domain";
import { AgentError } from "../../domain/models/Errors";
import { asArray, isArray, isEmpty, isNil, isObject, isString, notEmptyString, notNil } from "../../utils";
import { ProtocolType } from "../protocols/ProtocolTypes";
import { isArray, isEmpty, isNil, isString, notEmptyString, notNil } from "../../utils";
import { CredentialFormat } from "../protocols/issueCredential/CredentialFormat";
import {
ProposeCredentialBody,
OfferCredentialBody,
IssueCredentialBody,
CredentialBody,
MediationGrantBody,
PresentationBody,
RequestPresentationBody,
Expand Down Expand Up @@ -40,17 +35,6 @@ export const parseCredentialAttachments = (credentials: Map<string, any>) => {
);
};

const parseCredentialFormat = (value: unknown): CredentialFormat => {
if (!isObject(value) || isNil(value.attach_id) || isNil(value.format)) {
throw new AgentError.InvalidCredentialFormats();
}

return {
attach_id: value.attach_id,
format: value.format,
};
};

export const parseBasicMessageBody = (msg: Message): BasicMessageBody => {
if (notEmptyString(msg.body.content)) {
return {
Expand All @@ -68,82 +52,12 @@ export const parseProblemReportBody = (msg: Message): ProblemReportBody => {
isArray(msg.body.args) && isEmpty(msg.body.args)
) {
const { code, comment, escalate_to, args } = msg.body;
return { code, comment, escalate_to, args }
}
throw new AgentError.InvalidProblemReportBodyError()
}

export const parseCredentialBody = (msg: Message): CredentialBody => {
if (Object.keys(msg.body).length === 0) {
throw new AgentError.InvalidCredentialBodyError(
"Invalid CredentialBody Error"
);
}

if (notNil(msg.body.formats) && !isArray(msg.body.formats)) {
throw new AgentError.InvalidCredentialFormats();
return { code, comment, escalate_to, args };
}

return {
formats: asArray(msg.body.formats).map(x => parseCredentialFormat(x)),
goalCode: msg.body.goalCode,
comment: msg.body.comment,
};
throw new AgentError.InvalidProblemReportBodyError();
};

export const parseOfferCredentialMessage = (msg: Message): OfferCredentialBody => {
if (msg.piuri !== ProtocolType.DidcommOfferCredential) {
throw new AgentError.UnknownCredentialBodyError();
}

if (isNil(msg.body.credential_preview)) {
throw new AgentError.InvalidOfferCredentialBodyError("Undefined credentialPreview");
}

const credentialBody = parseCredentialBody(msg);

return {
...credentialBody,
credential_preview: msg.body.credential_preview,
replacementId: msg.body.replacementId,
multipleAvailable: msg.body.multipleAvailable,
};
};

export const parseIssueCredentialMessage = (msg: Message): IssueCredentialBody => {
if (msg.piuri !== ProtocolType.DidcommIssueCredential) {
throw new AgentError.UnknownCredentialBodyError();
}

if (notNil(msg.body.replacementId) && !isString(msg.body.replacementId)) {
throw new AgentError.InvalidIssueCredentialBodyError(
"Invalid replacementId, should be a string"
);
}

const credentialBody = parseCredentialBody(msg);

return {
...credentialBody,
replacementId: msg.body.replacementId,
moreAvailable: msg.body.moreAvailable,
};
};

export const parseProposeCredentialMessage = (msg: Message): ProposeCredentialBody => {
if (isNil(msg.body.credential_preview)) {
throw new AgentError.InvalidProposeCredentialBodyError(
"Undefined credentialPreview"
);
}

const credentialBody = parseCredentialBody(msg);

return {
...credentialBody,
credential_preview: msg.body.credential_preview,
};
};

export const parseMediationGrantMessage = (msg: Message): MediationGrantBody => {
if (isNil(msg.body.routing_did)) {
Expand Down
41 changes: 24 additions & 17 deletions src/edge-agent/protocols/issueCredential/CredentialPreview.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import { ProtocolType } from "../ProtocolTypes";
import { Nil, isArray, isObject, notEmptyString } from "../../../utils";

/**
* Specification:
* https://github.com/decentralized-identity/waci-didcomm/tree/main/issue_credential#preview-credential
*/

export interface CredentialPreview {
// type: ProtocolType.DidcommCredentialPreview;
type: string;
id?: string;
body: {
attributes: Attribute[];
};
}

export interface Attribute {
name: string;
value: string;
mimeType?: string;
media_type?: string | Nil;
}

export interface CredentialPreview {
type: ProtocolType.DidcommCredentialPreview;
attributes: Attribute[];
}
export const validateCredentialPreview = (value: unknown): value is CredentialPreview => isObject(value)
&& notEmptyString(value.type)
&& isObject(value.body)
&& isArray(value.body.attributes)
&& value.body.attributes.every(validateAttribute);

export function createCredentialPreviewAttribute(
name: string,
value: string,
mimeType?: string
): Attribute {
return {
name,
value,
mimeType,
};
}
const validateAttribute = (value: unknown): value is Attribute => isObject(value)
&& notEmptyString(value.name)
&& notEmptyString(value.value);
85 changes: 21 additions & 64 deletions src/edge-agent/protocols/issueCredential/IssueCredential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { uuid } from "@stablelib/uuid";
import { AttachmentDescriptor, DID, Message } from "../../../domain";
import { AgentError } from "../../../domain/models/Errors";
import { ProtocolType } from "../ProtocolTypes";
import { CredentialFormat } from "./CredentialFormat";
import { RequestCredential } from "./RequestCredential";
import { base64url } from "multiformats/bases/base64";
import { IssueCredentialBody } from "../types";
import { isNil } from "../../../utils";
import { parseCredentialAttachments, parseIssueCredentialMessage } from "../../helpers/ProtocolHelpers";

/**
* Specification:
* https://github.com/decentralized-identity/waci-didcomm/tree/main/issue_credential#issue-credential
*/

export interface IssueCredentialBody {
// optional field that provides human readable information about the issued credential
comment?: string;
// optional field that provides an identifier used to manage credential replacement
replacement_id?: string;
}

export class IssueCredential {
public static type = ProtocolType.DidcommIssueCredential;
Expand Down Expand Up @@ -47,75 +55,24 @@ export class IssueCredential {
}, initialValue);
}

static fromMessage(fromMessage: Message): IssueCredential {
static fromMessage(msg: Message): IssueCredential {
if (
fromMessage.piuri !== ProtocolType.DidcommIssueCredential ||
isNil(fromMessage.from) ||
isNil(fromMessage.to)
msg.piuri !== ProtocolType.DidcommIssueCredential
|| isNil(msg.from)
|| isNil(msg.to)
) {
throw new AgentError.InvalidIssueCredentialMessageError(
"Invalid issue credential message error."
);
}
const issueCredentialBody = parseIssueCredentialMessage(fromMessage);

return new IssueCredential(
issueCredentialBody,
fromMessage.attachments,
fromMessage.from,
fromMessage.to,
fromMessage.thid,
fromMessage.id
);
}

static makeIssueFromRequestCredential(msg: Message): IssueCredential {
const request = RequestCredential.fromMessage(msg);
return new IssueCredential(
createIssueCredentialBody(
request.body.formats,
request.body.goalCode,
request.body.comment
),
request.attachments,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
request.to!,
request.from,
msg.body,
msg.attachments,
msg.from,
msg.to,
msg.thid,
msg.id
);
}

static build<T>(
fromDID: DID,
toDID: DID,
thid?: string,
credentials: Map<string, T> = new Map()
): IssueCredential {
const { formats, attachments } = parseCredentialAttachments(credentials);
const issueCredentialBody = createIssueCredentialBody(formats);

return new IssueCredential(
issueCredentialBody,
attachments,
fromDID,
toDID,
thid
);
}
}

export function createIssueCredentialBody(
formats: CredentialFormat[],
goalCode?: string,
comment?: string,
replacementId?: string,
moreAvailable?: string
) {
return {
formats,
goalCode,
comment,
replacementId,
moreAvailable,
};
}
Loading

0 comments on commit 9e18487

Please sign in to comment.