Skip to content

Commit

Permalink
test(identity): adds tests and eslint to identity
Browse files Browse the repository at this point in the history
  • Loading branch information
gdixon authored and shavinac committed Apr 7, 2022
1 parent 1189f02 commit b1ad414
Show file tree
Hide file tree
Showing 38 changed files with 519 additions and 321 deletions.
6 changes: 3 additions & 3 deletions iam/.eslintignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*.js
/*.ts
/test/**/*.js
/dist/*
/coverage/*
**/__mocks__/**/*.js
**/__mocks__/**/*.ts
/node_modules/*
**/__mocks__/**/*
**/__tests__/**/*
2 changes: 1 addition & 1 deletion iam/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = {
"no-console": "warn",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["warn", { vars: "all", args: "after-used", ignoreRestSiblings: false }],
'@typescript-eslint/explicit-function-return-type': 'warn', // Consider using explicit annotations for object literals and function return types even when they can be inferred.
"@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred.
"no-empty": "warn",
"@typescript-eslint/no-misused-promises": 1,
"@typescript-eslint/no-floating-promises": 1,
Expand Down
25 changes: 23 additions & 2 deletions iam/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
/dist
/coverage
node_modules
.env
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
7 changes: 1 addition & 6 deletions iam/.prettierignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
/*.js
/*.ts
/test/**/*.js
/dist/*
/coverage/*
**/__mocks__/**/*.js
**/__mocks__/**/*.ts
/coverage/*
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import request from "supertest";

// ---- Test subject
import { app, config } from "../index";
import { app, config } from "../src/index";

// ---- Types
import { ErrorResponseBody, ValidResponseBody } from "@dpopp/types";
Expand Down
3 changes: 2 additions & 1 deletion iam/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
"private": true,
"version": "0.0.1",
"main": "dist/main.js",
"types": "dist/types.d.ts",
"license": "MIT",
"scripts": {
"clean": "rimraf dist node_modules",
"prebuild": "yarn run lint",
"build": "tsc",
"prestart": "yarn run build",
"start": "node .",
"test": "jest",
"test": "jest --verbose",
"prettier": "prettier --write .",
"lint": "tsc --noEmit && eslint --ext .ts,.js,.tsx ."
},
Expand Down
102 changes: 51 additions & 51 deletions iam/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import { utils } from "ethers";
// ---- Types
import { Response } from "express";
import {
Payload,
VerificationRecord,
ChallengeRecord,
RequestPayload,
ProofRecord,
ChallengeRequestBody,
VerifyRequestBody,
CredentialResponseBody,
Expand Down Expand Up @@ -68,37 +67,38 @@ app.post("/api/v0.0.0/challenge", (req: Request, res: Response): void => {
// get the payload from the JSON req body
const requestBody: ChallengeRequestBody = req.body as ChallengeRequestBody;
// console.log("requestBody", requestBody);
const payload: Payload = requestBody.payload;
// ensure address is checksummed
payload.address = utils.getAddress(payload.address);
const payload: RequestPayload = requestBody.payload;
// check for a valid payload
if (payload.address && payload.type) {
// check if the payload is valid against one of the providers
// ensure address is checksummed
payload.address = utils.getAddress(payload.address);
// generate a challange for the given payload
const challenge = providers.getChallenge(payload);
// check if the request was valid against Identity Providers
// if the request is valid then proceed to generate a challenge credential
if (challenge && challenge.valid === true) {
// add additional fields to the record object so that we definitely produce a valid merkleTree
const record = {
// recreate the record to ensure the minimun number of leafs are present to produce a valid merkleTree
const record: RequestPayload = {
// add fields to identify the bearer of the challenge
type: payload.type,
address: payload.address,
// version as defined by entry point
version: "0.0.0",
// extend/overwrite with record returned from the provider
...(challenge?.record || {}),
} as ChallengeRecord;
};

// generate a VC for the given payload
return void issueChallengeCredential(DIDKit, key, record).then((credential) => {
// check error state and run safety check to ensure we're returning a valid VC
if (credential.error) {
// return error msg indicating a failure producing VC
return errorRes(res, "Unable to produce a verifiable credential");
}

// return the verifiable credential
return res.json(credential as CredentialResponseBody);
});
return void issueChallengeCredential(DIDKit, key, record)
.then((credential) => {
// return the verifiable credential
return res.json(credential as CredentialResponseBody);
})
.catch((error) => {
if (error) {
// return error msg indicating a failure producing VC
return errorRes(res, "Unable to produce a verifiable credential");
}
});
} else {
// return error message if an error present
return void errorRes(res, (challenge.error && challenge.error.join(", ")) || "Unable to verify proofs");
Expand All @@ -112,58 +112,58 @@ app.post("/api/v0.0.0/challenge", (req: Request, res: Response): void => {
// expose verify entry point
app.post("/api/v0.0.0/verify", (req: Request, res: Response): void => {
const requestBody: VerifyRequestBody = req.body as VerifyRequestBody;
// each verify request should be received with a challenge credential detailing a signature contained in the Payload.proofs
// each verify request should be received with a challenge credential detailing a signature contained in the RequestPayload.proofs
const challenge = requestBody.challenge;
// get the payload from the JSON req body
const payload = requestBody.payload;

// Check the challenge and the payload is valid before issueing a credential from a registered provider
return void verifyCredential(DIDKit, challenge).then((verified) => {
if (verified && issuer === challenge.issuer) {
// pull the address and checksum
// pull the address and checksum so that its stored in a predicatable format
const address = utils.getAddress(
utils.verifyMessage(challenge.credentialSubject.challenge, payload.proofs.signature)
);
// if the signer matches...
const isSigner = challenge.credentialSubject.id === `did:ethr:${address}#challenge-${payload.type}`;
// ensure the only address we save is that of the signer
payload.address = address;
// check for a valid payload
// the signer should be the address outlined in the challenge credential - rebuild the id to check for a full match
const isSigner = challenge.credentialSubject.id === `did:ethr:${address}#challenge-${payload.type}`;
// type is required because we need it to select the correct Identity Provider
if (isSigner && payload && payload.type) {
// check if the payload is valid against one of the providers
const verifiedChallenge = providers.verify(payload);
// check if the request was valid against Identity Providers
if (verifiedChallenge && verifiedChallenge?.valid === true) {
// add additional fields to the record object so that we definitely produce a valid merkleTree
const record = {
// add enough fields to ensure the merkleTree is always valid
// each provider will apply buisness logic to the payload inorder to set the `valid` bool on the returned VerifiedPayload
const verifiedPayload = providers.verify(payload);
// check if the request is valid against the selected Identity Provider
if (verifiedPayload && verifiedPayload?.valid === true) {
// recreate the record to ensure the minimun number of leafs are present to produce a valid merkleTree
const record: ProofRecord = {
// type and address will always be known and can be obtained from the resultant credential
type: payload.type,
address: payload.address,
// version as defined by entry point
// version is defined by entry point
version: "0.0.0",
// extend/overwrite with record returned from the provider
...(verifiedChallenge?.record || {}),
} as VerificationRecord;
...(verifiedPayload?.record || {}),
};

// generate a VC for the given payload
return issueMerkleCredential(DIDKit, key, record).then(({ credential, record }) => {
// check error state and run safety check to ensure we're returning a valid VC
if (Object.hasOwnProperty.call(credential, "error")) {
// return error msg indicating a failure producing VC
return errorRes(res, "Unable to produce a verifiable credential");
}

// return the verifiable credential
return res.json({
record,
credential,
} as CredentialResponseBody);
});
return issueMerkleCredential(DIDKit, key, record)
.then(({ credential }) => {
return res.json({
record,
credential,
} as CredentialResponseBody);
})
.catch((error) => {
if (error) {
// return error msg indicating a failure producing VC
return errorRes(res, "Unable to produce a verifiable credential");
}
});
} else {
// return error message if an error present
// return error message if an error is present
return errorRes(
res,
(verifiedChallenge.error && verifiedChallenge.error.join(", ")) || "Unable to verify proofs"
(verifiedPayload.error && verifiedPayload.error.join(", ")) || "Unable to verify proofs"
);
}
}
Expand Down
21 changes: 8 additions & 13 deletions iam/src/providers/simple.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
// ----- Types
import { Payload, Verification } from "@dpopp/types";

// ---- Base Provider
import { Provider } from "../utils/provider";
import { Provider, ProviderOptions } from "../types";
import { RequestPayload, VerifiedPayload } from "@dpopp/types";

// Export a simple Provider as an example
export class SimpleProvider extends Provider {
export class SimpleProvider implements Provider {
// Give the provider a type so that we can select it with a payload
_type = "Simple";
type = "Simple";
// Options can be set here and/or via the constructor
_options = {
valid: "true",
};

// construct this and its super
constructor(options: { [prop: string]: { [prop: string]: string | undefined } | string } = {}) {
// construct Provider
super();
// set options against this instance
this.setOptions(options);
// construct the provider instance with supplied options
constructor(options: ProviderOptions = {}) {
this._options = { ...this._options, ...options };
}

// verify that the proof object contains valid === "true"
verify(payload: Payload): Verification {
verify(payload: RequestPayload): VerifiedPayload {
return {
valid: payload?.proofs?.valid === this._options.valid,
record: {
Expand Down
10 changes: 10 additions & 0 deletions iam/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RequestPayload, VerifiedPayload } from "@dpopp/types";

// All Identity Providers should implement Provider
export interface Provider {
type: string;
verify: (payload: RequestPayload) => VerifiedPayload;
}

// Use unknown
export type ProviderOptions = Record<string, unknown>;
29 changes: 0 additions & 29 deletions iam/src/utils/provider.ts

This file was deleted.

18 changes: 8 additions & 10 deletions iam/src/utils/providers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// ---- Base Provider instance that all Providers will inherit
import { Provider } from "./provider";

// ---- Types
import { Payload, Challenge, Verification } from "@dpopp/types";
import { Provider } from "../types";
import { RequestPayload, ChallengePayload, VerifiedPayload } from "@dpopp/types";

// ---- Return randomBytes as a challenge
// ---- Return randomBytes as a challenge to test that the user has control of a provided address
import crypto from "crypto";

// Collate all Providers to abstract verify logic
Expand All @@ -16,16 +14,16 @@ export class Providers {
constructor(_providers: Provider[]) {
// reduce unique entries into _providers object
this._providers = _providers.reduce((providers, provider) => {
if (!providers[provider._type]) {
providers[provider._type] = provider;
if (!providers[provider.type]) {
providers[provider.type] = provider;
}

return providers;
}, {} as { [k: string]: Provider });
}

// request a challenge sig
getChallenge(payload: Payload): Challenge {
getChallenge(payload: RequestPayload): ChallengePayload {
// @TODO - expand this to allow providers to set custom challanges?

// check that we've been provided an address for the challenge
Expand All @@ -34,9 +32,9 @@ export class Providers {
return {
valid: true,
record: {
challenge: crypto.randomBytes(32).toString("hex"),
address: payload.address,
type: payload.type,
challenge: crypto.randomBytes(32).toString("hex"),
},
};
} else {
Expand All @@ -49,7 +47,7 @@ export class Providers {
}

// Given the payload is valid return the response of the selected Providers verification proceedure
verify(payload: Payload): Verification {
verify(payload: RequestPayload): VerifiedPayload {
// collect provider from options
const provider = this._providers[payload.type];

Expand Down
9 changes: 9 additions & 0 deletions identity/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*.js
/*.ts
/dist/*
/coverage/*
/node_modules/*
/src/didkit-node/*
/src/didkit-browser/*
**/__mocks__/**/*
**/__tests__/**/*
Loading

0 comments on commit b1ad414

Please sign in to comment.