Skip to content

Commit

Permalink
feat(app,iam): using did session to sign challenges (passportxyz#1936)
Browse files Browse the repository at this point in the history
* feat(app,iam): using did session to sign challenges

* feat(iam): fall back to old challenge signature when necessary
  • Loading branch information
lucianHymer authored Nov 27, 2023
1 parent 7261a02 commit 74189b6
Show file tree
Hide file tree
Showing 46 changed files with 1,785 additions and 363 deletions.
1 change: 1 addition & 0 deletions app/__test-fixtures__/contextTestHelpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ const datastoreConnectionContext = {
disconnect: jest.fn(),
dbAccessToken: "token",
dbAccessTokenStatus: "idle" as DbAuthTokenStatus,
did: jest.fn() as any,
};

export const renderWithContext = (
Expand Down
5 changes: 3 additions & 2 deletions app/__tests__/components/GenericPlatform.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { Ens } = platforms;
import { CeramicContextState } from "../../context/ceramicContext";
import { mockAddress } from "../../__test-fixtures__/onboardHookValues";
import { UN_SUCCESSFUL_ENS_RESULT, SUCCESFUL_ENS_RESULTS } from "../../__test-fixtures__/verifiableCredentialResults";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";
import { makeTestCeramicContext, renderWithContext } from "../../__test-fixtures__/contextTestHelpers";
import { JsonRpcSigner } from "@ethersproject/providers";
import { mock } from "jest-mock-extended";
Expand All @@ -24,11 +24,12 @@ jest.mock("@didtools/cacao", () => ({
},
}));

jest.mock("@gitcoin/passport-identity/dist/commonjs/src/credentials", () => ({
jest.mock("@gitcoin/passport-identity", () => ({
fetchVerifiableCredential: jest.fn(),
}));

jest.mock("../../utils/helpers.tsx", () => ({
createSignedPayload: jest.fn(),
generateUID: jest.fn(),
getProviderSpec: jest.fn(),
difference: (setA: any, setB: any) => ({
Expand Down
4 changes: 2 additions & 2 deletions app/__tests__/components/ProviderCards/BrightidCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { mockAddress, mockWallet } from "../../../__test-fixtures__/onboardHookV
import { STAMP_PROVIDERS } from "../../../config/providers";
import { brightidStampFixture } from "../../../__test-fixtures__/databaseStorageFixtures";
import { SUCCESFUL_BRIGHTID_RESULT } from "../../../__test-fixtures__/verifiableCredentialResults";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";
import { mock } from "jest-mock-extended";
import { JsonRpcSigner } from "@ethersproject/providers";
import { makeTestCeramicContext, renderWithContext } from "../../../__test-fixtures__/contextTestHelpers";
import { CeramicContextState } from "../../../context/ceramicContext";

jest.mock("@gitcoin/passport-identity/dist/commonjs/src/credentials", () => ({
jest.mock("@gitcoin/passport-identity", () => ({
fetchVerifiableCredential: jest.fn(),
}));

Expand Down
4 changes: 2 additions & 2 deletions app/__tests__/components/ProviderCards/EnsCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { mockAddress } from "../../../__test-fixtures__/onboardHookValues";
import { STAMP_PROVIDERS } from "../../../config/providers";
import { ensStampFixture } from "../../../__test-fixtures__/databaseStorageFixtures";
import { SUCCESFUL_ENS_RESULT } from "../../../__test-fixtures__/verifiableCredentialResults";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";
import { makeTestCeramicContext, renderWithContext } from "../../../__test-fixtures__/contextTestHelpers";
import { JsonRpcSigner } from "@ethersproject/providers";
import { mock } from "jest-mock-extended";

jest.mock("@gitcoin/passport-identity/dist/commonjs/src/credentials", () => ({
jest.mock("@gitcoin/passport-identity", () => ({
fetchVerifiableCredential: jest.fn(),
}));

Expand Down
4 changes: 2 additions & 2 deletions app/__tests__/components/ProviderCards/PohCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { mockAddress, mockWallet } from "../../../__test-fixtures__/onboardHookV
import { STAMP_PROVIDERS } from "../../../config/providers";
import { pohStampFixture } from "../../../__test-fixtures__/databaseStorageFixtures";
import { SUCCESFUL_POH_RESULT } from "../../../__test-fixtures__/verifiableCredentialResults";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";
import { mock } from "jest-mock-extended";
import { JsonRpcSigner } from "@ethersproject/providers";
import { makeTestCeramicContext, renderWithContext } from "../../../__test-fixtures__/contextTestHelpers";
import { CeramicContextState } from "../../../context/ceramicContext";

jest.mock("@gitcoin/passport-identity/dist/commonjs/src/credentials", () => ({
jest.mock("@gitcoin/passport-identity", () => ({
fetchVerifiableCredential: jest.fn(),
}));

Expand Down
9 changes: 3 additions & 6 deletions app/__tests__/components/RefreshMyStampsModalContent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { makeTestCeramicContext, renderWithContext } from "../../__test-fixtures
import { useNavigate } from "react-router-dom";
import { ValidatedPlatform } from "../../signer/utils";

import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";
import { reduceStampResponse } from "../../utils/helpers";
import { CredentialResponseBody } from "@gitcoin/passport-types";
import { IAM_SIGNATURE_TYPE } from "../../config/stamp_config";
Expand All @@ -15,7 +15,7 @@ jest.mock("react-router-dom", () => ({
useNavigate: jest.fn(),
}));

jest.mock("@gitcoin/passport-identity/dist/commonjs/src/credentials", () => ({
jest.mock("@gitcoin/passport-identity", () => ({
fetchVerifiableCredential: jest.fn().mockResolvedValue({ credentials: [] } as unknown as CredentialResponseBody),
}));

Expand Down Expand Up @@ -43,11 +43,8 @@ const mockWalletState = {
address: "0xmyAddress",
};

const mockSigner = jest.fn();

jest.mock("../../context/walletStore", () => ({
useWalletStore: (callback: (state: any) => any) => callback(mockWalletState),
useSigner: () => mockSigner,
}));

const validPlatforms: ValidatedPlatform[] = [
Expand Down Expand Up @@ -117,7 +114,7 @@ describe("RefreshMyStampsModalContent", () => {
proofs: {},
signatureType: IAM_SIGNATURE_TYPE,
},
mockSigner
expect.any(Function)
)
);
});
Expand Down
26 changes: 0 additions & 26 deletions app/__tests__/context/datastoreConnectionContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,32 +107,6 @@ describe("<UserContext>", () => {
localStorage.setItem("connectedWallets", "[]");
});

it("should delete localStorage item if session has expired", async () => {
const ceramicConnect = jest.fn().mockResolvedValueOnce({
client: {
session: {
isExpired: true,
expireInSecs: 3400,
},
},
});
(framework.useViewerConnection as jest.Mock).mockReturnValue([
{ status: "connecting", selfID: { did: "did:test" } },
ceramicConnect,
jest.fn(),
]);

localStorage.setItem("didsession-0xmyAddress", "eyJzZXNzaW9uS2V5U2VlZCI6IlF5cTN4aW9ubGxD...");

renderTestComponent();

expect(screen.getByTestId("session-id")).toHaveTextContent("eyJzZXNzaW9uS2V5U2VlZCI6IlF5cTN4aW9ubGxD...");

screen.getByRole("button").click();

await waitFor(() => expect(screen.getByTestId("session-id").textContent).toBe(""));
});

describe("when using multichain", () => {
beforeEach(async () => {
const ceramicConnect = jest.fn().mockResolvedValueOnce({
Expand Down
45 changes: 33 additions & 12 deletions app/__tests__/context/stampClaimingContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import { makeTestCeramicContext } from "../../__test-fixtures__/contextTestHelpe

import { CeramicContext } from "../../context/ceramicContext";
import { StampClaimingContext, StampClaimingContextProvider } from "../../context/stampClaimingContext";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";

import { PLATFORM_ID } from "@gitcoin/passport-types";
import { PlatformProps } from "../../components/GenericPlatform";
import { AppContext, PlatformClass } from "@gitcoin/passport-platforms";
import { DatastoreConnectionContext, DbAuthTokenStatus } from "../../context/datastoreConnectionContext";

jest.mock("../../utils/helpers", () => ({
generateUID: jest.fn((length: number) => "some random string"),
}));

jest.mock("@gitcoin/passport-identity/dist/commonjs/src/credentials", () => ({
jest.mock("@gitcoin/passport-identity", () => ({
fetchVerifiableCredential: jest.fn(),
}));

Expand Down Expand Up @@ -118,20 +119,40 @@ const mockCeramicContext = makeTestCeramicContext({
describe("<StampClaimingContext>", () => {
const renderTestComponent = () =>
render(
<CeramicContext.Provider value={mockCeramicContext}>
<StampClaimingContextProvider>
<TestingComponent />
</StampClaimingContextProvider>
</CeramicContext.Provider>
<DatastoreConnectionContext.Provider
value={{
connect: jest.fn(),
disconnect: jest.fn(),
dbAccessToken: "token",
dbAccessTokenStatus: "idle" as DbAuthTokenStatus,
did: jest.fn() as any,
}}
>
<CeramicContext.Provider value={mockCeramicContext}>
<StampClaimingContextProvider>
<TestingComponent />
</StampClaimingContextProvider>
</CeramicContext.Provider>
</DatastoreConnectionContext.Provider>
);

const renderTestComponentWithEvmStamp = () =>
render(
<CeramicContext.Provider value={mockCeramicContext}>
<StampClaimingContextProvider>
<TestingComponentWithEvmStamp />
</StampClaimingContextProvider>
</CeramicContext.Provider>
<DatastoreConnectionContext.Provider
value={{
connect: jest.fn(),
disconnect: jest.fn(),
dbAccessToken: "token",
dbAccessTokenStatus: "idle" as DbAuthTokenStatus,
did: jest.fn() as any,
}}
>
<CeramicContext.Provider value={mockCeramicContext}>
<StampClaimingContextProvider>
<TestingComponentWithEvmStamp />
</StampClaimingContextProvider>
</CeramicContext.Provider>
</DatastoreConnectionContext.Provider>
);

beforeEach(() => {
Expand Down
45 changes: 45 additions & 0 deletions app/__tests__/utils/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createSignedPayload } from "../../utils/helpers";
import { Cacao } from "@didtools/cacao";

jest.mock("@didtools/cacao", () => ({
Cacao: {
fromBlockBytes: jest.fn().mockImplementation((_) => {
return {
p: {
iss: "did:ethr:0x123",
},
};
}),
},
}));

describe("createSignedPayload", () => {
it("should sign", async () => {
const mockDid = {
createDagJWS: () => ({
jws: {
link: {
bytes: [7, 8, 9],
},
payload: {
hello: "world",
},
signatures: ["0x123"],
},
cacaoBlock: [0, 1, 2],
}),
};

const signedPayload = await createSignedPayload(mockDid as any, { hello: "world" });

expect(Cacao.fromBlockBytes).toHaveBeenCalledWith([0, 1, 2]);

expect(signedPayload).toEqual({
signatures: ["0x123"],
payload: { hello: "world" },
cid: [7, 8, 9],
cacao: [0, 1, 2],
issuer: "did:ethr:0x123",
});
});
});
15 changes: 9 additions & 6 deletions app/components/GenericPlatform.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// --- Methods
import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
import React, { useContext, useEffect, useMemo, useState } from "react";

// --- Datadog
import { datadogLogs } from "@datadog/browser-logs";
Expand All @@ -12,7 +12,7 @@ import {
PLATFORM_ID,
StampPatch,
} from "@gitcoin/passport-types";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity/dist/commonjs/src/credentials";
import { fetchVerifiableCredential } from "@gitcoin/passport-identity";

// --- Style Components
import { SideBarContent } from "./SideBarContent";
Expand All @@ -23,7 +23,7 @@ import { JsonOutputModal } from "./JsonOutputModal";

// --- Context
import { CeramicContext } from "../context/ceramicContext";
import { useSigner, useWalletStore } from "../context/walletStore";
import { useWalletStore } from "../context/walletStore";
import { waitForRedirect } from "../context/stampClaimingContext";

// --- Types
Expand All @@ -32,10 +32,11 @@ import { PlatformClass } from "@gitcoin/passport-platforms";
import { IAM_SIGNATURE_TYPE, iamUrl } from "../config/stamp_config";

// --- Helpers
import { difference, generateUID } from "../utils/helpers";
import { createSignedPayload, difference, generateUID } from "../utils/helpers";

import { datadogRum } from "@datadog/browser-rum";
import { PlatformScoreSpec } from "../context/scorerContext";
import { useDatastoreConnectionContext } from "../context/datastoreConnectionContext";

export type PlatformProps = {
platFormGroupSpec: PlatformGroupSpec[];
Expand Down Expand Up @@ -68,13 +69,13 @@ export const GenericPlatform = ({
onClose,
}: GenericPlatformProps): JSX.Element => {
const address = useWalletStore((state) => state.address);
const signer = useSigner();
const { handlePatchStamps, verifiedProviderIds, userDid } = useContext(CeramicContext);
const [isLoading, setLoading] = useState(false);
const [canSubmit, setCanSubmit] = useState(false);
const [submitted, setSubmitted] = useState(false);
const [verificationResponse, setVerificationResponse] = useState<CredentialResponseBody[]>([]);
const [payloadModalIsOpen, setPayloadModalIsOpen] = useState(false);
const { did } = useDatastoreConnectionContext();
// const { handleFetchCredential } = useContext(StampClaimingContext);

// --- Chakra functions
Expand Down Expand Up @@ -153,6 +154,7 @@ export const GenericPlatform = ({
datadogLogs.logger.info("Saving Stamp", { platform: platform.platformId });
setLoading(true);
try {
if (!did) throw new Error("No DID found");
const state = `${platform.path}-` + generateUID(10);
const providerPayload = (await platform.getProviderPayload({
state,
Expand Down Expand Up @@ -181,7 +183,7 @@ export const GenericPlatform = ({
proofs: providerPayload,
signatureType: IAM_SIGNATURE_TYPE,
},
signer as { signMessage: (message: string) => Promise<string> }
(data: any) => createSignedPayload(did, data)
);

const verifiedCredentials =
Expand Down Expand Up @@ -246,6 +248,7 @@ export const GenericPlatform = ({

setLoading(false);
} catch (e) {
console.error(e);
datadogLogs.logger.error("Verification Error", { error: e, platform: platform.platformId });
doneToast(
"Verification Failed",
Expand Down
Loading

0 comments on commit 74189b6

Please sign in to comment.