From 0c39977e9f7649f392f8571f98925b73327a2ae0 Mon Sep 17 00:00:00 2001 From: broody Date: Wed, 5 Jun 2024 15:56:03 -1000 Subject: [PATCH 1/3] Connect provider parse session policies and origin --- packages/keychain/.env.development | 2 +- .../keychain/src/components/connect/types.ts | 2 -- packages/keychain/src/hooks/connection.tsx | 26 ++++++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/keychain/.env.development b/packages/keychain/.env.development index e583bf8be..a7296ca2a 100644 --- a/packages/keychain/.env.development +++ b/packages/keychain/.env.development @@ -11,7 +11,7 @@ NEXT_PUBLIC_ETH_RPC_SEPOLIA="https://eth-sepolia.g.alchemy.com/v2/mURnclB5pn5elD NEXT_PUBLIC_ORIGIN="http://localhost:3001" NEXT_PUBLIC_RP_ID="localhost" NEXT_PUBLIC_ADMIN_URL="http://localhost:3000" -NEXT_PUBLIC_API_BASE_URL="http://localhost:3001" +NEXT_PUBLIC_API_BASE_URL="http://localhost:8000" NEXT_PUBLIC_API_URL="http://localhost:8000/query" NEXT_PUBLIC_RPC_MAINNET="http://localhost:8000/x/starknet/mainnet" NEXT_PUBLIC_RPC_SEPOLIA="http://localhost:8000/x/starknet/sepolia" diff --git a/packages/keychain/src/components/connect/types.ts b/packages/keychain/src/components/connect/types.ts index bed90e9ce..c4cea6910 100644 --- a/packages/keychain/src/components/connect/types.ts +++ b/packages/keychain/src/components/connect/types.ts @@ -22,7 +22,5 @@ export type SignupProps = AuthBaseProps & { }; export type LoginProps = AuthBaseProps & { - chainId: string; - rpcUrl: string; onSignup: (username: string) => void; }; diff --git a/packages/keychain/src/hooks/connection.tsx b/packages/keychain/src/hooks/connection.tsx index 8bcab0452..c63c6a7a5 100644 --- a/packages/keychain/src/hooks/connection.tsx +++ b/packages/keychain/src/hooks/connection.tsx @@ -11,32 +11,44 @@ import Controller from "utils/controller"; import { connectToController, ConnectionCtx } from "utils/connection"; import { isIframe } from "components/connect/utils"; import { RpcProvider } from "starknet"; +import { Policy } from "@cartridge/controller"; const ConnectionContext = createContext(undefined); type ConnectionContextValue = { - controller: Controller; - setController: (controller: Controller) => void; context: ConnectionCtx; - setContext: (context: ConnectionCtx) => void; + controller: Controller; + origin: string; rpcUrl: string; - error: Error; chainId: string; + policies: Policy[]; + error: Error; + setContext: (context: ConnectionCtx) => void; + setController: (controller: Controller) => void; close: () => void; }; export function ConnectionProvider({ children }: PropsWithChildren) { const [parent, setParent] = useState(); const [context, setContext] = useState(); + const [origin, setOrigin] = useState(); const [rpcUrl, setRpcUrl] = useState(); const [chainId, setChainId] = useState(); + const [policies, setPolicies] = useState([]); const [controller, setController] = useState(Controller.fromStore); const [error, setError] = useState(); + const parsePolicies = (policiesStr: string | null): Policy[] => { + if (!policiesStr) return []; + return JSON.parse(policiesStr); + } + useEffect(() => { if (!isIframe()) { const urlParams = new URLSearchParams(window.location.search); + setOrigin(urlParams.get("origin") || process.env.NEXT_PUBLIC_ORIGIN); setRpcUrl(urlParams.get("rpc_url") || process.env.NEXT_PUBLIC_RPC_SEPOLIA); + setPolicies(parsePolicies(urlParams.get("policies"))); return; } @@ -78,12 +90,14 @@ export function ConnectionProvider({ children }: PropsWithChildren) { return ( Date: Thu, 6 Jun 2024 07:29:15 -1000 Subject: [PATCH 2/3] Use connection provider instead of context --- .../components/connect/CreateController.tsx | 14 +----------- .../keychain/src/components/connect/Login.tsx | 14 +++++++----- .../src/components/connect/Signup.tsx | 22 +++++++------------ .../keychain/src/components/connect/types.ts | 9 +------- packages/keychain/src/hooks/connection.tsx | 8 +++++-- packages/keychain/src/pages/login.tsx | 2 -- packages/keychain/src/pages/signup.tsx | 2 -- .../keychain/src/utils/connection/connect.ts | 6 +++++ .../keychain/src/utils/connection/index.ts | 10 +++++++-- 9 files changed, 38 insertions(+), 49 deletions(-) diff --git a/packages/keychain/src/components/connect/CreateController.tsx b/packages/keychain/src/components/connect/CreateController.tsx index 9441bb968..6e38caf8f 100644 --- a/packages/keychain/src/components/connect/CreateController.tsx +++ b/packages/keychain/src/components/connect/CreateController.tsx @@ -2,13 +2,11 @@ import { useState } from "react"; import { Signup } from "./Signup"; import { Login } from "./Login"; import { useConnection } from "hooks/connection"; -import { ConnectCtx } from "utils/connection"; export function CreateController({ isSlot }: { isSlot?: boolean }) { - const { chainId, rpcUrl, context, setController, error } = useConnection(); + const { error } = useConnection(); const [showSignup, setShowSignup] = useState(false); const [prefilledUsername, setPrefilledUsername] = useState(); - const ctx = context as ConnectCtx; if (error) { return <>{error.message}; @@ -16,30 +14,20 @@ export function CreateController({ isSlot }: { isSlot?: boolean }) { return showSignup ? ( { setPrefilledUsername(username); setShowSignup(false); }} - onSuccess={setController} isSlot={isSlot} /> ) : ( { setPrefilledUsername(username); setShowSignup(true); }} - onSuccess={setController} isSlot={isSlot} /> ); diff --git a/packages/keychain/src/components/connect/Login.tsx b/packages/keychain/src/components/connect/Login.tsx index 74d8e8210..9c5c84edc 100644 --- a/packages/keychain/src/components/connect/Login.tsx +++ b/packages/keychain/src/components/connect/Login.tsx @@ -16,17 +16,15 @@ import { RegistrationLink } from "./RegistrationLink"; import { useControllerTheme } from "hooks/theme"; import { doLogin } from "hooks/account"; import { Error as ErrorComp } from "components/Error"; +import { useConnection } from "hooks/connection"; export function Login({ - chainId, - rpcUrl, - origin, - policies, prefilledName = "", isSlot, onSuccess, onSignup, }: LoginProps) { + const { origin, policies, chainId, rpcUrl, setController } = useConnection(); const { event: log } = useAnalytics(); const theme = useControllerTheme(); const [isLoading, setIsLoading] = useState(false); @@ -63,7 +61,11 @@ export function Login({ } controller.store(); - onSuccess(controller); + setController(controller); + + if (onSuccess) { + onSuccess(); + } log({ type: "webauthn_login", address }); } catch (e) { @@ -80,7 +82,7 @@ export function Login({ setIsLoading(false); }, - [chainId, rpcUrl, origin, policies, expiresAt, isSlot, log, onSuccess], + [chainId, rpcUrl, origin, policies, expiresAt, isSlot, log], ); return ( diff --git a/packages/keychain/src/components/connect/Signup.tsx b/packages/keychain/src/components/connect/Signup.tsx index 1cd6d01c7..44f43fa23 100644 --- a/packages/keychain/src/components/connect/Signup.tsx +++ b/packages/keychain/src/components/connect/Signup.tsx @@ -25,14 +25,11 @@ import { doSignup } from "hooks/account"; import { useControllerTheme } from "hooks/theme"; import { Error as ErrorComp } from "components/Error"; import { shortString } from "starknet"; +import { useConnection } from "hooks/connection"; export function Signup({ prefilledName = "", - origin, - policies, isSlot, - chainId, - rpcUrl, onSuccess, onLogin, }: SignupProps) { @@ -78,15 +75,11 @@ export function Signup({ validateOnBlur={false} >
@@ -97,10 +90,6 @@ export function Signup({ } function Form({ - origin, - policies, - chainId, - rpcUrl, isRegistering, isLoading, isSlot, @@ -114,6 +103,7 @@ function Form({ setIsRegistering: (val: boolean) => void; error: Error; }) { + const { origin, policies, chainId, rpcUrl, setController } = useConnection(); const theme = useControllerTheme(); const { values, isValidating } = useFormikContext(); @@ -160,7 +150,11 @@ function Form({ controller.store(); await controller.account.sync(); - onSuccess(controller); + setController(controller); + + if (onSuccess) { + onSuccess(); + } }, }, ); diff --git a/packages/keychain/src/components/connect/types.ts b/packages/keychain/src/components/connect/types.ts index c4cea6910..28c0c154a 100644 --- a/packages/keychain/src/components/connect/types.ts +++ b/packages/keychain/src/components/connect/types.ts @@ -1,6 +1,3 @@ -import Controller from "utils/controller"; -import { Policy } from "@cartridge/controller"; - export type FormValues = { username: string; }; @@ -9,12 +6,8 @@ export type AuthProps = SignupProps | LoginProps; type AuthBaseProps = { prefilledName?: string; - origin?: string; - policies?: Policy[]; isSlot?: boolean; - chainId: string; - rpcUrl: string; - onSuccess: (controller: Controller) => void; + onSuccess?: () => void; }; export type SignupProps = AuthBaseProps & { diff --git a/packages/keychain/src/hooks/connection.tsx b/packages/keychain/src/hooks/connection.tsx index c63c6a7a5..741c58e78 100644 --- a/packages/keychain/src/hooks/connection.tsx +++ b/packages/keychain/src/hooks/connection.tsx @@ -41,19 +41,23 @@ export function ConnectionProvider({ children }: PropsWithChildren) { const parsePolicies = (policiesStr: string | null): Policy[] => { if (!policiesStr) return []; return JSON.parse(policiesStr); - } + }; useEffect(() => { if (!isIframe()) { const urlParams = new URLSearchParams(window.location.search); setOrigin(urlParams.get("origin") || process.env.NEXT_PUBLIC_ORIGIN); - setRpcUrl(urlParams.get("rpc_url") || process.env.NEXT_PUBLIC_RPC_SEPOLIA); + setRpcUrl( + urlParams.get("rpc_url") || process.env.NEXT_PUBLIC_RPC_SEPOLIA, + ); setPolicies(parsePolicies(urlParams.get("policies"))); return; } const connection = connectToController({ + setOrigin, setRpcUrl, + setPolicies, setContext, setController, }); diff --git a/packages/keychain/src/pages/login.tsx b/packages/keychain/src/pages/login.tsx index 0d6d43df0..61e4e91cd 100644 --- a/packages/keychain/src/pages/login.tsx +++ b/packages/keychain/src/pages/login.tsx @@ -21,8 +21,6 @@ const Login: NextPage = () => { return ( router.push({ pathname: "/signup", query: router.query })} onSuccess={async () => { router.replace(`${process.env.NEXT_PUBLIC_ADMIN_URL}/profile`); diff --git a/packages/keychain/src/pages/signup.tsx b/packages/keychain/src/pages/signup.tsx index fc79b2a9e..83bf33e22 100644 --- a/packages/keychain/src/pages/signup.tsx +++ b/packages/keychain/src/pages/signup.tsx @@ -20,8 +20,6 @@ export default function Signup() { return ( router.push({ pathname: "/login", query: router.query })} onSuccess={() => { router.replace(`${process.env.NEXT_PUBLIC_ADMIN_URL}/profile`); diff --git a/packages/keychain/src/utils/connection/connect.ts b/packages/keychain/src/utils/connection/connect.ts index c64632b24..0b0afb6b7 100644 --- a/packages/keychain/src/utils/connection/connect.ts +++ b/packages/keychain/src/utils/connection/connect.ts @@ -3,15 +3,21 @@ import { ConnectCtx, ConnectionCtx } from "./types"; import Controller from "utils/controller"; export function connectFactory({ + setOrigin, setRpcUrl, + setPolicies, setContext, }: { + setOrigin: (origin: string) => void; setRpcUrl: (url: string) => void; + setPolicies: (policies: Policy[]) => void; setContext: (context: ConnectionCtx) => void; }) { return (origin: string) => (policies: Policy[], rpcUrl: string): Promise => { + setOrigin(origin); setRpcUrl(rpcUrl); + setPolicies(policies); return new Promise((resolve, reject) => { setContext({ diff --git a/packages/keychain/src/utils/connection/index.ts b/packages/keychain/src/utils/connection/index.ts index 251a3788c..36b5f15c7 100644 --- a/packages/keychain/src/utils/connection/index.ts +++ b/packages/keychain/src/utils/connection/index.ts @@ -1,6 +1,6 @@ export * from "./types"; -import { ConnectError, ResponseCodes } from "@cartridge/controller"; +import { ConnectError, Policy, ResponseCodes } from "@cartridge/controller"; import { connectToParent } from "@cartridge/penpal"; import { normalize as normalizeOrigin } from "utils/url"; import Controller from "utils/controller"; @@ -15,17 +15,23 @@ import { username } from "./username"; import { ConnectionCtx } from "./types"; export function connectToController({ + setOrigin, setRpcUrl, + setPolicies, setContext, setController, }: { + setOrigin: (origin: string) => void; setRpcUrl: (url: string) => void; + setPolicies: (policies: Policy[]) => void; setContext: (ctx: ConnectionCtx) => void; setController: (controller: Controller) => void; }) { return connectToParent({ methods: { - connect: normalize(connectFactory({ setRpcUrl, setContext })), + connect: normalize( + connectFactory({ setOrigin, setRpcUrl, setPolicies, setContext }), + ), disconnect: normalize(validate(disconnectFactory(setController))), execute: normalize(validate(executeFactory({ setContext }))), estimateDeclareFee: normalize(validate(estimateDeclareFee)), From 3a25a1ff03492bc8d059bce332692bcb7f544e8d Mon Sep 17 00:00:00 2001 From: broody Date: Thu, 6 Jun 2024 08:06:54 -1000 Subject: [PATCH 3/3] Login mode --- .../components/connect/CreateController.tsx | 10 +++++++++- .../keychain/src/components/connect/Login.tsx | 18 +++++++++++++----- .../keychain/src/components/connect/types.ts | 6 ++++++ packages/keychain/src/pages/index.tsx | 3 ++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/keychain/src/components/connect/CreateController.tsx b/packages/keychain/src/components/connect/CreateController.tsx index 6e38caf8f..fdc50d0c1 100644 --- a/packages/keychain/src/components/connect/CreateController.tsx +++ b/packages/keychain/src/components/connect/CreateController.tsx @@ -2,8 +2,15 @@ import { useState } from "react"; import { Signup } from "./Signup"; import { Login } from "./Login"; import { useConnection } from "hooks/connection"; +import { LoginMode } from "./types"; -export function CreateController({ isSlot }: { isSlot?: boolean }) { +export function CreateController({ + isSlot, + loginMode, +}: { + isSlot?: boolean; + loginMode?: LoginMode; +}) { const { error } = useConnection(); const [showSignup, setShowSignup] = useState(false); const [prefilledUsername, setPrefilledUsername] = useState(); @@ -28,6 +35,7 @@ export function CreateController({ isSlot }: { isSlot?: boolean }) { setPrefilledUsername(username); setShowSignup(true); }} + mode={loginMode} isSlot={isSlot} /> ); diff --git a/packages/keychain/src/components/connect/Login.tsx b/packages/keychain/src/components/connect/Login.tsx index 9c5c84edc..f107bb5c5 100644 --- a/packages/keychain/src/components/connect/Login.tsx +++ b/packages/keychain/src/components/connect/Login.tsx @@ -9,7 +9,7 @@ import { } from "components"; import { useCallback, useState } from "react"; import Controller from "utils/controller"; -import { FormValues, LoginProps } from "./types"; +import { FormValues, LoginMode, LoginProps } from "./types"; import { useAnalytics } from "hooks/analytics"; import { fetchAccount, validateUsernameFor } from "./utils"; import { RegistrationLink } from "./RegistrationLink"; @@ -21,6 +21,7 @@ import { useConnection } from "hooks/connection"; export function Login({ prefilledName = "", isSlot, + mode = LoginMode.Webauthn, onSuccess, onSignup, }: LoginProps) { @@ -54,10 +55,17 @@ export function Login({ }); try { - if (isSlot || !origin || !policies) { - await doLogin(values.username, credentialId, publicKey); - } else { - await controller.approve(origin, expiresAt, policies); + switch (mode) { + case LoginMode.Webauthn: + await doLogin(values.username, credentialId, publicKey); + break; + case LoginMode.Controller: + if (policies.length === 0) { + throw new Error("Policies required for controller "); + } + + await controller.approve(origin, expiresAt, policies); + break; } controller.store(); diff --git a/packages/keychain/src/components/connect/types.ts b/packages/keychain/src/components/connect/types.ts index 28c0c154a..6f6d37f2f 100644 --- a/packages/keychain/src/components/connect/types.ts +++ b/packages/keychain/src/components/connect/types.ts @@ -14,6 +14,12 @@ export type SignupProps = AuthBaseProps & { onLogin: (username: string) => void; }; +export enum LoginMode { + Webauthn, // client server login flow + Controller, // client side only create session flow +} + export type LoginProps = AuthBaseProps & { + mode?: LoginMode; onSignup: (username: string) => void; }; diff --git a/packages/keychain/src/pages/index.tsx b/packages/keychain/src/pages/index.tsx index 1b2fcbb1b..0e658399e 100644 --- a/packages/keychain/src/pages/index.tsx +++ b/packages/keychain/src/pages/index.tsx @@ -19,6 +19,7 @@ import { } from "utils/connection"; import { diff } from "utils/controller"; import { logout } from "utils/connection/logout"; +import { LoginMode } from "components/connect/types"; function Home() { const { context, controller, chainId, setContext, error } = useConnection(); @@ -37,7 +38,7 @@ function Home() { // No controller, send to login if (!controller) { - return ; + return ; } const onLogout = (context: ConnectionCtx) => {