Skip to content

Commit

Permalink
fix(unlock-app): refactor waas util and clean up sessions (#15046)
Browse files Browse the repository at this point in the history
* refactor keyUtil

* improve google sign in component

* sign out nextauth session upon successful privy login

* cleanup
  • Loading branch information
0xTxbi authored Nov 8, 2024
1 parent c676972 commit d22244c
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 138 deletions.
11 changes: 9 additions & 2 deletions unlock-app/src/components/legacy-auth/ConnectToPrivy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useLogin } from '@privy-io/react-auth'
import { useEffect, useRef } from 'react'
import { LoginModal } from '@privy-io/react-auth'
import { signOut as nextAuthSignOut, useSession } from 'next-auth/react'

interface ConnectToPrivyProps {
userEmail: string
Expand All @@ -13,9 +14,15 @@ export default function ConnectToPrivy({
userEmail,
onNext,
}: ConnectToPrivyProps) {
const { data: session } = useSession()

const { login } = useLogin({
onComplete: () => {
// When login is complete, proceed to next step (tab)
onComplete: async () => {
// When Privy login is complete, sign out of next-auth session
if (session) {
await nextAuthSignOut({ redirect: false })
}
// Then proceed to next step
onNext()
},
onError: (error) => {
Expand Down
191 changes: 133 additions & 58 deletions unlock-app/src/components/legacy-auth/SignInWithCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
InitializeWaas,
PrivateKeyFormat,
RawPrivateKey,
Waas,
} from '@coinbase/waas-sdk-web'
import { config } from '~/config/app'
import { getUserWaasUuid } from '~/utils/getUserWaasUuid'
Expand All @@ -15,64 +16,124 @@ import { Button } from '@unlock-protocol/ui'
import { getSession } from 'next-auth/react'
import ReCAPTCHA from 'react-google-recaptcha'

// TODO: finish testing this works in a "real" environment (can't test with existing accounts on a different domain)
export const getPrivateKeyFromWaas = async (
captcha: string,
accountType: UserAccountType
) => {
const waas = await InitializeWaas({
// Singleton instance for WAAS
let waasInstance: Waas | null = null

/**
* Initialize and return the WAAS instance.
* Ensures that WAAS is initialized only once (Singleton Pattern).
*/
const getWaasInstance = async (): Promise<Waas> => {
if (waasInstance) {
return waasInstance
}

waasInstance = await InitializeWaas({
collectAndReportMetrics: true,
enableHostedBackups: true,
prod: config.env === 'prod',
projectId: config.coinbaseProjectId,
})

const user = await waas.auth.login({
provideAuthToken: async () => {
const nextAuthSession = await getSession()
const waasToken = await getUserWaasUuid(
captcha,
nextAuthSession?.user?.email as string,
accountType,
nextAuthSession?.user?.token as string
)
return waasToken!
},
})
return waasInstance
}

console.log('user from waas', user)

let wallet

if (waas.wallets.wallet) {
// Resuming wallet
wallet = waas.wallets.wallet
} else if (user.hasWallet) {
// Restoring wallet
console.log('restoring wallet')
wallet = await waas.wallets.restoreFromHostedBackup()
console.log('wallet from waas', wallet)
} else {
console.log('creating a waas wallet (for debugging only!)', wallet)
// Creating wallet
wallet = await waas.wallets.create()
}
// TODO: finish testing this works in a "real" environment (can't test with existing accounts on a different domain)
/**
* Retrieves the private key from WAAS.
* @param captcha - The CAPTCHA value.
* @param accountType - The type of user account.
* @returns The private key string or null if not found.
*/
export const getPrivateKeyFromWaas = async (
captcha: string,
accountType: UserAccountType
): Promise<string | null> => {
try {
const waas = await getWaasInstance()

if (!wallet) {
console.error('No wallet linked to that user. It cannot be migrated.')
return
}
const user = await waas.auth.login({
provideAuthToken: async () => {
const nextAuthSession = await getSession()
if (
!nextAuthSession ||
!nextAuthSession.user ||
!nextAuthSession.user.email ||
!nextAuthSession.user.token
) {
throw new Error('Invalid session data')
}

const exportedKeys = await wallet.exportKeysFromHostedBackup(
undefined,
'RAW' as PrivateKeyFormat
)
// use the first key's private key (ecKeyPrivate)
if (exportedKeys.length > 0) {
const firstKey = exportedKeys[0] as RawPrivateKey
return firstKey.ecKeyPrivate
} else {
console.error('No private keys found in wallet, so it cannot be migrated.')
const waasToken = await getUserWaasUuid(
captcha,
nextAuthSession.user.email,
accountType,
nextAuthSession.user.token
)

if (!waasToken) {
throw new Error('Failed to retrieve WAAS token')
}

return waasToken
},
})

// Conditionally log based on environment
if (config.env !== 'prod') {
console.log('user from waas', user)
}

let wallet: any = null

if (waas.wallets.wallet) {
// Resuming wallet
wallet = waas.wallets.wallet
} else if (user.hasWallet) {
// Restoring wallet
if (config.env !== 'prod') {
console.log('restoring wallet')
}
wallet = await waas.wallets.restoreFromHostedBackup()

if (config.env !== 'prod') {
console.log('wallet from waas', wallet)
}
} else {
if (config.env !== 'prod') {
console.log('creating a waas wallet (for debugging only!)')
}
// Creating wallet
wallet = await waas.wallets.create()
}

if (!wallet) {
console.error('No wallet linked to that user. It cannot be migrated.')
return null
}

const exportedKeys = await wallet.exportKeysFromHostedBackup(
undefined,
PrivateKeyFormat.RAW
)

if (config.env !== 'prod') {
console.log('exportedKeys', exportedKeys)
}

// Use the first key's private key (ecKeyPrivate)
if (exportedKeys.length > 0) {
const firstKey = exportedKeys[0] as RawPrivateKey
return firstKey.ecKeyPrivate
} else {
console.error(
'No private keys found in wallet, so it cannot be migrated.'
)
return null
}
} catch (error) {
console.error('Error in getPrivateKeyFromWaas:', error)
return null
}
}

Expand All @@ -89,24 +150,38 @@ export const SignInWithCode = ({
const sendEmailCode = async () => {
try {
const captcha = await getCaptchaValue()
if (!captcha) {
ToastHelper.error('CAPTCHA verification failed')
return
}
await locksmith.sendVerificationCode(captcha, email)
ToastHelper.success('Email code sent!')
setCodeSent(true)
} catch (error) {
console.error(error)
console.error('Error sending email code:', error)
ToastHelper.error('Error sending email code, try again later')
}
}

const onCodeCorrect = async () => {
const privateKey = await getPrivateKeyFromWaas(
await getCaptchaValue(),
UserAccountType.EmailCodeAccount
)
if (privateKey) {
onNext(privateKey)
} else {
ToastHelper.error('Error getting private key from WAAS')
try {
const captcha = await getCaptchaValue()
if (!captcha) {
ToastHelper.error('CAPTCHA verification failed')
return
}
const privateKey = await getPrivateKeyFromWaas(
captcha,
UserAccountType.EmailCodeAccount
)
if (privateKey) {
onNext(privateKey)
} else {
ToastHelper.error('Error getting private key from WAAS')
}
} catch (error) {
console.error('Error in onCodeCorrect:', error)
ToastHelper.error('Error processing the code')
}
}

Expand Down
Loading

0 comments on commit d22244c

Please sign in to comment.