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

Feat(app, iam): Implements Twitter OAuth as a Provider #87

Merged
merged 2 commits into from May 18, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/.env-example.env
@@ -1,5 +1,9 @@
# .env
NEXT_PUBLIC_DPOPP_GOOGLE_CLIENT_ID=MY-APP-ID.apps.googleusercontent.com
NEXT_PUBLIC_DPOPP_TWITTER_CALLBACK=http://localhost:3000/

NEXT_PUBLIC_DPOPP_IAM_URL=http://localhost:80/api/
NEXT_PUBLIC_DPOPP_PROCEDURE_URL=http://localhost:80/procedure/

NEXT_PUBLIC_DPOPP_INFURA_KEY=YOUR_INFURA_KEY
NEXT_PUBLIC_CERAMIC_CLIENT_URL=https://ceramic-clay.3boxlabs.com
15 changes: 15 additions & 0 deletions app/__mocks__/broadcast-channel.js
@@ -0,0 +1,15 @@
const constructor = jest.fn();
const postMessage = jest.fn();
const close = jest.fn();

export class BroadcastChannel {
constructor() {
return constructor();
}
postMessage() {
return postMessage();
}
close() {
return close();
}
}
5 changes: 5 additions & 0 deletions app/__test-fixtures__/databaseStorageFixtures.ts
Expand Up @@ -39,6 +39,11 @@ export const ensStampFixture: Stamp = {
credential,
};

export const twitterStampFixture: Stamp = {
provider: "Twitter",
credential,
};

export const passportFixture: Passport = {
issuanceDate: new Date("2022-01-01"),
expiryDate: new Date("2022-01-02"),
Expand Down
16 changes: 0 additions & 16 deletions app/__tests__/components/ProviderCards/EnsCard.test.tsx
Expand Up @@ -22,14 +22,6 @@ const mockUserContext: UserContextState = {
passport: undefined,
isLoadingPassport: false,
allProvidersState: {
Google: {
providerSpec: STAMP_PROVIDERS.Google,
stamp: undefined,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
Expand Down Expand Up @@ -67,14 +59,6 @@ describe("when user has verified with EnsProvider", () => {
value={{
...mockUserContext,
allProvidersState: {
Google: {
providerSpec: STAMP_PROVIDERS.Google,
stamp: undefined,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: ensStampFixture,
Expand Down
16 changes: 0 additions & 16 deletions app/__tests__/components/ProviderCards/GoogleCard.test.tsx
Expand Up @@ -21,14 +21,6 @@ const mockUserContext: UserContextState = {
providerSpec: STAMP_PROVIDERS.Google,
stamp: undefined,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
},
handleAddStamp: handleAddStamp,
handleCreatePassport: mockCreatePassport,
Expand Down Expand Up @@ -66,14 +58,6 @@ describe("when user has verified with GoogleProvider", () => {
providerSpec: STAMP_PROVIDERS.Google,
stamp: googleStampFixture,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
},
}}
>
Expand Down
16 changes: 0 additions & 16 deletions app/__tests__/components/ProviderCards/SimpleCard.test.tsx
Expand Up @@ -17,18 +17,10 @@ const mockUserContext: UserContextState = {
passport: undefined,
isLoadingPassport: false,
allProvidersState: {
Google: {
providerSpec: STAMP_PROVIDERS.Google,
stamp: undefined,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
},
handleAddStamp: handleAddStamp,
handleCreatePassport: mockCreatePassport,
Expand Down Expand Up @@ -62,18 +54,10 @@ describe("when user has verified with SimpleProvider", () => {
value={{
...mockUserContext,
allProvidersState: {
Google: {
providerSpec: STAMP_PROVIDERS.Google,
stamp: undefined,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: simpleStampFixture,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
},
}}
>
Expand Down
72 changes: 72 additions & 0 deletions app/__tests__/components/ProviderCards/TwitterCard.test.tsx
@@ -0,0 +1,72 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import { TwitterCard } from "../../../components/ProviderCards";

import { UserContext, UserContextState } from "../../../context/userContext";
import { mockAddress, mockWallet } from "../../../__test-fixtures__/onboardHookValues";
import { STAMP_PROVIDERS } from "../../../config/providers";
import { twitterStampFixture } from "../../../__test-fixtures__/databaseStorageFixtures";

jest.mock("../../../utils/onboard.ts");

const mockHandleConnection = jest.fn();
const mockCreatePassport = jest.fn();
const handleAddStamp = jest.fn();
const mockUserContext: UserContextState = {
loggedIn: true,
passport: undefined,
isLoadingPassport: false,
allProvidersState: {
Twitter: {
providerSpec: STAMP_PROVIDERS.Twitter,
stamp: undefined,
},
},
handleAddStamp: handleAddStamp,
handleCreatePassport: mockCreatePassport,
handleConnection: mockHandleConnection,
address: mockAddress,
wallet: mockWallet,
signer: undefined,
walletLabel: mockWallet.label,
};

describe("when user has not verfied with TwitterProvider", () => {
it("should display a twitter verification button", () => {
render(
<UserContext.Provider value={mockUserContext}>
<TwitterCard />
</UserContext.Provider>
);

const verifyTwitterButton = screen.queryByRole("button", {
name: /Verify/,
});

expect(verifyTwitterButton).toBeInTheDocument();
});
});

describe("when user has verified with TwitterProvider", () => {
it("should display that twitter is verified", () => {
render(
<UserContext.Provider
value={{
...mockUserContext,
allProvidersState: {
Twitter: {
providerSpec: STAMP_PROVIDERS.Twitter,
stamp: twitterStampFixture,
},
},
}}
>
<TwitterCard />
</UserContext.Provider>
);

const twitterVerified = screen.queryByText(/Verified/);

expect(twitterVerified).toBeInTheDocument();
});
});
4 changes: 4 additions & 0 deletions app/__tests__/pages/Dashboard.test.tsx
Expand Up @@ -29,6 +29,10 @@ const mockUserContext: UserContextState = {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
Twitter: {
providerSpec: STAMP_PROVIDERS.Twitter,
stamp: undefined,
},
},
handleAddStamp: handleAddStamp,
handleCreatePassport: mockCreatePassport,
Expand Down
8 changes: 8 additions & 0 deletions app/__tests__/pages/Home.test.tsx
Expand Up @@ -24,6 +24,14 @@ const mockUserContext: UserContextState = {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
Twitter: {
providerSpec: STAMP_PROVIDERS.Twitter,
stamp: undefined,
},
},
handleAddStamp: handleAddStamp,
handleCreatePassport: mockCreatePassport,
Expand Down
78 changes: 78 additions & 0 deletions app/__tests__/pages/index.test.tsx
@@ -0,0 +1,78 @@
import { render } from "@testing-library/react";
import { STAMP_PROVIDERS } from "../../config/providers";
import { UserContext, UserContextState } from "../../context/userContext";
import Index from "../../pages/index";

jest.mock("../../utils/onboard.ts");

const mockHandleConnection = jest.fn();
const mockCreatePassport = jest.fn();
const handleAddStamp = jest.fn();
const mockUserContext: UserContextState = {
loggedIn: false,
passport: undefined,
isLoadingPassport: false,
allProvidersState: {
Google: {
providerSpec: STAMP_PROVIDERS.Google,
stamp: undefined,
},
Simple: {
providerSpec: STAMP_PROVIDERS.Simple,
stamp: undefined,
},
Ens: {
providerSpec: STAMP_PROVIDERS.Ens,
stamp: undefined,
},
Twitter: {
providerSpec: STAMP_PROVIDERS.Twitter,
stamp: undefined,
},
},
handleAddStamp: handleAddStamp,
handleCreatePassport: mockCreatePassport,
handleConnection: mockHandleConnection,
address: undefined,
wallet: null,
signer: undefined,
walletLabel: undefined,
};

const broadcastChannel = jest.spyOn(require("broadcast-channel"), "BroadcastChannel");

describe("when index is provided queryParams matching twitters OAuth response", () => {
it("should postMessage to opener and close window", async () => {
const mockPostMessage = jest.fn();
const mockCloseWindow = jest.fn();

// Mock query params
Object.defineProperty(window, "location", {
writable: false,
value: {
search: "?code=ABC&state=twitter-123",
},
});

// Mock BroadcastChannel
broadcastChannel.mockImplementation(() => ({
postMessage: mockPostMessage,
}));

// Mock window.close
Object.defineProperty(window, "close", {
writable: false,
value: mockCloseWindow,
});

render(
<UserContext.Provider value={mockUserContext}>
<Index />
</UserContext.Provider>
);

// expect message to be posted and window.close() to have been called)
expect(mockPostMessage).toBeCalledTimes(1);
expect(mockCloseWindow).toBeCalledTimes(1);
});
});
3 changes: 2 additions & 1 deletion app/components/CardList.tsx
Expand Up @@ -2,7 +2,7 @@
import React from "react";

// --- Identity Providers
import { GoogleCard, SimpleCard, EnsCard } from "./ProviderCards";
import { GoogleCard, SimpleCard, EnsCard, TwitterCard } from "./ProviderCards";

export const CardList = (): JSX.Element => {
return (
Expand All @@ -12,6 +12,7 @@ export const CardList = (): JSX.Element => {
<SimpleCard />
<GoogleCard />
<EnsCard />
<TwitterCard />
</div>
</div>
</section>
Expand Down
3 changes: 2 additions & 1 deletion app/components/ProviderCards/EnsCard.tsx
Expand Up @@ -13,6 +13,7 @@ import { VerifyModal } from "../VerifyModal";
import { useDisclosure } from "@chakra-ui/react";

import { PROVIDER_ID, Stamp } from "@dpopp/types";
import { ProviderSpec } from "../../config/providers";

const iamUrl = process.env.NEXT_PUBLIC_DPOPP_IAM_URL || "";

Expand Down Expand Up @@ -91,7 +92,7 @@ export default function EnsCard(): JSX.Element {

return (
<Card
providerSpec={allProvidersState[providerId].providerSpec}
providerSpec={allProvidersState[providerId].providerSpec as ProviderSpec}
verifiableCredential={allProvidersState[providerId].stamp?.credential}
issueCredentialWidget={issueCredentialWidget}
/>
Expand Down
3 changes: 2 additions & 1 deletion app/components/ProviderCards/GoogleCard.tsx
Expand Up @@ -13,6 +13,7 @@ import { UserContext } from "../../context/userContext";
import { Card } from "../Card";

import { PROVIDER_ID } from "@dpopp/types";
import { ProviderSpec } from "../../config/providers";

// import from .env
const iamUrl = process.env.NEXT_PUBLIC_DPOPP_IAM_URL || "";
Expand Down Expand Up @@ -50,7 +51,7 @@ export default function GoogleCard(): JSX.Element {

return (
<Card
providerSpec={allProvidersState[providerId].providerSpec}
providerSpec={allProvidersState[providerId].providerSpec as ProviderSpec}
verifiableCredential={allProvidersState[providerId].stamp?.credential}
issueCredentialWidget={
<GoogleLogin
Expand Down
3 changes: 2 additions & 1 deletion app/components/ProviderCards/SimpleCard.tsx
Expand Up @@ -9,6 +9,7 @@ import { fetchVerifiableCredential } from "@dpopp/identity/dist/commonjs";
import { UserContext } from "../../context/userContext";

import { Card } from "../Card";
import { ProviderSpec } from "../../config/providers";

const iamUrl = process.env.NEXT_PUBLIC_DPOPP_IAM_URL || "";

Expand Down Expand Up @@ -50,7 +51,7 @@ export default function SimpleCard(): JSX.Element {

return (
<Card
providerSpec={allProvidersState[providerId].providerSpec}
providerSpec={allProvidersState[providerId].providerSpec as ProviderSpec}
verifiableCredential={allProvidersState[providerId].stamp?.credential}
issueCredentialWidget={issueCredentialWidget}
/>
Expand Down