Skip to content

Commit

Permalink
changes requested
Browse files Browse the repository at this point in the history
  • Loading branch information
moe-dev committed Dec 5, 2024
1 parent ed9a791 commit 74f5ba7
Show file tree
Hide file tree
Showing 17 changed files with 142 additions and 108 deletions.
4 changes: 3 additions & 1 deletion examples/react-components/.env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ TURNKEY_API_PRIVATE_KEY="<Turnkey API Private Key>"
NEXT_PUBLIC_BASE_URL="https://api.turnkey.com"
NEXT_PUBLIC_ORGANIZATION_ID="<Turnkey organization ID>"
NEXT_PUBLIC_AUTH_IFRAME_URL="https://auth.turnkey.com"
NEXT_PUBLIC_EXPORT_IFRAME_URL="https://export.turnkey.com"
NEXT_PUBLIC_IMPORT_IFRAME_URL="https://import.turnkey.com"
NEXT_PUBLIC_GOOGLE_CLIENT_ID="<Google OIDC client ID>"
NEXT_PUBLIC_APPLE_CLIENT_ID="<Apple OIDC client ID>"
NEXT_PUBLIC_FACEBOOK_CLIENT_ID="<Facebook OIDC client ID>"
NEXT_PUBLIC_OAUTH_REDIRECT_URI="http://localhost:3000/" # Where your login page is - make sure to have a trailing "/" NOTE: Sign in with Apple and Google does not support localhost redirects! You can use ngrok to test locally
NEXT_PUBLIC_OAUTH_REDIRECT_URI="http://localhost:3000/" # Where your login page is - make sure to have a trailing "/" NOTE: Sign in with Apple and Google does not support localhost redirects! You can use ngrok to test locally
8 changes: 5 additions & 3 deletions examples/react-components/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Example: `react-components`

This example shows a an example app created using our react components from @turnkey/sdk-react. For more information [check out our documentation](https://docs.turnkey.com/features/TODO).
This example shows an example app created using our react components from @turnkey/sdk-react. For more information [check out our documentation](https://docs.turnkey.com/features/TODO). #TODO docs for react components and is also hosted [here](TODO) #TODO add hosted URL - e.g demo.turnkey.com

## Getting started

### 1/ Cloning the example

Make sure you have `node` installed locally; we recommend using Node v18+. You will also need NextJs v13+ (for use-server/use-client directives and /app directory structure). Our components leverage use-server to make server side calls using private api keys without requiring developers to setup their own backend for turnkey authentication
Make sure you have `node` installed locally; we recommend using Node v18+. You will also need NextJS v13+ (for use-server/use-client directives and /app directory structure). Our components leverage use-server to make server side calls using private API keys without requiring developers to setup their own backend for Turnkey authentication

```bash
$ git clone https://github.com/tkhq/sdk
Expand Down Expand Up @@ -40,6 +40,8 @@ Now open `.env.local` and add the missing environment variables:
- `NEXT_PUBLIC_FACEBOOK_CLIENT_ID`
- `NEXT_PUBLIC_APPLE_CLIENT_ID`
- `NEXT_PUBLIC_AUTH_IFRAME_URL`
- `NEXT_PUBLIC_IMPORT_IFRAME_URL`
- `NEXT_PUBLIC_EXPORT_IFRAME_URL`
- `NEXT_PUBLIC_OAUTH_REDIRECT_URI`

### 3/ Running the app
Expand All @@ -48,4 +50,4 @@ Now open `.env.local` and add the missing environment variables:
$ pnpm run dev
```

This command will run a NextJS app on port 3000. If you navigate to http://localhost:3000 in your browser, you can follow the prompts to start an oauth activity.
This command will run a NextJS app on port 3000. If you navigate to http://localhost:3000 in your browser, the example app using auth components should be ready to use!
7 changes: 5 additions & 2 deletions examples/react-components/src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import {
} from "@mui/material";
import LogoutIcon from "@mui/icons-material/Logout";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { verifyEthSignature, verifySolSignatureWithAddress } from "../utils";
import {
verifyEthSignatureWithAddress,
verifySolSignatureWithAddress,
} from "../utils";
import { keccak256, toUtf8Bytes } from "ethers";
import { useRouter } from "next/navigation";
import AddCircleIcon from "@mui/icons-material/AddCircle";
Expand Down Expand Up @@ -385,7 +388,7 @@ export default function Dashboard() {
const addressType = selectedAccount?.startsWith("0x") ? "ETH" : "SOL";
const verificationPassed =
addressType === "ETH"
? verifyEthSignature(
? verifyEthSignatureWithAddress(
messageToSign,
signature.r,
signature.s,
Expand Down
51 changes: 12 additions & 39 deletions examples/react-components/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ import {
Droppable,
Draggable,
DropResult,
DroppableProvided,
DraggableProvided,
} from "@hello-pangea/dnd";
import "./index.css";
import { useRouter } from "next/navigation";
import Navbar from "./components/Navbar";
import { Toaster, toast } from "sonner";

// Define reusable types for provided props
type DroppableProvidedProps = DroppableProvided;
type DraggableProvidedProps = DraggableProvided;

// Define types for config and socials
interface SocialConfig {
enabled: boolean;
Expand All @@ -37,6 +43,7 @@ export default function AuthPage() {
const handleAuthSuccess = async () => {
router.push("/dashboard");
};

const [configOrder, setConfigOrder] = useState([
"socials",
"email",
Expand Down Expand Up @@ -140,25 +147,7 @@ export default function AuthPage() {

<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="configList">
{(provided: {
droppableProps: React.JSX.IntrinsicAttributes &
React.ClassAttributes<HTMLDivElement> &
React.HTMLAttributes<HTMLDivElement>;
innerRef: React.LegacyRef<HTMLDivElement> | undefined;
placeholder:
| string
| number
| boolean
| React.ReactElement<
any,
string | React.JSXElementConstructor<any>
>
| Iterable<React.ReactNode>
| React.ReactPortal
| React.PromiseLikeOfReactNode
| null
| undefined;
}) => (
{(provided: DroppableProvidedProps) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
Expand All @@ -172,22 +161,14 @@ export default function AuthPage() {
index={index}
isDragDisabled={!config.socials.enabled}
>
{(provided: {
innerRef: React.LegacyRef<HTMLDivElement> | undefined;
draggableProps: React.JSX.IntrinsicAttributes &
React.ClassAttributes<HTMLDivElement> &
React.HTMLAttributes<HTMLDivElement>;
dragHandleProps: React.JSX.IntrinsicAttributes &
React.ClassAttributes<HTMLDivElement> &
React.HTMLAttributes<HTMLDivElement>;
}) => (
{(provided: DraggableProvidedProps) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
className="socialContainer"
>
<div
{...provided.dragHandleProps}
{...(provided.dragHandleProps || {})}
className="toggleSocialRow"
>
<div className="labelContainer">
Expand Down Expand Up @@ -242,19 +223,11 @@ export default function AuthPage() {
(!config[key as keyof Config] as boolean)
}
>
{(provided: {
innerRef: React.LegacyRef<HTMLDivElement> | undefined;
draggableProps: React.JSX.IntrinsicAttributes &
React.ClassAttributes<HTMLDivElement> &
React.HTMLAttributes<HTMLDivElement>;
dragHandleProps: React.JSX.IntrinsicAttributes &
React.ClassAttributes<HTMLDivElement> &
React.HTMLAttributes<HTMLDivElement>;
}) => (
{(provided: DraggableProvidedProps) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
{...(provided.dragHandleProps || {})}
className="toggleRow"
>
<div className="labelContainer">
Expand Down
2 changes: 1 addition & 1 deletion examples/react-components/src/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { hashMessage, keccak256, recoverAddress, toUtf8Bytes } from "ethers";
* @param {string} address - The Ethereum address of the signer.
* @returns {boolean} - The recovered Ethereum address.
*/
export function verifyEthSignature(
export function verifyEthSignatureWithAddress(
message: string,
r: string,
s: string,
Expand Down
9 changes: 5 additions & 4 deletions examples/react-components/src/app/utils/facebookUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use server";

import crypto from "crypto";

export async function generateChallengePair() {
Expand All @@ -11,10 +12,10 @@ export async function generateChallengePair() {
}

export async function exchangeCodeForToken(
clientId: any,
redirectURI: any,
authCode: any,
verifier: any
clientId: string,
redirectURI: string,
authCode: string,
verifier: string
) {
const response = await fetch(
`https://graph.facebook.com/v11.0/oauth/access_token`,
Expand Down
41 changes: 29 additions & 12 deletions examples/react-components/src/app/utils/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import { sha256 } from "@noble/hashes/sha2";
import { bytesToHex } from "@noble/hashes/utils";
import { exchangeCodeForToken, generateChallengePair } from "./facebookUtils";

// OAuth URLs
const APPLE_AUTH_URL = "https://appleid.apple.com/auth/authorize";
const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
const FACEBOOK_AUTH_URL = "https://www.facebook.com/v11.0/dialog/oauth";

// Popup Size
const popupWidth = 500;
const popupHeight = 600;
interface OidcTokenParams {
iframePublicKey: string;
clientId: string;
Expand All @@ -16,15 +24,15 @@ export const appleOidcToken = async ({
redirectURI,
}: OidcTokenParams): Promise<any> => {
const nonce = bytesToHex(sha256(iframePublicKey));
const appleAuthUrl = new URL("https://appleid.apple.com/auth/authorize");
const appleAuthUrl = new URL(APPLE_AUTH_URL);
appleAuthUrl.searchParams.set("client_id", clientId);
appleAuthUrl.searchParams.set("redirect_uri", redirectURI);
appleAuthUrl.searchParams.set("response_type", "code id_token");
appleAuthUrl.searchParams.set("response_mode", "fragment");
appleAuthUrl.searchParams.set("nonce", nonce);

const width = 500;
const height = 600;
const width = popupWidth;
const height = popupHeight;
const left = window.screenX + (window.innerWidth - width) / 2;
const top = window.screenY + (window.innerHeight - height) / 2;

Expand Down Expand Up @@ -53,7 +61,10 @@ export const appleOidcToken = async ({
}
}
} catch (error) {
// Ignore cross-origin errors until redirected
// Ignore cross-origin errors until the popup redirects to the same origin.
// These errors occur because the script attempts to access the URL of the popup window while it's on a different domain.
// Due to browser security policies (Same-Origin Policy), accessing properties like location.href on a window that is on a different domain will throw an exception.
// Once the popup redirects to the same origin as the parent window, these errors will no longer occur, and the script can safely access the popup's location to extract parameters.
}

if (authWindow?.closed) {
Expand All @@ -70,16 +81,16 @@ export const googleOidcToken = async ({
redirectURI,
}: OidcTokenParams): Promise<any> => {
const nonce = bytesToHex(sha256(iframePublicKey));
const googleAuthUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
const googleAuthUrl = new URL(GOOGLE_AUTH_URL);
googleAuthUrl.searchParams.set("client_id", clientId);
googleAuthUrl.searchParams.set("redirect_uri", redirectURI);
googleAuthUrl.searchParams.set("response_type", "id_token");
googleAuthUrl.searchParams.set("scope", "openid email profile");
googleAuthUrl.searchParams.set("nonce", nonce);
googleAuthUrl.searchParams.set("prompt", "select_account");

const width = 500;
const height = 600;
const width = popupWidth;
const height = popupHeight;
const left = window.screenX + (window.innerWidth - width) / 2;
const top = window.screenY + (window.innerHeight - height) / 2;

Expand Down Expand Up @@ -108,7 +119,10 @@ export const googleOidcToken = async ({
}
}
} catch (error) {
// Ignore cross-origin errors until redirected
// Ignore cross-origin errors until the popup redirects to the same origin.
// These errors occur because the script attempts to access the URL of the popup window while it's on a different domain.
// Due to browser security policies (Same-Origin Policy), accessing properties like location.href on a window that is on a different domain will throw an exception.
// Once the popup redirects to the same origin as the parent window, these errors will no longer occur, and the script can safely access the popup's location to extract parameters.
}

if (authWindow?.closed) {
Expand Down Expand Up @@ -138,9 +152,9 @@ export const facebookOidcToken = async ({
response_type: "code",
});

const facebookOAuthURL = `https://www.facebook.com/v11.0/dialog/oauth?${params.toString()}`;
const width = 500;
const height = 600;
const facebookOAuthURL = `${FACEBOOK_AUTH_URL}?${params.toString()}`;
const width = popupWidth;
const height = popupHeight;
const left = window.screenX + (window.innerWidth - width) / 2;
const top = window.screenY + (window.innerHeight - height) / 2;

Expand Down Expand Up @@ -192,7 +206,10 @@ export const facebookOidcToken = async ({
}
}
} catch (error) {
// Ignore cross-origin errors until the popup redirects to the same origin
// Ignore cross-origin errors until the popup redirects to the same origin.
// These errors occur because the script attempts to access the URL of the popup window while it's on a different domain.
// Due to browser security policies (Same-Origin Policy), accessing properties like location.href on a window that is on a different domain will throw an exception.
// Once the popup redirects to the same origin as the parent window, these errors will no longer occur, and the script can safely access the popup's location to extract parameters.
}
}, 250);
});
Expand Down
23 changes: 14 additions & 9 deletions packages/sdk-browser/src/sdk-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,20 +313,25 @@ export class TurnkeyBrowserClient extends TurnkeySDKClientBase {
*
* @param credentialBundle
* @param expirationSeconds
* @returns {Promise<void>}
* @returns {Promise<boolean>}
*/
loginWithAuthBundle = async (
credentialBundle: string,
expirationSeconds: string = DEFAULT_SESSION_EXPIRATION
): Promise<any> => {
const whoAmIResult = await this.getWhoami();

const readWriteSessionResultWithSession = {
...whoAmIResult,
credentialBundle: credentialBundle,
sessionExpiry: Date.now() + Number(expirationSeconds) * 1000,
};
await saveSession(readWriteSessionResultWithSession, this.authClient);
try {
const whoAmIResult = await this.getWhoami();

const readWriteSessionResultWithSession = {
...whoAmIResult,
credentialBundle: credentialBundle,
sessionExpiry: Date.now() + Number(expirationSeconds) * 1000,
};
await saveSession(readWriteSessionResultWithSession, this.authClient);
return true;
} catch {
return false;
}
};
}

Expand Down
6 changes: 1 addition & 5 deletions packages/sdk-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
"@react-oauth/google": "^0.12.1",
"@turnkey/sdk-browser": "workspace:*",
"@turnkey/wallet-stamper": "workspace:*",
"usehooks-ts": "^3.1.0",
"@turnkey/crypto": "workspace:*",
"@turnkey/sdk-server": "workspace:*",
"usehooks-ts": "^3.1.0",
"libphonenumber-js": "^1.11.14",
"next": "^15.0.2",
"react-apple-login": "^1.1.6",
Expand All @@ -72,10 +72,6 @@
"@types/react": "^18.2.75",
"react": "^18.2.0"
},
"overrides": {
"react": "18.2.0",
"@types/react": "18.2.75"
},
"engines": {
"node": ">=18.0.0"
}
Expand Down
Loading

0 comments on commit 74f5ba7

Please sign in to comment.