Skip to content

Commit

Permalink
fix(platforms): check that civic pass is active (passportxyz#2012)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianHymer authored Jan 8, 2024
1 parent 0672a7a commit 77707c7
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 22 deletions.
28 changes: 14 additions & 14 deletions platforms/src/Civic/Providers/civic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,29 @@ export class CivicPassProvider implements Provider {

// Verify that address defined in the payload has a civic pass
async verify(payload: RequestPayload): Promise<VerifiedPayload> {
// if a signer is provider we will use that address to verify against
const address = payload.address.toString().toLowerCase();
const now = getNowAsBigNumberSeconds();
let errors = undefined;
let record = undefined;
let errors, record, expiresInSeconds;
let valid = false;

const allPasses = await findAllPasses(address, this.includeTestnets, [this.passType]);
const activePasses = allPasses.filter(({ state }) => state === "ACTIVE");
const validPasses = activePasses.filter(({ expiry }) => expiry.gt(now));

if (allPasses.length > 0) {
const validPasses = allPasses.filter((pass) => pass.expiry.gt(now));
if (validPasses.length > 0) {
record = { address };
valid = true;
} else {
errors = [`Your ${CivicPassType[this.passType]} pass${allPasses.length > 1 ? "es are" : " is"} expired.`];
}
} else {
if (allPasses.length === 0) {
errors = [`You do not have a ${CivicPassType[this.passType]} pass.`];
} else if (activePasses.length === 0) {
errors = [
`Your ${CivicPassType[this.passType]} pass${allPasses.length > 1 ? "es are" : " is"} frozen or revoked.`,
];
} else if (validPasses.length === 0) {
errors = [`Your ${CivicPassType[this.passType]} pass${activePasses.length > 1 ? "es are" : " is"} expired.`];
} else {
record = { address };
valid = true;
expiresInSeconds = secondsFromNow(latestExpiry(validPasses));
}

const expiresInSeconds = valid ? secondsFromNow(latestExpiry(allPasses)) : undefined;

return {
valid,
errors,
Expand Down
5 changes: 4 additions & 1 deletion platforms/src/Civic/Providers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const supportedChains = [

export type SupportedChain = typeof supportedChains[number];

type CivicPassState = "ACTIVE" | "FROZEN" | "REVOKED";

export type CivicPassLookupPass = {
type: {
slotId: number;
Expand All @@ -37,14 +39,15 @@ export type CivicPassLookupPass = {
chain: string;
identifier: string;
expiry?: number;
state: "ACTIVE" | "FROZEN" | "REVOKED";
state: CivicPassState;
};
export type PassesForAddress = { passes: Record<string, CivicPassLookupPass[]> };
export type CivicPassLookupResponse = Record<string, PassesForAddress>;

type PassDetails = {
expiry?: BigNumber;
identifier: string;
state: CivicPassState;
};
export type Pass = PassDetails & {
type: CivicPassType;
Expand Down
1 change: 1 addition & 0 deletions platforms/src/Civic/Providers/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const passLookupResponseToPass =
chain: pass.chain as SupportedChain,
expiry: BigNumber.from(pass.expiry),
identifier: pass.identifier,
state: pass.state,
});

const passTypesToNames = (passTypes: CivicPassType[]): string[] => passTypes.map((id) => CivicPassType[id]);
Expand Down
37 changes: 30 additions & 7 deletions platforms/src/Civic/__tests__/civic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { RequestPayload } from "@gitcoin/passport-types";
import { CivicPassProvider } from "../Providers/civic";
import { CivicPassLookupPass, CivicPassType, PassesForAddress } from "../Providers/types";
import axios from "axios";
import { ProviderExternalVerificationError } from "../../types";

// Mock out all top level functions, such as get, put, delete and post:
jest.mock("axios");
Expand All @@ -15,19 +14,21 @@ const stubCivic = (passes: PassesForAddress["passes"]): void => {
});
};

const stubCivicError = (error: string): void => {
(axios.get as jest.Mock).mockRejectedValue(new Error(error));
};

const now = Math.floor(Date.now() / 1000);

const userAddress = "0x123";
const requestPayload = { address: userAddress } as RequestPayload;
const expirySeconds = 1000;
const dummyPass = {
const dummyPass: CivicPassLookupPass = {
chain: "ETHEREUM_MAINNET",
expiry: now + expirySeconds,
} as CivicPassLookupPass;
state: "ACTIVE",
type: {
slotId: 0,
address: userAddress,
},
identifier: "0x456",
};

describe("Civic Pass Provider", function () {
beforeEach(() => {
Expand Down Expand Up @@ -74,6 +75,28 @@ describe("Civic Pass Provider", function () {
});
});

it("should return detailed error for a revoked pass", async () => {
const revokedPass = { ...dummyPass };
revokedPass.state = "REVOKED";

stubCivic({
UNIQUENESS: [revokedPass],
});

const civic = new CivicPassProvider({
type: "uniqueness",
passType: CivicPassType.UNIQUENESS,
});

const verifiedPayload = await civic.verify(requestPayload);

expect(verifiedPayload).toMatchObject({
valid: false,
record: undefined,
errors: ["Your UNIQUENESS pass is frozen or revoked."],
});
});

it("should return valid true if a pass is found", async () => {
stubCivic({
UNIQUENESS: [dummyPass],
Expand Down

0 comments on commit 77707c7

Please sign in to comment.