Skip to content

Commit

Permalink
🚧 Login + session token with privy wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
KONFeature committed Dec 18, 2024
1 parent 891d2e4 commit f8662a3
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 44 deletions.
3 changes: 2 additions & 1 deletion infra/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
indexerUrl,
isProd,
nexusRpcSecret,
pimlicoApiKey, privyAppId,
pimlicoApiKey,
privyAppId,
umamiWalletWebsiteId,
vapidPublicKey,
} from "./config";
Expand Down
6 changes: 2 additions & 4 deletions packages/app-essentials/src/blockchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ export {
type GetTokenMetadataParams,
} from "./actions/getTokenMetadata";
export { getTokenBalances } from "./actions/getTokenBalances";
export {
KernelWallet
} from "./wallet"
export { KernelWallet } from "./wallet";
// Abis
export {
campaignFactoryAbi,
Expand Down Expand Up @@ -62,4 +60,4 @@ export { getExecutionAbi, mintAbi } from "./abis/custom";
export {
sendInteractionsSelector,
sendInteractionSelector,
} from "./abis/selectors";
} from "./abis/selectors";
12 changes: 6 additions & 6 deletions packages/app-essentials/src/blockchain/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ const webAuthNValidatorEnablingLayout = [
* @param signerPubKey
*/
function getWebAuthNSmartWalletInitCode({
authenticatorIdHash,
signerPubKey,
}: {
authenticatorIdHash,
signerPubKey,
}: {
authenticatorIdHash: Hex;
signerPubKey: { x: Hex; y: Hex };
}): Hex {
Expand Down Expand Up @@ -84,8 +84,8 @@ function getWebAuthNSmartWalletInitCode({
* @param ecdsaAddress
*/
function getFallbackWalletInitCode({
ecdsaAddress,
}: {
ecdsaAddress,
}: {
ecdsaAddress: Hex;
}): Hex {
if (!ecdsaAddress) throw new Error("Owner account not found");
Expand All @@ -108,4 +108,4 @@ function getFallbackWalletInitCode({
export const KernelWallet = {
getWebAuthNSmartWalletInitCode,
getFallbackWalletInitCode,
}
};
35 changes: 21 additions & 14 deletions packages/backend-elysia/src/domain/auth/models/WalletSessionDto.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { t } from "@backend-utils";

export const WalletAuthResponseDto = t.Object({
token: t.String(),
const PrivyWalletTokenDto = t.Object({
address: t.Address(),
authenticatorId: t.String(),
publicKey: t.Object({
x: t.Hex(),
y: t.Hex(),
}),
transports: t.Optional(t.Array(t.String())),
sdkJwt: t.Object({
token: t.String(),
expires: t.Number(),
}),
authenticatorId: t.TemplateLiteral([t.Literal("privy-"), t.String()]),
publicKey: t.Hex(),
transports: t.Undefined(),
});
export const WalletTokenDto = t.Object({
export const WebAuthNWalletTokenDto = t.Object({
address: t.Address(),
authenticatorId: t.String(),
authenticatorId: t.String(), // 'Privy' in case of fallback authentication
publicKey: t.Object({
x: t.Hex(),
y: t.Hex(),
}),
transports: t.Optional(t.Array(t.String())),
});
export const WalletTokenDto = t.Union([
PrivyWalletTokenDto,
WebAuthNWalletTokenDto,
]);

export const WalletAuthResponseDto = t.Intersect([
t.Object({
token: t.String(),
sdkJwt: t.Object({
token: t.String(),
expires: t.Number(),
}),
}),
WalletTokenDto,
]);
76 changes: 76 additions & 0 deletions packages/backend-elysia/src/domain/auth/routes/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@simplewebauthn/server";
import { Elysia } from "elysia";
import { Binary } from "mongodb";
import { verifyMessage } from "viem/actions";
import { WalletAuthResponseDto } from "../models/WalletSessionDto";
import { walletSdkSessionService } from "../services/WalletSdkSessionService";
import { webAuthNService } from "../services/WebAuthNService";
Expand Down Expand Up @@ -53,6 +54,81 @@ export const walletAuthRoutes = new Elysia({ prefix: "/wallet" })
},
}
)
// Privy login
.post(
"/privyLogin",
async ({
// Request
body: { expectedChallenge, signature, wallet, walletId, ssoId },
// Response
error,
// Context
client,
walletJwt,
generateSdkJwt,
resolveSsoSession,
}) => {
// Rebuild the message that have been signed
const message = `I want to connect to Frak and I accept the CGU.\n Verification code:${expectedChallenge}`;

// Verify the message signature
const isValidSignature = await verifyMessage(client, {
signature,
message,
address: wallet,
});
if (!isValidSignature) {
return error(404, "Invalid signature");
}

const authenticatorId = `privy-${walletId}` as const;

// Create the token and set the cookie
const token = await walletJwt.sign({
address: wallet,
authenticatorId,
publicKey: wallet,
sub: wallet,
iat: Date.now(),
transports: undefined,
});

// Finally, generate a JWT token for the SDK
const sdkJwt = await generateSdkJwt({ wallet });

// If all good, mark the sso as done
if (ssoId) {
await resolveSsoSession({
id: ssoId,
wallet,
authenticatorId,
});
}

return {
token,
address: wallet,
authenticatorId,
publicKey: wallet,
sdkJwt,
transports: undefined,
};
},
{
body: t.Object({
expectedChallenge: t.String(),
wallet: t.Address(),
walletId: t.String(),
signature: t.Hex(),
// potential sso id
ssoId: t.Optional(t.Hex()),
}),
response: {
404: t.String(),
200: WalletAuthResponseDto,
},
}
)
// Login
.post(
"/login",
Expand Down
15 changes: 14 additions & 1 deletion packages/wallet/app/context/wallet/smartWallet/connector.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { currentChain } from "@/context/blockchain/provider";
import { getSmartAccountProvider } from "@/context/wallet/smartWallet/provider";
import type { SmartAccountV06 } from "@/context/wallet/smartWallet/utils";
import type { ConnectedWallet } from "@privy-io/react-auth";
import type { Transport } from "viem";
import { createConnector } from "wagmi";

smartAccountConnector.type = "frakSmartAccountConnector" as const;

export type FrakWalletConnector = ReturnType<
ReturnType<typeof smartAccountConnector>
>;

/**
* Create a connector for the smart account
*/
Expand All @@ -22,7 +27,10 @@ export function smartAccountConnector<
let provider: Provider | undefined;

// Create the wagmi connector itself
return createConnector<Provider>((config) => ({
return createConnector<
Provider,
{ onPrivyWalletsUpdate: (args: { wallets: ConnectedWallet[] }) => void }
>((config) => ({
id: "frak-wallet-connector",
name: "Frak Smart Account",
type: smartAccountConnector.type,
Expand Down Expand Up @@ -151,5 +159,10 @@ export function smartAccountConnector<
onDisconnect() {
config.emitter.emit("disconnect");
},
// When the list of privy wallets change
onPrivyWalletsUpdate({ wallets }: { wallets: ConnectedWallet[] }) {
// todo: Do some stuff with it
console.log("Wagmi provider wallets update", { wallets });
},
}));
}
23 changes: 19 additions & 4 deletions packages/wallet/app/context/wallet/smartWallet/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { parseWebAuthNAuthentication } from "@/context/wallet/smartWallet/webAut
import { sessionAtom } from "@/module/common/atoms/session";
import { lastWebAuthNActionAtom } from "@/module/common/atoms/webauthn";
import { getSafeSession } from "@/module/listener/utils/localStorage";
import type { PrivyWallet } from "@/types/Session";
import type { WebAuthNWallet } from "@/types/WebAuthN";
import { jotaiStore } from "@module/atoms/store";
import { startAuthentication } from "@simplewebauthn/browser";
Expand All @@ -28,7 +29,7 @@ type SmartAccountProvierParameters = {
/**
* Method when the account has changed
*/
onAccountChanged: (newWallet?: WebAuthNWallet) => void;
onAccountChanged: (newWallet?: WebAuthNWallet | PrivyWallet) => void;
};

/**
Expand Down Expand Up @@ -102,10 +103,24 @@ export function getSmartAccountProvider<
return undefined;
}

const privyWallet =
currentWebAuthNWallet.authenticatorId.startsWith("privy-")
? (currentWebAuthNWallet as PrivyWallet)
: undefined;
const webauthnWallet = privyWallet
? undefined
: (currentWebAuthNWallet as WebAuthNWallet);

// Otherwise, build it
targetSmartAccount = await buildSmartAccount({
wallet: currentWebAuthNWallet,
});
if (privyWallet) {
// todo: Custom for privy here
return undefined;
}
targetSmartAccount = webauthnWallet
? await buildSmartAccount({
wallet: webauthnWallet,
})
: undefined;

// Save the new one
currentSmartAccountClient = targetSmartAccount;
Expand Down
Loading

0 comments on commit f8662a3

Please sign in to comment.