diff --git a/locksmith/__tests__/controllers/v2/userController.test.ts b/locksmith/__tests__/controllers/v2/userController.test.ts deleted file mode 100644 index 681127bb2ae..00000000000 --- a/locksmith/__tests__/controllers/v2/userController.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { vi, expect, beforeAll, afterAll } from 'vitest' -import request from 'supertest' -import app from '../../app' -import { IssueUserTokenOptions } from '@coinbase/waas-server-auth' -import { UserAccountType } from '../../../src/controllers/userController' -import { UserAccount } from '../../../src/models/userAccount' -import VerificationCodes from '../../../src/models/verificationCodes' - -const nextAuthToken = 'token' -const token = crypto.randomUUID() -const coinbaseAuthToken = 'coinbase' -const emailAddress = 'test@test.com' -const emailAddress2 = 'test2@test.com' -const selectedProvider = 'GOOGLE_ACCOUNT' -const emailCode = '123456' - -vi.mock('../../../src/utils/verifyNextAuthToken', () => { - return { - verifyNextAuthToken: vi - .fn() - .mockImplementation( - (selectedProvider: UserAccountType, email: string, token: string) => { - return true - } - ), - } -}) - -vi.mock('@coinbase/waas-server-auth', () => { - return { - issueUserToken: vi - .fn() - .mockImplementation((options: IssueUserTokenOptions): Promise => { - return Promise.resolve(coinbaseAuthToken) - }), - } -}) - -vi.mock('../../../src/operations/wedlocksOperations', () => { - return { - sendEmail: vi.fn().mockImplementation(() => { - return true - }), - sendEmail: vi.fn().mockImplementation(() => { - return true - }), - } -}) - -describe('Get UUID from Coinbase WAAS', () => { - afterEach(async () => { - await UserAccount.destroy({ where: { emailAddress: emailAddress } }) - }) - - it('returns UUID from Coinbase Waas', async () => { - expect.assertions(2) - const retrieveWaasUuidRes = await request(app) - .post(`/v2/api/users/${emailAddress}/${selectedProvider}/waas`) - .send({ token: nextAuthToken }) - - expect(retrieveWaasUuidRes.status).toBe(200) - expect(retrieveWaasUuidRes.body).toEqual({ token: coinbaseAuthToken }) - }) - - it('returns error if no token is provided', async () => { - expect.assertions(1) - const retrieveWaasUuidRes = await request(app) - .post(`/v2/api/users/${emailAddress}/${selectedProvider}/waas`) - .send({ token: '' }) - - expect(retrieveWaasUuidRes.status).toBe(401) - }) - - it('should not create and login user with UnlockAccount', async () => { - expect.assertions(1) - const retrieveWaasUuidRes = await request(app) - .post( - `/v2/api/users/${emailAddress}/${UserAccountType.UnlockAccount}/waas` - ) - .send({ token: nextAuthToken }) - - expect(retrieveWaasUuidRes.status).toBe(500) - }) -}) - -describe('Email verification code', async () => { - beforeAll(async () => { - await VerificationCodes.create({ - emailAddress: emailAddress, - code: emailCode, - codeExpiration: new Date('2049-01-01'), - token: token, - tokenExpiration: new Date('2049-01-01'), - }) - }) - - afterAll(async () => { - await VerificationCodes.destroy({ where: { emailAddress: emailAddress } }) - await VerificationCodes.destroy({ - where: { emailAddress: emailAddress2 }, - }) - }) - - it('should send email verification code', async () => { - expect.assertions(1) - const res = await request(app).get( - `/v2/api/users/${emailAddress2}/send-verification-code` - ) - - expect(res.status).toBe(200) - }) - - it('should return 200 if email code is valid', async () => { - expect.assertions(1) - const res = await request(app) - .post(`/v2/api/users/${emailAddress}/verify-email-code`) - .send({ code: emailCode }) - - expect(res.status).toBe(200) - }) - - it('should return 400 if email code is invalid', async () => { - expect.assertions(1) - const res = await request(app) - .post(`/v2/api/users/${emailAddress}/verify-email-code`) - .send({ code: '654321' }) - - expect(res.status).toBe(400) - }) - - it('should return 400 if email code has been used', async () => { - expect.assertions(1) - const res = await request(app) - .post(`/v2/api/users/${emailAddress}/verify-email-code`) - .send({ code: emailCode }) - - expect(res.status).toBe(400) - }) - - it('should change used email code', async () => { - expect.assertions(2) - const res = await request(app).get( - `/v2/api/users/some@test.email/send-verification-code` - ) - - expect(res.status).toBe(200) - - const codeRes = await request(app) - .post(`/v2/api/users/${emailAddress}/verify-email-code`) - .send({ code: emailCode }) - - expect(codeRes.status).toBe(400) - }) -}) diff --git a/locksmith/package.json b/locksmith/package.json index e9f8b246560..c55da55f658 100644 --- a/locksmith/package.json +++ b/locksmith/package.json @@ -37,7 +37,6 @@ "dependencies": { "@ambire/signature-validator": "1.4.1", "@aws-sdk/client-s3": "3.703.0", - "@coinbase/waas-server-auth": "3.1.2", "@logtail/node": "0.5.2", "@logtail/winston": "0.5.2", "@nuintun/qrcode": "4.1.6", diff --git a/locksmith/src/controllers/userController.ts b/locksmith/src/controllers/userController.ts index c8442633c3d..b8a5ba8389f 100644 --- a/locksmith/src/controllers/userController.ts +++ b/locksmith/src/controllers/userController.ts @@ -5,13 +5,6 @@ import UserOperations from '../operations/userOperations' import logger from '../logger' import { ethers } from 'ethers' import { MemoryCache } from 'memory-cache-node' -import { issueUserToken } from '@coinbase/waas-server-auth' -import config from '../config/config' -import { verifyNextAuthToken } from '../utils/verifyNextAuthToken' -import { z } from 'zod' -import { generateVerificationCode } from '../utils/generateVerificationCode' -import VerificationCodes from '../models/verificationCodes' -import { sendEmail } from '../operations/wedlocksOperations' // Decoy users are cached for 15 minutes const cacheDuration = 60 * 15 @@ -19,9 +12,6 @@ const decoyUserCache = new MemoryCache(cacheDuration / 5, 1000) export const enum UserAccountType { UnlockAccount = 'UNLOCK_ACCOUNT', - GoogleAccount = 'GOOGLE_ACCOUNT', - PasskeyAccount = 'PASSKEY_ACCOUNT', - EmailCodeAccount = 'EMAIL_CODE', } export const createUser = async (req: Request, res: Response): Promise => { @@ -143,90 +133,6 @@ export const retrieveEncryptedPrivatekey = async ( } } -const RetrieveWaasUuidBodySchema = z.object({ - token: z.string(), -}) - -export const retrieveWaasUuid = async ( - req: Request, - res: Response -): Promise => { - const { emailAddress, selectedProvider } = req.params - const { token } = RetrieveWaasUuidBodySchema.parse(req.body) - - if (!token) { - res.sendStatus(401) - return - } - - // Verify the JWT token - const isTokenValid = await verifyNextAuthToken( - selectedProvider as UserAccountType, - emailAddress, - token - ) - if (!isTokenValid) { - res.status(401).json({ - message: 'There was an error verifying the token or it is not valid', - }) - return - } - - let userUUID - - const user = await UserOperations.findUserAccountByEmail( - req.params.emailAddress - ) - - userUUID = user?.id - - // If no user is found, create - if (!user) { - const userAccountType = selectedProvider as UserAccountType - if (!userAccountType) { - logger.error('No selectedProvider provided') - res.status(500).json({ message: 'No selectedProvider provided' }) - return - } - if (userAccountType === UserAccountType.UnlockAccount) { - logger.error('Creating a user with UnlockAccount type is not allowed') - res.status(500).json({ - message: 'Creating a user with UnlockAccount type is not allowed', - }) - return - } - const newUserUUID = await UserOperations.createUserAccount( - emailAddress, - selectedProvider as UserAccountType - ) - userUUID = newUserUUID - - await sendEmail({ - template: 'welcome', - recipient: emailAddress, - }) - } - - try { - const token = await issueUserToken({ - apiKeyName: config.coinbaseCloudApiKeyName as string, - privateKey: config.coinbaseCloudPrivateKey as string, - userID: userUUID as string, - }) - res.json({ token }) - } catch (error) { - logger.error( - 'Error issuing Coinbase WAAS token for user', - userUUID, - error.message - ) - res - .status(400) - .json({ message: 'Error issuing Coinbase WAAS token for user' }) - return - } -} - export const retrieveRecoveryPhrase = async ( req: Request, res: Response @@ -472,116 +378,10 @@ export const existNextAuth = async (request: Request, response: Response) => { return } -export const sendVerificationCode = async ( - request: Request, - response: Response -) => { - const { emailAddress } = request.params - const currentTime = new Date() - - try { - let verificationEntry = await VerificationCodes.findOne({ - where: { emailAddress }, - }) - - if ( - !verificationEntry || - verificationEntry.codeExpiration < currentTime || - verificationEntry.isCodeUsed - ) { - const { code, expiration } = generateVerificationCode() - - if (verificationEntry) { - await verificationEntry.update({ - code, - codeExpiration: expiration, - isCodeUsed: false, - token: crypto.randomUUID(), - tokenExpiration: new Date(Date.now() + 60 * 60 * 1000), - }) - } else { - verificationEntry = await VerificationCodes.create({ - emailAddress, - code, - codeExpiration: expiration, - token: crypto.randomUUID(), - tokenExpiration: new Date(Date.now() + 60 * 60 * 1000), - }) - } - } - - await sendEmail({ - template: 'nextAuthCode', - recipient: emailAddress, - params: { - code: verificationEntry.code, - }, - }) - - response.status(200).json({ - message: 'Email code sent', - }) - } catch (error) { - logger.error('Error sending verification code:', error) - response.status(500).send('Error sending verification code') - } -} - -export const verifyEmailCode = async (request: Request, response: Response) => { - const { emailAddress } = request.params - const { code } = request.body - - if (!emailAddress || !code) { - response.sendStatus(400).json({ message: 'Missing parameters' }) - return - } - - try { - const verificationEntry = await VerificationCodes.findOne({ - where: { emailAddress }, - }) - - if (!verificationEntry) { - response.status(404).json({ message: 'Verification code not found' }) - return - } - - const currentTime = new Date() - if ( - verificationEntry.code === code && - verificationEntry.codeExpiration > currentTime && - !verificationEntry.isCodeUsed - ) { - verificationEntry.update({ isCodeUsed: true }) - response.status(200).json({ - message: 'Verification successful', - token: verificationEntry.token, - }) - return - } else if (verificationEntry.codeExpiration <= currentTime) { - response.status(400).json({ message: 'Verification code has expired' }) - return - } else if (verificationEntry.isCodeUsed) { - response - .status(400) - .json({ message: 'Verification code has already been used' }) - return - } else { - response.status(400).json({ message: 'Invalid verification code' }) - return - } - } catch (error) { - logger.error('Error verifying email code:', error) - response.status(500).json({ message: 'Error verifying email code' }) - return - } -} - const UserController = { createUser, userCreationStatus, retrieveEncryptedPrivatekey, - retrieveWaasUuid, retrieveRecoveryPhrase, updateUser, updatePaymentDetails, @@ -593,8 +393,6 @@ const UserController = { eject, exist, existNextAuth, - sendVerificationCode, - verifyEmailCode, } export default UserController diff --git a/locksmith/src/routes/v2/user.ts b/locksmith/src/routes/v2/user.ts index d20e6b9797c..d6ced17d13e 100644 --- a/locksmith/src/routes/v2/user.ts +++ b/locksmith/src/routes/v2/user.ts @@ -1,21 +1,8 @@ import express from 'express' -import { captchaMiddleware } from '../../utils/middlewares/recaptchaMiddleware' import userController from '../../controllers/userController' const router: express.Router = express.Router({ mergeParams: true }) -router.post( - '/:emailAddress/:selectedProvider/waas', - captchaMiddleware, - userController.retrieveWaasUuid -) - router.get('/:emailAddress/existNextAuth', userController.existNextAuth) -router.get( - '/:emailAddress/send-verification-code', - captchaMiddleware, - userController.sendVerificationCode -) -router.post('/:emailAddress/verify-email-code', userController.verifyEmailCode) export default router diff --git a/locksmith/src/utils/verifyNextAuthToken.ts b/locksmith/src/utils/verifyNextAuthToken.ts deleted file mode 100644 index c5413fb299b..00000000000 --- a/locksmith/src/utils/verifyNextAuthToken.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { OAuth2Client } from 'google-auth-library' -import config from '../config/config' -import { UserAccountType } from '../controllers/userController' -import VerificationCodes from '../models/verificationCodes' - -export const verifyNextAuthToken = async ( - selectedProvider: UserAccountType, - email: string, - token: string -) => { - switch (selectedProvider) { - case UserAccountType.GoogleAccount: - return await verifyGoogleToken(email, token) - case UserAccountType.EmailCodeAccount: - return verifyEmailToken(email, token) - default: - return false - } -} - -export const verifyGoogleToken = async (email: string, token: string) => { - const client = new OAuth2Client() - let ticket - try { - ticket = await client.verifyIdToken({ - idToken: token, - audience: config.googleAuthClientId, - }) - } catch (e) { - console.error('Error verifying Google token', e) - return false - } - - const payload = ticket.getPayload() - - if (payload?.email === email) { - return true - } - - return false -} - -export const verifyEmailToken = async (email: string, token: string) => { - let verificationEntry - try { - verificationEntry = await VerificationCodes.findOne({ - where: { emailAddress: email, token: token }, - }) - } catch (e) { - console.error('Error verifying email token', e) - return false - } - - if (!verificationEntry) { - return false - } - - const currentTime = new Date() - if (verificationEntry.tokenExpiration > currentTime) { - return true - } else { - return false - } -} diff --git a/unlock-app/src/utils/getUserWaasUuid.ts b/unlock-app/src/utils/getUserWaasUuid.ts deleted file mode 100644 index 9c71fb162be..00000000000 --- a/unlock-app/src/utils/getUserWaasUuid.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { locksmith } from '~/config/locksmith' -import { UserAccountType } from './userAccountType' - -/** - * Given a user's email address, used provide and token retrieves their WAAS UUID. In the case of failure a rejected promise - * is returned to the caller. - * @param {*} emailAddress - * @param {*} provider - * @param {*} token - * @returns {Promise<*>} - */ -export const getUserWaasUuid = async ( - captcha: string, - emailAddress: string, - provider: UserAccountType, - token: string -) => { - try { - const response = await locksmith.getWaasToken( - captcha, - emailAddress, - provider, - { token } - ) - - const waasToken = await response.data.token - - return waasToken - } catch (error) { - console.log(error) - } -} diff --git a/yarn.lock b/yarn.lock index f2d7c5d982c..bcd22f49d9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3591,15 +3591,6 @@ __metadata: languageName: node linkType: hard -"@coinbase/waas-server-auth@npm:3.1.2": - version: 3.1.2 - resolution: "@coinbase/waas-server-auth@npm:3.1.2" - dependencies: - node-jose: "npm:^2.2.0" - checksum: 10/cc9ad7cae049bac65a9ccac545eabae283f7c1a448870cc1ad63481cb7ed53959849f21635c64439332142eade58154e91c23d9b9e163f25c9859fcfe1651e79 - languageName: node - linkType: hard - "@coinbase/wallet-sdk@npm:4.0.3": version: 4.0.3 resolution: "@coinbase/wallet-sdk@npm:4.0.3" @@ -18147,7 +18138,6 @@ __metadata: dependencies: "@ambire/signature-validator": "npm:1.4.1" "@aws-sdk/client-s3": "npm:3.703.0" - "@coinbase/waas-server-auth": "npm:3.1.2" "@logtail/node": "npm:0.5.2" "@logtail/winston": "npm:0.5.2" "@nuintun/qrcode": "npm:4.1.6" @@ -21605,13 +21595,6 @@ __metadata: languageName: node linkType: hard -"base64url@npm:^3.0.1": - version: 3.0.1 - resolution: "base64url@npm:3.0.1" - checksum: 10/a77b2a3a526b3343e25be424de3ae0aa937d78f6af7c813ef9020ef98001c0f4e2323afcd7d8b2d2978996bf8c42445c3e9f60c218c622593e5fdfd54a3d6e18 - languageName: node - linkType: hard - "base@npm:^0.11.1": version: 0.11.2 resolution: "base@npm:0.11.2" @@ -39238,7 +39221,7 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:1.3.1, node-forge@npm:^1, node-forge@npm:^1.2.1, node-forge@npm:^1.3.1": +"node-forge@npm:1.3.1, node-forge@npm:^1, node-forge@npm:^1.3.1": version: 1.3.1 resolution: "node-forge@npm:1.3.1" checksum: 10/05bab6868633bf9ad4c3b1dd50ec501c22ffd69f556cdf169a00998ca1d03e8107a6032ba013852f202035372021b845603aeccd7dfcb58cdb7430013b3daa8d @@ -39283,23 +39266,6 @@ __metadata: languageName: node linkType: hard -"node-jose@npm:^2.2.0": - version: 2.2.0 - resolution: "node-jose@npm:2.2.0" - dependencies: - base64url: "npm:^3.0.1" - buffer: "npm:^6.0.3" - es6-promise: "npm:^4.2.8" - lodash: "npm:^4.17.21" - long: "npm:^5.2.0" - node-forge: "npm:^1.2.1" - pako: "npm:^2.0.4" - process: "npm:^0.11.10" - uuid: "npm:^9.0.0" - checksum: 10/699f025ccc2f51370d3520af0e6268e9b8f103781e100f64efac82d14c33ab7c15907295bae7edc98d1e8a3d3e0f977f47aea03942a5b3d564185ac2213fb69f - languageName: node - linkType: hard - "node-mocks-http@npm:1.16.1": version: 1.16.1 resolution: "node-mocks-http@npm:1.16.1" @@ -40607,13 +40573,6 @@ __metadata: languageName: node linkType: hard -"pako@npm:^2.0.4": - version: 2.1.0 - resolution: "pako@npm:2.1.0" - checksum: 10/38a04991d0ec4f4b92794a68b8c92bf7340692c5d980255c92148da96eb3e550df7a86a7128b5ac0c65ecddfe5ef3bbe9c6dab13e1bc315086e759b18f7c1401 - languageName: node - linkType: hard - "pako@npm:~1.0.2, pako@npm:~1.0.5": version: 1.0.11 resolution: "pako@npm:1.0.11"