From 14a230f0b626740a0101e65eaee0816f048dc52e Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 10:27:48 +0200 Subject: [PATCH 1/9] feat: register-user-with-passkey NextJS --- .../user/register-user-with-passkey/route.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/app/api/peanut/user/register-user-with-passkey/route.ts diff --git a/src/app/api/peanut/user/register-user-with-passkey/route.ts b/src/app/api/peanut/user/register-user-with-passkey/route.ts new file mode 100644 index 000000000..fda86421e --- /dev/null +++ b/src/app/api/peanut/user/register-user-with-passkey/route.ts @@ -0,0 +1,57 @@ +import { NextRequest, NextResponse } from 'next/server' +import * as consts from '@/constants' +import { cookies } from 'next/headers' + +export async function POST(request: NextRequest) { + const { username } = await request.json() + const apiKey = process.env.PEANUT_API_KEY + + if (!username) { + return new NextResponse('Bad Request: missing required parameters', { status: 400 }) + } + + try { + const response = await fetch(`${consts.PEANUT_API_URL}/register-user-with-passkey`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'api-key': apiKey, + }, + body: JSON.stringify({ username }), + }) + const data = await response.json() + + if (response.status !== 200) { + return new NextResponse( + JSON.stringify({ + error: data.error, + userId: data.userId, + }), + { + status: response.status, + headers: { + 'Content-Type': 'application/json', + }, + } + ) + } + + const token = data.token + + // Set the JWT token in a cookie, nextjs requires to do this serverside + cookies().set('jwt-token', token, { + httpOnly: true, + path: '/', + sameSite: 'strict', + }) + return new NextResponse(JSON.stringify(data), { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }) + } catch (error) { + console.error('Error:', error) + return new NextResponse('Internal Server Error', { status: 500 }) + } +} From 06c31dab3ce33ecf756640a17b4f8c6e76f3cd76 Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 10:38:23 +0200 Subject: [PATCH 2/9] fix: call register endpoint when kernelProvider ready --- src/context/authContext.tsx | 59 +++++++++++++++---------------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index ede1eb258..d634dab7b 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -46,6 +46,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { // TODO: add handle const [user, setUser] = useState(null) + const [username, setUsername] = useState('') + const [isRegistering, setIsRegistering] = useState(false) + const [isLoggingIn, setIsLoggingIn] = useState(false) const [isAuthed, setIsAuthed] = useState(false) const [isFetchingUser, setIsFetchingUser] = useState(true) const toast = useToast({ @@ -69,51 +72,35 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { // you first login w/ passkeys, so flow below would never happen useEffect(() => { if (kernelClientAddress != null && isKernelClientReady) { + if (isRegistering){ + + await fetch('/api/peanut/user/register-user-with-passkey', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username, + address: kernelClientAddress, + }), + }) + setIsRegistering(false) + setUsername('') + + // TODO: handle case where they try to register w/ pre-registered passkey + } + // set PW as active wallet setupWalletsAfterLogin() } }, [kernelClientAddress, isKernelClientReady]) const registerUserWithPasskey = async (username: string) => { + setIsRegistering(true) + setUsername(username) // validatiion of @handle has happened before this function await handleLogin(username) // TODO: replace this with handleRegister // TODO: case of failure on register - - - const userIdResponse = await fetch('/api/peanut/user/get-user-id', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - accountIdentifier: address, - }), - }) - - const response = await userIdResponse.json() - - const siwemsg = utils.createSiweMessage({ - address: address ?? '', - statement: `Sign in to peanut.to. This is your unique user identifier! ${response.userId}`, - }) - - const signature = await signMessageAsync({ - message: siwemsg, - }) - - await fetch('/api/peanut/user/get-jwt-token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - signature: signature, - message: siwemsg, - }), - }) - - - // TODO: handle case where they try to register w/ pre-registered passkey } const loginUserWithPasskey = async (username: string) => { From 778c94de7abca044ec284926d926c87202338700 Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 10:40:33 +0200 Subject: [PATCH 3/9] fix: async call in useEffect --- src/context/authContext.tsx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index d634dab7b..26e11a77a 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -73,17 +73,20 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { useEffect(() => { if (kernelClientAddress != null && isKernelClientReady) { if (isRegistering){ + const fetchRegister = async () => { + await fetch('/api/peanut/user/register-user-with-passkey', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username, + address: kernelClientAddress, + }), + }) + } - await fetch('/api/peanut/user/register-user-with-passkey', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - username, - address: kernelClientAddress, - }), - }) + fetchRegister() setIsRegistering(false) setUsername('') From 9e496b55bb488ded1a683e4b55d4cc9534f29247 Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 12:29:43 +0200 Subject: [PATCH 4/9] feat: Add login logic --- .../api/peanut/user/get-jwt-token/route.ts | 8 ++- .../user/login-user-with-passkey/route.ts | 55 +++++++++++++++++ src/context/authContext.tsx | 59 ++++++++++++++++++- .../walletContext/zeroDevContext.context.tsx | 8 +++ 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 src/app/api/peanut/user/login-user-with-passkey/route.ts diff --git a/src/app/api/peanut/user/get-jwt-token/route.ts b/src/app/api/peanut/user/get-jwt-token/route.ts index 9667a5390..932550a73 100644 --- a/src/app/api/peanut/user/get-jwt-token/route.ts +++ b/src/app/api/peanut/user/get-jwt-token/route.ts @@ -2,7 +2,7 @@ import { NextResponse, NextRequest } from 'next/server' import { cookies } from 'next/headers' import * as consts from '@/constants' export async function POST(request: NextRequest) { - const { signature, message } = await request.json() + const { signature, message, address, user_id } = await request.json() const apiKey = process.env.PEANUT_API_KEY if (!signature || !message || !apiKey) { @@ -10,7 +10,7 @@ export async function POST(request: NextRequest) { } try { - const response = await fetch(`${consts.PEANUT_API_URL}/get-token`, { + const response = await fetch(`${consts.PEANUT_API_URL}/login-user-with-passkey`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -19,11 +19,13 @@ export async function POST(request: NextRequest) { body: JSON.stringify({ signature: signature, message: message, + address: address, + user_id: user_id }), }) if (response.status != 200) { - return new NextResponse('Error in get-jwt-token', { + return new NextResponse('Error in login-user-with-passkey', { status: response.status, headers: { 'Content-Type': 'application/json', diff --git a/src/app/api/peanut/user/login-user-with-passkey/route.ts b/src/app/api/peanut/user/login-user-with-passkey/route.ts new file mode 100644 index 000000000..9667a5390 --- /dev/null +++ b/src/app/api/peanut/user/login-user-with-passkey/route.ts @@ -0,0 +1,55 @@ +import { NextResponse, NextRequest } from 'next/server' +import { cookies } from 'next/headers' +import * as consts from '@/constants' +export async function POST(request: NextRequest) { + const { signature, message } = await request.json() + const apiKey = process.env.PEANUT_API_KEY + + if (!signature || !message || !apiKey) { + return new NextResponse('Bad Request: missing required parameters', { status: 400 }) + } + + try { + const response = await fetch(`${consts.PEANUT_API_URL}/get-token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'api-key': apiKey, + }, + body: JSON.stringify({ + signature: signature, + message: message, + }), + }) + + if (response.status != 200) { + return new NextResponse('Error in get-jwt-token', { + status: response.status, + headers: { + 'Content-Type': 'application/json', + }, + }) + } + + const data = await response.json() + const token = data.token + + // Set the JWT token in a cookie, nextjs requires to do this serverside + cookies().set('jwt-token', token, { + httpOnly: true, + secure: true, + path: '/', + sameSite: 'strict', + }) + + return new NextResponse(JSON.stringify(data), { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }) + } catch (error) { + console.error('Error:', error) + return new NextResponse('Internal Server Error', { status: 500 }) + } +} diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index 26e11a77a..a1ee765dc 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -42,7 +42,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { const { deactiveWalletsOnLogout, setupWalletsAfterLogin } = useWallet() - const {address: kernelClientAddress, isKernelClientReady, handleLogin} = useZeroDev() + const {address: kernelClientAddress, isKernelClientReady, handleLogin, signUserTx} = useZeroDev() // TODO: add handle const [user, setUser] = useState(null) @@ -91,6 +91,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { setUsername('') // TODO: handle case where they try to register w/ pre-registered passkey + } else if (isLoggingIn) { + handleLoginAfterKernelProvider(kernelClientAddress) } // set PW as active wallet @@ -107,7 +109,62 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { } const loginUserWithPasskey = async (username: string) => { + setIsLoggingIn(true) + setUsername(username) + // validatiion of @handle has happened before this function + await handleLogin(username) + // TODO: case of failure on login + } + const handleLoginAfterKernelProvider = async (address: string, ) => { + const userIdResponse = await fetch('/api/peanut/user/get-user-id', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + accountIdentifier: address, + }), + }) + + const response = await userIdResponse.json() + + const siwemsg = utils.createSiweMessage({ + address: address ?? '', + statement: `Sign in to peanut.to. This is your unique user identifier! ${response.userId}`, + }) + + const signature = await signUserTx(siwemsg) + + await fetch('/api/peanut/user/get-jwt-token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + signature: signature, + message: siwemsg, + }), + }) + + return + + await fetch('/api/peanut/user/login-user-with-passkey', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + signature: signature, + message: siwemsg, + address: kernelClientAddress, + user_id: response.userId + }), + }) + + fetchUser() + setIsRegistering(false) + setUsername('') } // TODO: document better diff --git a/src/context/walletContext/zeroDevContext.context.tsx b/src/context/walletContext/zeroDevContext.context.tsx index b472b52b1..c6663e2f0 100644 --- a/src/context/walletContext/zeroDevContext.context.tsx +++ b/src/context/walletContext/zeroDevContext.context.tsx @@ -33,6 +33,7 @@ interface ZeroDevContextType { setIsSendingUserOp: (sendingUserOp: boolean) => void handleRegister: (username: string) => Promise handleLogin: (username: string) => Promise + signUserTx: (message: any) => Promise handleSendUserOpEncoded: ( { to, @@ -197,6 +198,12 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { ////// UserOp functions // + const signUserTx = async (message: any) => { + return kernelClient!.account!.signMessage({ + message + }) + } + // TODO: better docstrings // used when data is already encoded from Peanut // but remains unsigned @@ -312,6 +319,7 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { isSendingUserOp, setIsSendingUserOp, handleRegister, handleLogin, + signUserTx, handleSendUserOpEncoded, handleSendUserOpNotEncoded, address From 13d30f228309754273fa302414d4627565ee90ba Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 12:51:19 +0200 Subject: [PATCH 5/9] Bring back jwt-endpoint --- src/app/(setup)/setup/page.tsx | 1 + src/app/api/peanut/user/get-jwt-token/route.ts | 8 +++----- src/context/authContext.tsx | 7 ++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/(setup)/setup/page.tsx b/src/app/(setup)/setup/page.tsx index 490b2e6cb..2aa507ba2 100644 --- a/src/app/(setup)/setup/page.tsx +++ b/src/app/(setup)/setup/page.tsx @@ -23,6 +23,7 @@ const SetupPage = () => { step.containerClassname )} > +

{step.title}

diff --git a/src/app/api/peanut/user/get-jwt-token/route.ts b/src/app/api/peanut/user/get-jwt-token/route.ts index 932550a73..9667a5390 100644 --- a/src/app/api/peanut/user/get-jwt-token/route.ts +++ b/src/app/api/peanut/user/get-jwt-token/route.ts @@ -2,7 +2,7 @@ import { NextResponse, NextRequest } from 'next/server' import { cookies } from 'next/headers' import * as consts from '@/constants' export async function POST(request: NextRequest) { - const { signature, message, address, user_id } = await request.json() + const { signature, message } = await request.json() const apiKey = process.env.PEANUT_API_KEY if (!signature || !message || !apiKey) { @@ -10,7 +10,7 @@ export async function POST(request: NextRequest) { } try { - const response = await fetch(`${consts.PEANUT_API_URL}/login-user-with-passkey`, { + const response = await fetch(`${consts.PEANUT_API_URL}/get-token`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -19,13 +19,11 @@ export async function POST(request: NextRequest) { body: JSON.stringify({ signature: signature, message: message, - address: address, - user_id: user_id }), }) if (response.status != 200) { - return new NextResponse('Error in login-user-with-passkey', { + return new NextResponse('Error in get-jwt-token', { status: response.status, headers: { 'Content-Type': 'application/json', diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index a1ee765dc..417f531ef 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -15,6 +15,8 @@ interface AuthContextType { updateUserName: (username: string) => Promise submitProfilePhoto: (file: File) => Promise updateBridgeCustomerId: (bridgeCustomerId: string) => Promise + registerUserWithPasskey: (username: string) => Promise + loginUserWithPasskey: (username: string) => Promise addAccount: ({ accountIdentifier, accountType, @@ -173,9 +175,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { // set isAuthed setIsAuthed(true) - //TODO: REMOVE THIS - ONLY FOR TESTING - await handleLogin('hey2') - // // fetch user wallets // // set PW as active wallet // setupWalletsAfterLogin() @@ -426,6 +425,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { fetchUser, updateUserName, submitProfilePhoto, + loginUserWithPasskey, + registerUserWithPasskey, addAccount, isFetchingUser, logoutUser, From 6eb8c226ada499b0d706701a7ec66629d5f556a5 Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 15:20:32 +0200 Subject: [PATCH 6/9] feat: PW can auth with our own passkey server --- src/context/authContext.tsx | 81 ++++--------------- .../walletContext/zeroDevContext.context.tsx | 15 ++-- 2 files changed, 23 insertions(+), 73 deletions(-) diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index 417f531ef..3ed407d00 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -44,13 +44,10 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { const { deactiveWalletsOnLogout, setupWalletsAfterLogin } = useWallet() - const {address: kernelClientAddress, isKernelClientReady, handleLogin, signUserTx} = useZeroDev() + const {address: kernelClientAddress, isKernelClientReady, handleLogin, signMessage} = useZeroDev() // TODO: add handle const [user, setUser] = useState(null) - const [username, setUsername] = useState('') - const [isRegistering, setIsRegistering] = useState(false) - const [isLoggingIn, setIsLoggingIn] = useState(false) const [isAuthed, setIsAuthed] = useState(false) const [isFetchingUser, setIsFetchingUser] = useState(true) const toast = useToast({ @@ -74,51 +71,28 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { // you first login w/ passkeys, so flow below would never happen useEffect(() => { if (kernelClientAddress != null && isKernelClientReady) { - if (isRegistering){ - const fetchRegister = async () => { - await fetch('/api/peanut/user/register-user-with-passkey', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - username, - address: kernelClientAddress, - }), - }) - } - - fetchRegister() - setIsRegistering(false) - setUsername('') - - // TODO: handle case where they try to register w/ pre-registered passkey - } else if (isLoggingIn) { - handleLoginAfterKernelProvider(kernelClientAddress) - } - - // set PW as active wallet - setupWalletsAfterLogin() + authAndFetchUser(kernelClientAddress) } }, [kernelClientAddress, isKernelClientReady]) const registerUserWithPasskey = async (username: string) => { - setIsRegistering(true) - setUsername(username) // validatiion of @handle has happened before this function - await handleLogin(username) // TODO: replace this with handleRegister + const kernelClient = await handleLogin(username) // TODO: replace this with handleRegister // TODO: case of failure on register } const loginUserWithPasskey = async (username: string) => { - setIsLoggingIn(true) - setUsername(username) // validatiion of @handle has happened before this function - await handleLogin(username) + const kernelClient = await handleLogin(username) // TODO: case of failure on login } - const handleLoginAfterKernelProvider = async (address: string, ) => { + const authAndFetchUser = async (address: string) => { + await authUser(address) + await fetchUser() + } + + const authUser = async (address: string) => { const userIdResponse = await fetch('/api/peanut/user/get-user-id', { method: 'POST', headers: { @@ -131,12 +105,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { const response = await userIdResponse.json() - const siwemsg = utils.createSiweMessage({ - address: address ?? '', - statement: `Sign in to peanut.to. This is your unique user identifier! ${response.userId}`, - }) + const message = 'CHANGE THIS STRING WITH A MORE ROBUST THAT INCLUDES USER_ID' - const signature = await signUserTx(siwemsg) + const signature = await signMessage(message) await fetch('/api/peanut/user/get-jwt-token', { method: 'POST', @@ -145,28 +116,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { }, body: JSON.stringify({ signature: signature, - message: siwemsg, - }), - }) - - return - - await fetch('/api/peanut/user/login-user-with-passkey', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - signature: signature, - message: siwemsg, - address: kernelClientAddress, - user_id: response.userId + message: message, }), }) - - fetchUser() - setIsRegistering(false) - setUsername('') } // TODO: document better @@ -177,20 +129,17 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { // // fetch user wallets // // set PW as active wallet - // setupWalletsAfterLogin() - - + setupWalletsAfterLogin() } const afterLogoutUserSetup = async (): Promise => { // set isAuthed setIsAuthed(false) - // fetch user wallets + // deactivate wallets deactiveWalletsOnLogout() } - const fetchUser = async (): Promise => { // @Hugo0: this logic seems a bit duplicated. We should rework with passkeys login. try { diff --git a/src/context/walletContext/zeroDevContext.context.tsx b/src/context/walletContext/zeroDevContext.context.tsx index c6663e2f0..e482f6b0e 100644 --- a/src/context/walletContext/zeroDevContext.context.tsx +++ b/src/context/walletContext/zeroDevContext.context.tsx @@ -33,7 +33,7 @@ interface ZeroDevContextType { setIsSendingUserOp: (sendingUserOp: boolean) => void handleRegister: (username: string) => Promise handleLogin: (username: string) => Promise - signUserTx: (message: any) => Promise + signMessage: (message: any) => Promise handleSendUserOpEncoded: ( { to, @@ -140,6 +140,7 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { setKernelClient(kernelClient) setAddress(kernelClient.account!.address) setIsKernelClientReady(true) + return kernelClient } // TODO: handle logout @@ -165,9 +166,9 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { validatorContractVersion: PasskeyValidatorContractVersion.V0_0_2 }) - await createKernelClient(passkeyValidator) - + const kernelClient = await createKernelClient(passkeyValidator) setIsRegistering(false) + return kernelClient } ////// Login functions @@ -189,16 +190,16 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { validatorContractVersion: PasskeyValidatorContractVersion.V0_0_2 }) - await createKernelClient(passkeyValidator) - + const kernelClient = await createKernelClient(passkeyValidator) setIsLoggingIn(false) + return kernelClient } ////// UserOp functions // - const signUserTx = async (message: any) => { + const signMessage = async (message: any) => { return kernelClient!.account!.signMessage({ message }) @@ -319,7 +320,7 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { isSendingUserOp, setIsSendingUserOp, handleRegister, handleLogin, - signUserTx, + signMessage, handleSendUserOpEncoded, handleSendUserOpNotEncoded, address From 75a7d41d91fd94e4ee5871bc94a134ffa2b8b2a2 Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 15:26:28 +0200 Subject: [PATCH 7/9] feat: Remove routes of auth in favor of signing --- .../user/login-user-with-passkey/route.ts | 55 ------------------ .../user/register-user-with-passkey/route.ts | 57 ------------------- 2 files changed, 112 deletions(-) delete mode 100644 src/app/api/peanut/user/login-user-with-passkey/route.ts delete mode 100644 src/app/api/peanut/user/register-user-with-passkey/route.ts diff --git a/src/app/api/peanut/user/login-user-with-passkey/route.ts b/src/app/api/peanut/user/login-user-with-passkey/route.ts deleted file mode 100644 index 9667a5390..000000000 --- a/src/app/api/peanut/user/login-user-with-passkey/route.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { NextResponse, NextRequest } from 'next/server' -import { cookies } from 'next/headers' -import * as consts from '@/constants' -export async function POST(request: NextRequest) { - const { signature, message } = await request.json() - const apiKey = process.env.PEANUT_API_KEY - - if (!signature || !message || !apiKey) { - return new NextResponse('Bad Request: missing required parameters', { status: 400 }) - } - - try { - const response = await fetch(`${consts.PEANUT_API_URL}/get-token`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'api-key': apiKey, - }, - body: JSON.stringify({ - signature: signature, - message: message, - }), - }) - - if (response.status != 200) { - return new NextResponse('Error in get-jwt-token', { - status: response.status, - headers: { - 'Content-Type': 'application/json', - }, - }) - } - - const data = await response.json() - const token = data.token - - // Set the JWT token in a cookie, nextjs requires to do this serverside - cookies().set('jwt-token', token, { - httpOnly: true, - secure: true, - path: '/', - sameSite: 'strict', - }) - - return new NextResponse(JSON.stringify(data), { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }) - } catch (error) { - console.error('Error:', error) - return new NextResponse('Internal Server Error', { status: 500 }) - } -} diff --git a/src/app/api/peanut/user/register-user-with-passkey/route.ts b/src/app/api/peanut/user/register-user-with-passkey/route.ts deleted file mode 100644 index fda86421e..000000000 --- a/src/app/api/peanut/user/register-user-with-passkey/route.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server' -import * as consts from '@/constants' -import { cookies } from 'next/headers' - -export async function POST(request: NextRequest) { - const { username } = await request.json() - const apiKey = process.env.PEANUT_API_KEY - - if (!username) { - return new NextResponse('Bad Request: missing required parameters', { status: 400 }) - } - - try { - const response = await fetch(`${consts.PEANUT_API_URL}/register-user-with-passkey`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'api-key': apiKey, - }, - body: JSON.stringify({ username }), - }) - const data = await response.json() - - if (response.status !== 200) { - return new NextResponse( - JSON.stringify({ - error: data.error, - userId: data.userId, - }), - { - status: response.status, - headers: { - 'Content-Type': 'application/json', - }, - } - ) - } - - const token = data.token - - // Set the JWT token in a cookie, nextjs requires to do this serverside - cookies().set('jwt-token', token, { - httpOnly: true, - path: '/', - sameSite: 'strict', - }) - return new NextResponse(JSON.stringify(data), { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }) - } catch (error) { - console.error('Error:', error) - return new NextResponse('Internal Server Error', { status: 500 }) - } -} From caecde34ec34ef648d767b771f153c7fb7a5fa9d Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 16:35:44 +0200 Subject: [PATCH 8/9] chore: Remove useless docs --- src/context/walletContext/walletContext.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context/walletContext/walletContext.tsx b/src/context/walletContext/walletContext.tsx index 347e78d50..efbb67266 100644 --- a/src/context/walletContext/walletContext.tsx +++ b/src/context/walletContext/walletContext.tsx @@ -22,9 +22,9 @@ const WalletContext = createContext(undefined) // TODO: change description /** - * Context provider to manage user authentication and profile interactions. - * It handles fetching the user profile, updating user details (e.g., username, profile photo), - * adding accounts and logging out. It also provides hooks for child components to access user data and auth-related functions. + * + * + * */ export const WalletProvider = ({ children }: { children: ReactNode }) => { ////// ZeroDev props From 5066357667999a73b31b4868ba0dac325f4ddc23 Mon Sep 17 00:00:00 2001 From: panosfilianos Date: Thu, 31 Oct 2024 17:35:37 +0200 Subject: [PATCH 9/9] feat: get wallets --- src/context/authContext.tsx | 4 -- src/context/walletContext/walletContext.tsx | 43 +++++-------------- .../walletContext/zeroDevContext.context.tsx | 13 ++++-- src/interfaces/interfaces.ts | 1 + 4 files changed, 21 insertions(+), 40 deletions(-) diff --git a/src/context/authContext.tsx b/src/context/authContext.tsx index 8568d81a5..47f69dc18 100644 --- a/src/context/authContext.tsx +++ b/src/context/authContext.tsx @@ -123,10 +123,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { const afterLoginUserSetup = async (): Promise => { // set isAuthed setIsAuthed(true) - - // // fetch user wallets - // // set PW as active wallet - setupWalletsAfterLogin() } diff --git a/src/context/walletContext/walletContext.tsx b/src/context/walletContext/walletContext.tsx index efbb67266..8b37b12a8 100644 --- a/src/context/walletContext/walletContext.tsx +++ b/src/context/walletContext/walletContext.tsx @@ -34,45 +34,24 @@ export const WalletProvider = ({ children }: { children: ReactNode }) => { const { user } = useAuth() ////// BYOW props - const { address: wagmiAddress, isConnected: isWagmiConnected, addresses } = useAccount() + const { address: wagmiAddress, isConnected: isWagmiConnected } = useAccount() ////// Selected Wallet const [selectedWallet, setSelectedWallet] = useState(undefined) // TODO: this is the var that should be exposed for the app to consume, instead of const { address } = useAccount() anywhere ////// Wallets - const { data: wallets } = useQuery({ + const { data: wallets } = useQuery({ queryKey: ["wallets", user?.user.userId], queryFn: async () => { - /** - * TODO: fetch wallets from backend - * TODO: 2: Remove fetch & pass user?.account ? - */ - const localPasskeys = PasskeyStorage.list() - // const walletsResponse = await fetch('/api/peanut/user/get-wallets') - // if (walletsResponse.ok) { - // // receive in backend format - // const { dbWallets }: { dbWallets: interfaces.IDBWallet[] } = await walletsResponse.json() - // // manipulate to frontend format (add connected attribute) - // const wallets: interfaces.IWallet[] = dbWallets.map((dbWallet: interfaces.IDBWallet) => ({ - // ...dbWallet, - // connected: false // this property will be processed into accurate values later in the flow - // })) - // } - return [ - // { - // walletProviderType: interfaces.WalletProviderType.BYOW, - // protocolType: interfaces.WalletProtocolType.EVM, - // connected: false, - // address: '0x7D4c7063E003CeB8B9413f63569e7AB968AF3714' - // }, - ...localPasskeys.map(({ handle, account }) => ({ - walletProviderType: interfaces.WalletProviderType.PEANUT, - protocolType: interfaces.WalletProtocolType.EVM, - connected: false, - address: account, - handle - })) - ] + const processedWallets = user?.accounts.filter( + account => Object.values(interfaces.WalletProviderType).includes(account.account_type) + ).map(account=> ({ + walletProviderType: account.account_type, + protocolType: account.chain, + address: account.account_identifier, + connected: false + })) + return processedWallets ? processedWallets : [] } }) diff --git a/src/context/walletContext/zeroDevContext.context.tsx b/src/context/walletContext/zeroDevContext.context.tsx index 13ced9e76..4a72eb975 100644 --- a/src/context/walletContext/zeroDevContext.context.tsx +++ b/src/context/walletContext/zeroDevContext.context.tsx @@ -48,8 +48,8 @@ interface ZeroDevContextType { setIsLoggingIn: (loggingIn: boolean) => void isSendingUserOp: boolean setIsSendingUserOp: (sendingUserOp: boolean) => void - handleRegister: (username: string) => Promise - handleLogin: (username: string) => Promise + handleRegister: (username: string) => Promise + handleLogin: (username: string) => Promise signMessage: (message: any) => Promise handleSendUserOpEncoded: ( { @@ -180,7 +180,7 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { ////// Login functions // - const handleLogin = async (handle: string) => { + const handleLogin = async (handle: string): Promise => { setIsLoggingIn(true) try { @@ -198,12 +198,17 @@ export const ZeroDevProvider = ({ children }: { children: ReactNode }) => { validatorContractVersion: PasskeyValidatorContractVersion.V0_0_2 }) - await createKernelClient(passkeyValidator) + const client = await createKernelClient(passkeyValidator) + + setIsLoggingIn(false) + + return client } catch (e) { toast.error('Error logging in. Please try again.') } finally { setIsLoggingIn(false) + return undefined } } diff --git a/src/interfaces/interfaces.ts b/src/interfaces/interfaces.ts index 2874b3021..1e7a1f988 100644 --- a/src/interfaces/interfaces.ts +++ b/src/interfaces/interfaces.ts @@ -365,6 +365,7 @@ interface Account { referrer: string | null referred_users_points: number totalReferralPoints: number + chain: string } export interface IUserProfile {