Skip to content

Commit

Permalink
fix(api): fixed casing of additional address (passportxyz#1498)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianHymer authored Jul 20, 2023
1 parent 672133f commit c6fdbd0
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 4 deletions.
136 changes: 136 additions & 0 deletions iam/__tests__/additional_signer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// ---- Testing libraries
import request from "supertest";
import * as DIDKit from "@spruceid/didkit-wasm-node";

// --- Mocks - test configuration

process.env.IAM_JWK = DIDKit.generateEd25519Key();
process.env.ATTESTATION_SIGNER_PRIVATE_KEY = "0x04d16281ff3bf268b29cdd684183f72542757d24ae9fdfb863e7c755e599163a";
process.env.GITCOIN_VERIFIER_CHAIN_ID = "11155111";
process.env.GITCOIN_VERIFIER_CONTRACT_ADDRESS = "0xD8088f772006CAFD81082e8e2e467fA18564e879";
process.env.ALLO_SCORER_ID = "1";
process.env.SCORER_ENDPOINT = "http://127.0.0.1:8002";
process.env.SCORER_API_KEY = "abcd";
process.env.EAS_GITCOIN_STAMP_SCHEMA = "0x";
process.env.EAS_GITCOIN_SCORE_SCHEMA = "0x";
process.env.EAS_GITCOIN_PASSPORT_SCHEMA = "0x";

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

import * as identityMock from "@gitcoin/passport-identity/dist/commonjs/src/credentials";

jest.mock("ethers", () => {
const originalModule = jest.requireActual("ethers");
const ethers = originalModule.ethers;
const utils = originalModule.utils;

return {
utils: {
...utils,
getAddress: jest
.fn()
.mockImplementationOnce(() => {
return "0x1";
})
.mockImplementationOnce(() => {
return "0xAbC";
})
.mockImplementationOnce(() => {
return "0xAbC";
}),
verifyMessage: jest.fn().mockImplementation(() => {
return "string";
}),
splitSignature: jest.fn().mockImplementation(() => {
return { v: 0, r: "r", s: "s" };
}),
},
ethers,
};
});

describe("POST /verify", function () {
it("produces the same hash for an additional signer as if the passport is used directly", async () => {
const challengeForReqWithAdditionalSigner = {
issuer: config.issuer,
credentialSubject: {
id: "did:pkh:eip155:1:0x1",
provider: "challenge-any",
address: "0x1",
challenge: {
issuer: "did:key:z6Mkecq4nKTCniqNed5cdDSURj1JX4SEdNhvhitZ48HcJMnN",
},
},
};

// payload containing a signature of the challenge in the challenge credential
const payloadWithAdditionalSigner = {
type: "any",
types: ["SimpleEvm"],
address: "0x1",
proofs: {
valid: "true",
username: "test",
signature: "pass",
},
signer: {
address: "0xabc",
signature: "0x123456",
challenge: {
issuer: "did:key:z6Mkecq4nKTCniqNed5cdDSURj1JX4SEdNhvhitZ48HcJMnN",
credentialSubject: {
challenge: "I commit that this wallet is under my control",
},
},
},
};

const challengeForReqWithoutAdditionalSigner = {
issuer: config.issuer,
credentialSubject: {
id: "did:pkh:eip155:1:0xAbC",
provider: "challenge-any",
address: "0xabc",
challenge: {
issuer: "did:key:z6Mkecq4nKTCniqNed5cdDSURj1JX4SEdNhvhitZ48HcJMnN",
},
},
};

// payload containing a signature of the challenge in the challenge credential
const payloadWithoutAdditionalSigner = {
type: "any",
types: ["SimpleEvm"],
address: "0xabc",
proofs: {
valid: "true",
username: "test",
signature: "pass",
},
};

// resolve the verification
jest.spyOn(identityMock, "verifyCredential").mockResolvedValue(true);

const res = await request(app)
.post("/api/v0.0.0/verify")
.send({ challenge: challengeForReqWithAdditionalSigner, payload: payloadWithAdditionalSigner })
.set("Accept", "application/json")
.expect(200)
.expect("Content-Type", /json/);

const hashWithAdditionalSigner = res.body[0].credential.credentialSubject.hash;

const res2 = await request(app)
.post("/api/v0.0.0/verify")
.send({ challenge: challengeForReqWithoutAdditionalSigner, payload: payloadWithoutAdditionalSigner })
.set("Accept", "application/json")
.expect(200)
.expect("Content-Type", /json/);

const hashWithoutAdditionalSigner = res2.body[0].credential.credentialSubject.hash;

expect(hashWithAdditionalSigner).toEqual(hashWithoutAdditionalSigner);
});
});
11 changes: 7 additions & 4 deletions iam/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,14 +362,17 @@ app.post("/api/v0.0.0/verify", (req: Request, res: Response): void => {

const additionalSignerCredential = await verifyCredential(DIDKit, additionalChallenge);

const verifiedAddress = utils
.getAddress(utils.verifyMessage(additionalChallenge.credentialSubject.challenge, payload.signer.signature))
.toLocaleLowerCase();
// pull the address so that its stored in a predictable (checksummed) format
const verifiedAddress = utils.getAddress(
utils.verifyMessage(additionalChallenge.credentialSubject.challenge, payload.signer.signature)
);

// if verifiedAddress does not equal the additional signer address throw an error because signature is invalid
if (!additionalSignerCredential || verifiedAddress !== payload.signer.address) {
if (!additionalSignerCredential || verifiedAddress.toLowerCase() !== payload.signer.address.toLowerCase()) {
return void errorRes(res, "Unable to verify payload", 401);
}

payload.signer.address = verifiedAddress;
}

// type is required because we need it to select the correct Identity Provider
Expand Down
2 changes: 2 additions & 0 deletions platforms/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Provider Utils
import { Providers } from "./utils/providers";
import { SimpleProvider } from "./utils/simpleProvider";
import { SimpleEvmProvider } from "./utils/simpleEvmProvider";
import { ClearTextSimpleProvider } from "./utils/clearTextSimpleProvider";
import { ClearTextTwitterProvider, ClearTextGithubOrgProvider } from "./ClearText";

Expand Down Expand Up @@ -31,6 +32,7 @@ Object.values(platforms).map(({ ProviderConfig }) => {
export const providers = new Providers([
// Example provider which verifies the payload when `payload.proofs.valid === "true"`
new SimpleProvider(),
new SimpleEvmProvider(),
new ClearTextSimpleProvider(),
new ClearTextTwitterProvider(),
new ClearTextGithubOrgProvider(),
Expand Down
28 changes: 28 additions & 0 deletions platforms/src/utils/simpleEvmProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// ----- Types
import type { Provider, ProviderOptions } from "../types";
import type { RequestPayload, VerifiedPayload } from "@gitcoin/passport-types";

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

// 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: RequestPayload): Promise<VerifiedPayload> {
return Promise.resolve({
valid: payload?.proofs?.valid === this._options.valid,
record: {
address: payload.address,
},
});
}
}

0 comments on commit c6fdbd0

Please sign in to comment.