Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Credential Service Staging - Resolve issue with page not loading and broken login [DEV-3162] #358

Merged
merged 8 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 145 additions & 41 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
"@cosmjs/amino": "^0.31.1",
"@cosmjs/encoding": "^0.31.1",
"@logto/express": "^2.1.0",
"@types/jsonwebtoken": "^9.0.2",
"@veramo/core": "^5.4.1",
"@veramo/credential-ld": "^5.4.1",
"@veramo/credential-w3c": "^5.4.1",
Expand Down Expand Up @@ -100,6 +99,7 @@
"@types/express-session": "^1.17.7",
"@types/helmet": "^4.0.0",
"@types/json-stringify-safe": "^5.0.0",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "^20.5.6",
"@types/secp256k1": "^4.0.3",
"@types/swagger-jsdoc": "^6.0.1",
Expand Down
11 changes: 11 additions & 0 deletions src/controllers/revocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,17 @@ export class RevocationController {
}`,
} satisfies CheckStatusListUnsuccessfulResponseBody);

// handle incorrect access control conditions
if (errorRef?.errorCode === 'incorrect_access_control_conditions')
return response.status(StatusCodes.BAD_REQUEST).json({
checked: false,
error: `check: error: ${
errorRef?.message
? 'incorrect access control conditions'
: (error as Record<string, unknown>).toString()
}`,
} satisfies CheckStatusListUnsuccessfulResponseBody);

// return catch-all error
return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
checked: false,
Expand Down
6 changes: 5 additions & 1 deletion src/monkey-patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export const JSONStringify = (obj: Record<string, any> | null) => {
return tempArr.join('');
};

const escape = (str: string) => {
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
};

if (ignoreDataTypes(obj)) {
return undefined;
}
Expand All @@ -88,7 +92,7 @@ export const JSONStringify = (obj: Record<string, any> | null) => {

if (restOfDataTypes(obj)) {
const passQuotes = isString(obj) ? `"` : '';
return `${passQuotes}${obj}${passQuotes}`;
return `${passQuotes}${isString(obj) ? escape(obj as unknown as string) : obj}${passQuotes}`;
}

if (isArray(obj)) {
Expand Down
4 changes: 2 additions & 2 deletions src/services/connectors/verida.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class VeridaService {
)
}

const messagingClient = await this.context!.getMessaging()
const messagingClient = await this.context?.getMessaging()

const messageType = 'inbox/type/dataSend' // There are different types of message, here we are sending some data.
const messageData = {
Expand All @@ -93,7 +93,7 @@ export class VeridaService {
did: recipientDid,
}

await messagingClient.send(
await messagingClient?.send(
recipientDid,
messageType,
messageData,
Expand Down
5 changes: 4 additions & 1 deletion src/services/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { CredentialPayload, VerifiableCredential } from '@veramo/core';
import { VC_CONTEXT, VC_TYPE } from '../types/constants.js';
import type { CredentialRequest } from '../types/shared.js';
import { IdentityServiceStrategySetup } from './identity/index.js';
import { VeridaService } from '../services/connectors/verida.js';
import { v4 } from 'uuid';
import * as dotenv from 'dotenv';
dotenv.config();
Expand Down Expand Up @@ -41,6 +40,10 @@ export class Credentials {

if (ENABLE_VERIDA_CONNECTOR === 'true' && request.subjectDid.startsWith('did:vda')) {
if (!request.credentialSchema) throw new Error('Credential schema is required');

// dynamic import to avoid circular dependency
const { VeridaService } = await import('./connectors/verida.js');

await VeridaService.instance.sendCredential(
request.subjectDid,
'New Verifiable Credential',
Expand Down
28 changes: 20 additions & 8 deletions src/services/identity/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { KeyManager } from '@veramo/key-manager';
import { DIDStore, KeyStore } from '@veramo/data-store';
import { DIDManager } from '@veramo/did-manager';
import { DIDResolverPlugin, getUniversalResolver as UniversalResolver } from '@veramo/did-resolver';
import { getResolver as VeridaResolver } from '@verida/vda-did-resolver';
import { CredentialPlugin } from '@veramo/credential-w3c';
import { CredentialIssuerLD, LdDefaultContexts, VeramoEd25519Signature2018 } from '@veramo/credential-ld';
import {
Expand Down Expand Up @@ -51,7 +50,6 @@ import {
import type { CheqdNetwork } from '@cheqd/sdk';
import { getDidKeyResolver as KeyDidResolver } from '@veramo/did-provider-key';
import { Resolver, ResolverRegistry } from 'did-resolver';

import {
BroadcastStatusListOptions,
CheckStatusListOptions,
Expand All @@ -74,6 +72,10 @@ import {
import { MINIMAL_DENOM, VC_PROOF_FORMAT, VC_REMOVE_ORIGINAL_FIELDS } from '../../types/constants.js';
import { toCoin, toDefaultDkg, toMinimalDenom } from '../../helpers/helpers.js';

// dynamic import to avoid circular dependency
const VeridaResolver =
process.env.ENABLE_VERIDA_CONNECTOR === 'true' ? (await import('@verida/vda-did-resolver')).getResolver : undefined;

export class Veramo {
static instance = new Veramo();

Expand Down Expand Up @@ -115,14 +117,24 @@ export class Veramo {
}

if (enableResolver) {
// construct resolver map
const resolvers = {
...(CheqdDidResolver({ url: process.env.RESOLVER_URL }) as ResolverRegistry),
...KeyDidResolver(),
...UniversalResolver(),
};

// handle optional dependencies
if (VeridaResolver) {
const veridaResolver = VeridaResolver();

// add verida resolver to resolver map
Object.assign(resolvers, veridaResolver);
}

plugins.push(
new DIDResolverPlugin({
resolver: new Resolver({
...(CheqdDidResolver({ url: process.env.RESOLVER_URL }) as ResolverRegistry),
...KeyDidResolver(),
...VeridaResolver(),
...UniversalResolver(),
}),
resolver: new Resolver(resolvers),
})
);
}
Expand Down
111 changes: 56 additions & 55 deletions src/services/identity/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
import type { AbstractPrivateKeyStore } from '@veramo/key-manager';
import { KeyManagementSystem, SecretBox } from '@veramo/kms-local';
import { PrivateKeyStore } from '@veramo/data-store';
import { CheqdNetwork } from '@cheqd/sdk'
import { CheqdNetwork } from '@cheqd/sdk';
import {
Cheqd,
CheqdDIDProvider,
Expand Down Expand Up @@ -127,7 +127,7 @@ export class PostgresIdentityService extends DefaultIdentityService {
async getKey(kid: string, agentId: string) {
const isOwner = await CustomerService.instance.find(agentId, { kid });
if (!isOwner) {
throw new Error(`${kid} not found in wallet`)
throw new Error(`${kid} not found in wallet`);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return await Veramo.instance.getKey(this.agent!, kid);
Expand Down Expand Up @@ -169,11 +169,11 @@ export class PostgresIdentityService extends DefaultIdentityService {
throw new Error('Customer not found');
}
try {
const agent = await this.createAgent(agentId)
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
const agent = await this.createAgent(agentId);
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.deactivateDid(agent, did)
return await Veramo.instance.deactivateDid(agent, did);
} catch (error) {
throw new Error(`${error}`);
}
Expand Down Expand Up @@ -205,12 +205,12 @@ export class PostgresIdentityService extends DefaultIdentityService {

async createResource(network: string, payload: ResourcePayload, agentId: string) {
try {
const agent = await this.createAgent(agentId)
const did = `did:cheqd:${network}:${payload.collectionId}`
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
const agent = await this.createAgent(agentId);
const did = `did:cheqd:${network}:${payload.collectionId}`;
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.createResource(agent, network, payload)
return await Veramo.instance.createResource(agent, network, payload);
} catch (error) {
throw new Error(`${error}`);
}
Expand Down Expand Up @@ -259,9 +259,9 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
): Promise<CreateStatusList2021Result> {
const agent = await this.createAgent(agentId);
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
}
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.createUnencryptedStatusList2021(agent, did, resourceOptions, statusOptions);
}

Expand All @@ -272,9 +272,9 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
): Promise<CreateStatusList2021Result> {
const agent = await this.createAgent(agentId);
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
}
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.createEncryptedStatusList2021(agent, did, resourceOptions, statusOptions);
}

Expand All @@ -284,9 +284,9 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
): Promise<BulkRevocationResult | BulkSuspensionResult | BulkUnsuspensionResult> {
const agent = await this.createAgent(agentId);
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
}
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.updateUnencryptedStatusList2021(agent, did, statusOptions);
}

Expand All @@ -296,9 +296,9 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
): Promise<BulkRevocationResult | BulkSuspensionResult | BulkUnsuspensionResult> {
const agent = await this.createAgent(agentId);
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
}
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.updateUnencryptedStatusList2021(agent, did, statusOptions);
}

Expand All @@ -308,9 +308,9 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
): Promise<StatusCheckResult> {
const agent = await this.createAgent(agentId);
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
}
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.checkStatusList2021(agent, did, statusOptions);
}

Expand All @@ -321,9 +321,9 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
): Promise<boolean> {
const agent = await this.createAgent(agentId);
if (!await CustomerService.instance.find(agentId, { did })) {
throw new Error(`${did} not found in wallet`)
}
if (!(await CustomerService.instance.find(agentId, { did }))) {
throw new Error(`${did} not found in wallet`);
}
return await Veramo.instance.broadcastStatusList2021(agent, did, resourceOptions, statusOptions);
}

Expand All @@ -338,7 +338,7 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
) {
const agent = await this.createAgent(agentId);
await this.validateCredentialAccess(credentials, agentId)
await this.validateCredentialAccess(credentials, agentId);
return await Veramo.instance.revokeCredentials(agent, credentials, publish);
}

Expand All @@ -348,7 +348,7 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
) {
const agent = await this.createAgent(agentId);
await this.validateCredentialAccess(credentials, agentId)
await this.validateCredentialAccess(credentials, agentId);
return await Veramo.instance.suspendCredentials(agent, credentials, publish);
}

Expand All @@ -358,31 +358,32 @@ export class PostgresIdentityService extends DefaultIdentityService {
agentId: string
) {
const agent = await this.createAgent(agentId);
await this.validateCredentialAccess(credentials, agentId)
await this.validateCredentialAccess(credentials, agentId);
return await Veramo.instance.unsuspendCredentials(agent, credentials, publish);
}

private async validateCredentialAccess(credentials: VerifiableCredential | VerifiableCredential[], agentId: string) {
credentials = Array.isArray(credentials) ? credentials : [credentials]
const customer = await CustomerService.instance.get(agentId) as CustomerEntity | null
if(!customer) {
throw new Error('Customer not found')
}

for(const credential of credentials) {
const decodedCredential = typeof credential === 'string'
? await Cheqd.decodeCredentialJWT(credential)
: credential

const issuerId = typeof decodedCredential.issuer === 'string'
? decodedCredential.issuer
: decodedCredential.issuer.id

const existsInWallet = customer.dids.find((did) => did === issuerId)

if (!existsInWallet) {
throw new Error(`${issuerId} not found in wallet`)
}
}
}
private async validateCredentialAccess(
credentials: VerifiableCredential | VerifiableCredential[],
agentId: string
) {
credentials = Array.isArray(credentials) ? credentials : [credentials];
const customer = (await CustomerService.instance.get(agentId)) as CustomerEntity | null;
if (!customer) {
throw new Error('Customer not found');
}

for (const credential of credentials) {
const decodedCredential =
typeof credential === 'string' ? await Cheqd.decodeCredentialJWT(credential) : credential;

const issuerId =
typeof decodedCredential.issuer === 'string' ? decodedCredential.issuer : decodedCredential.issuer.id;

const existsInWallet = customer.dids.find((did) => did === issuerId);

if (!existsInWallet) {
throw new Error(`${issuerId} not found in wallet`);
}
}
}
}
8 changes: 5 additions & 3 deletions src/static/custom-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ window.addEventListener('load', function () {
window.location.href = base_url + '/logto/sign-out';
};
const auth_pan = document.createElement('div');
auth_pan.classList.add('auth-wrapper');
auth_pan.classList.add('auth-wrapper', 'wrapper');
auth_pan.appendChild(login_button);

const scheme_pan = document.getElementsByClassName('scheme-container')[0];
scheme_pan.children[0].appendChild(auth_pan);
const info_div = document.getElementsByClassName('information-container')[0];
if (info_div) {
info_div.insertAdjacentElement('afterend', auth_pan);
}

isAuthenticated().then(function (value) {
if (value) {
Expand Down
2 changes: 1 addition & 1 deletion src/types/swagger-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@
* type: string
* example: cheqd1qs0nhyk868c246defezhz5eymlt0dmajna2csg
* feePaymentAmount:
* description: Amount in CHEQ tokens to unlocked the encrypted StatusList2021 DID-Linked Resource.
* description: Amount in CHEQ tokens to unlock the encrypted StatusList2021 DID-Linked Resource.
* type: number
* minimum: 0
* exclusiveMinimum: true
Expand Down