Skip to content

Commit

Permalink
feat(cab): Use new verifiers with custom domains, update torus libs (#…
Browse files Browse the repository at this point in the history
…4942)

### Description

Kind of a two-birds-one-stone PR, both for
[ACT-875](https://linear.app/valora/issue/ACT-875/use-a-custom-domain-with-auth0)
and
[ACT-876](https://linear.app/valora/issue/ACT-876/refactor-gettorusprivatekey-to-use-updated-torusweb3auth-dependencies).
Basically the work that led to this PR progressed as follows:
* Updated Auth0 to use custom domains for prod/dev
* Tried to update our custom verifiers in web3auth dashboard to use new
Auth0 domains
  * Our existing verifiers were nowhere to be found in the dashboard
* Created new verifiers using the newest Sapphire main/devnets (as
opposed to deprecated Cyan network, which our previous verifiers were
on)
* Had trouble adapting our existing wallet code to integrate with the
new verifiers
* Updated `@torus/*` libraries and referred to their docs for how to
migrate, was able to connect to new verifiers
* Was able to go through backup + restore using custom Auth0 domains
_and_ new verifiers on Alfajores.

This fulfills the requirements for both ACT-875 and ACT-876 AFAICT. I
haven't yet tried to directly use the web3auth libs _instead_ of the
torus libs; I'll give it a shot, but if it doesn't seem to work, the
changes in this ticket should also suffice.

### Test plan

Manual tested.

### Related issues

- Fixes
[ACT-875](https://linear.app/valora/issue/ACT-875/use-a-custom-domain-with-auth0)
and
[ACT-876](https://linear.app/valora/issue/ACT-876/refactor-gettorusprivatekey-to-use-updated-torusweb3auth-dependencies).

### Backwards compatibility

This is not backwards compatible, in the sense that someone who's backed
up will not be able to restore, but this shouldn't matter since we
haven't gone live with CAB yet.

### Network scalability

If a new NetworkId and/or Network are added in the future, the changes
in this PR will:

- [x] Continue to work without code changes, OR trigger a compilation
error (guaranteeing we find it when a new network is added)

---------

Co-authored-by: Tom McGuire <Mcgtom10@gmail.com>
Co-authored-by: Jacob Waterman <jacobrwaterman@gmail.com>
  • Loading branch information
3 people committed Feb 23, 2024
1 parent b8156cc commit e916b99
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .env.alfajores
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ APP_DISPLAY_NAME=Alfajores
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.alfajores.plist
DYNAMIC_DOWNLOAD_LINK=https://vlra.app/invite
SENTRY_ENABLED=true
AUTH0_DOMAIN=dev-pgky8jht288zqgkm.us.auth0.com
AUTH0_DOMAIN=auth.alfajores.valora.xyz
2 changes: 1 addition & 1 deletion .env.alfajoresdev
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ APP_DISPLAY_NAME=Alfajores (dev)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.alfajoresdev.plist
DYNAMIC_DOWNLOAD_LINK=https://vlra.app/invite
SENTRY_ENABLED=false
AUTH0_DOMAIN=dev-pgky8jht288zqgkm.us.auth0.com
AUTH0_DOMAIN=auth.alfajores.valora.xyz
2 changes: 1 addition & 1 deletion .env.alfajoresnightly
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ APP_DISPLAY_NAME=Alfajores (nightly)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.alfajoresnightly.plist
DYNAMIC_DOWNLOAD_LINK=https://vlra.app/invite
SENTRY_ENABLED=true
AUTH0_DOMAIN=dev-pgky8jht288zqgkm.us.auth0.com
AUTH0_DOMAIN=auth.alfajores.valora.xyz
2 changes: 1 addition & 1 deletion .env.mainnet
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ APP_DISPLAY_NAME=Valora
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.mainnet.plist
DYNAMIC_DOWNLOAD_LINK=https://vlra.app/invite
SENTRY_ENABLED=true
AUTH0_DOMAIN=valora.us.auth0.com
AUTH0_DOMAIN=auth.valora.xyz
2 changes: 1 addition & 1 deletion .env.mainnetdev
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ APP_DISPLAY_NAME=Valora (dev)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.mainnetdev.plist
DYNAMIC_DOWNLOAD_LINK=https://vlra.app/invite
SENTRY_ENABLED=false
AUTH0_DOMAIN=valora.us.auth0.com
AUTH0_DOMAIN=auth.valora.xyz
2 changes: 1 addition & 1 deletion .env.mainnetnightly
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ APP_DISPLAY_NAME=Valora (nightly)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.mainnetnightly.plist
DYNAMIC_DOWNLOAD_LINK=https://vlra.app/invite
SENTRY_ENABLED=true
AUTH0_DOMAIN=valora.us.auth0.com
AUTH0_DOMAIN=auth.valora.xyz
2 changes: 1 addition & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ APP_DISPLAY_NAME=Celo (test)
IOS_GOOGLE_SERVICE_PLIST=GoogleService-Info.alfajores.plist
SENTRY_ENABLED=false
SUPERCHARGE_AVAILABLE_REWARDS_URL=
AUTH0_DOMAIN=dev-pgky8jht288zqgkm.us.auth0.com
AUTH0_DOMAIN=auth.alfajores.valora.xyz
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@
"@sentry/react-native": "^4.15.2",
"@sentry/types": "^7.101.1",
"@th3rdwave/react-navigation-bottom-sheet": "^0.2.7",
"@toruslabs/fetch-node-details": "^8.1.1",
"@toruslabs/torus.js": "^6.4.1",
"@toruslabs/constants": "^13.1.0",
"@toruslabs/fetch-node-details": "^13.1.1",
"@toruslabs/torus.js": "^12.1.1",
"@walletconnect/react-native-compat": "2.10.0",
"@walletconnect/utils": "2.10.0",
"@walletconnect/web3wallet": "1.9.0",
Expand Down
Binary file modified secrets.json.enc
Binary file not shown.
11 changes: 7 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { LoggerLevel } from 'src/utils/LoggerLevels'
// eslint-disable-next-line import/no-relative-packages
import * as secretsFile from '../secrets.json'
import { ONE_HOUR_IN_MILLIS } from './utils/time'

import { TORUS_SAPPHIRE_NETWORK } from '@toruslabs/constants'
export * from 'src/brandingConfig'

// extract secrets from secrets.json
Expand Down Expand Up @@ -92,9 +92,10 @@ export const STATSIG_ENV = {
export const E2E_TEST_STATSIG_ID = 'e2e_test_statsig_id'

// Keyless backup settings
export const TORUS_NETWORK = DEFAULT_TESTNET === 'mainnet' ? 'cyan' : 'testnet'
export const TORUS_SIGNER_BASE_URL =
TORUS_NETWORK === 'cyan' ? 'https://signer-polygon.tor.us' : 'https://signer.tor.us'
export const TORUS_NETWORK =
DEFAULT_TESTNET === 'mainnet'
? TORUS_SAPPHIRE_NETWORK.SAPPHIRE_MAINNET
: TORUS_SAPPHIRE_NETWORK.SAPPHIRE_DEVNET

// FEATURE FLAGS
export const FIREBASE_ENABLED = stringToBoolean(Config.FIREBASE_ENABLED || 'true')
Expand All @@ -104,6 +105,8 @@ export const SENTRY_ENABLED = stringToBoolean(Config.SENTRY_ENABLED || 'false')
export const SUPERCHARGE_AVAILABLE_REWARDS_URL = Config.SUPERCHARGE_AVAILABLE_REWARDS_URL

// SECRETS
export const WEB3AUTH_CLIENT_ID = keyOrUndefined(secretsFile, DEFAULT_TESTNET, 'WEB3AUTH_CLIENT_ID')

export const ALCHEMY_ETHEREUM_API_KEY = keyOrUndefined(
secretsFile,
DEFAULT_TESTNET,
Expand Down
5 changes: 4 additions & 1 deletion src/keylessBackup/saga.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ describe('keylessBackup saga', () => {
const mockTorusKeyshare = 'my-torus-keyshare'
await expectSaga(handleGoogleSignInCompleted, googleSignInCompleted({ idToken: mockJwt }))
.provide([
[call(getTorusPrivateKey, { verifier: 'valora-auth0', jwt: mockJwt }), mockTorusKeyshare],
[
call(getTorusPrivateKey, { verifier: 'valora-cab-auth0', jwt: mockJwt }),
mockTorusKeyshare,
],
])
.put(torusKeyshareIssued({ keyshare: mockTorusKeyshare }))
.run()
Expand Down
2 changes: 1 addition & 1 deletion src/keylessBackup/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export function* handleGoogleSignInCompleted({
}: ReturnType<typeof googleSignInCompleted>) {
// Note: this is done async while the user verifies their phone number.
try {
const torusPrivateKey = yield* call(getTorusPrivateKey, { verifier: 'valora-auth0', jwt })
const torusPrivateKey = yield* call(getTorusPrivateKey, { verifier: 'valora-cab-auth0', jwt })
yield* put(torusKeyshareIssued({ keyshare: torusPrivateKey }))
} catch (error) {
Logger.error(TAG, 'Error getting Torus private key from auth0 jwt', error)
Expand Down
72 changes: 72 additions & 0 deletions src/keylessBackup/web3auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { getTorusPrivateKey } from './web3auth'
import Torus from '@toruslabs/torus.js'

jest.mock('@toruslabs/torus.js')
jest.mock('@toruslabs/fetch-node-details', () => {
return () => {
return {
getNodeDetails: jest.fn().mockReturnValue({
torusNodeEndpoints: 'foo',
torusNodePub: 'bar',
torusIndexes: 'baz',
}),
}
}
})

// default JWT from https://jwt.io/
const MOCK_JWT =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'

describe('getTorusPrivateKey', () => {
let mockRetrieveShares: jest.Mock
let mockGetPublicAddress: jest.Mock

beforeEach(() => {
jest.resetAllMocks()

mockRetrieveShares = jest.fn().mockReturnValue({
finalKeyData: {
evmAddress: 'Some EVM address',
privKey: 'some private key',
},
})
mockGetPublicAddress = jest.fn().mockReturnValue({
finalKeyData: {
evmAddress: 'Some EVM address',
},
})
jest.mocked(Torus).mockReturnValue({
getPublicAddress: mockGetPublicAddress,
retrieveShares: mockRetrieveShares,
} as any)
})

it('should retrun private key successfully', async () => {
const result = await getTorusPrivateKey({ verifier: 'verifier', jwt: MOCK_JWT })
expect(result).toEqual('some private key')
})

it('should throw when keyshare address does not match torus address', async () => {
mockRetrieveShares.mockReturnValue({
finalKeyData: {
evmAddress: 'Some different EVM address',
privKey: 'some private key',
},
})
await expect(getTorusPrivateKey({ verifier: 'verifier', jwt: MOCK_JWT })).rejects.toThrow(
'sharesEthAddressLower does not match torusPubKey'
)
})

it('should throw when private key missing from share data', async () => {
mockRetrieveShares.mockReturnValue({
finalKeyData: {
evmAddress: 'Some EVM address',
},
})
await expect(getTorusPrivateKey({ verifier: 'verifier', jwt: MOCK_JWT })).rejects.toThrow(
'private key missing from share data'
)
})
})
27 changes: 9 additions & 18 deletions src/keylessBackup/web3auth.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
import jwtDecode from 'jwt-decode'
import Logger from 'src/utils/Logger'
import { TORUS_NETWORK, TORUS_SIGNER_BASE_URL } from 'src/config'
import { TORUS_NETWORK, WEB3AUTH_CLIENT_ID } from 'src/config'
import Torus from '@toruslabs/torus.js'
import NodeDetailManager from '@toruslabs/fetch-node-details'

const TAG = 'keylessBackup/torus'

// TODO update to use the latest Torus/Web3Auth API and add test coverage https://linear.app/valora/issue/ACT-876
// note: @toruslabs/torus.js and @toruslabs/fetch-node-details currently have empty mocks in jest_setup.ts
export async function getTorusPrivateKey({ verifier, jwt }: { verifier: string; jwt: string }) {
// largely copied from CustomAuth triggerLogin
Logger.debug(TAG, `decoding jwt ${jwt}`)
const sub = jwtDecode<{ sub: string }>(jwt).sub
const nodeDetailManager = new NodeDetailManager({
network: TORUS_NETWORK,
proxyAddress:
TORUS_NETWORK === 'cyan'
? NodeDetailManager.PROXY_ADDRESS_CYAN
: NodeDetailManager.PROXY_ADDRESS_TESTNET,
})
const torus = new Torus({
enableOneKey: false, // same as default from CustomAuth
metadataHost: 'https://metadata.tor.us',
allowHost: `${TORUS_SIGNER_BASE_URL}/api/allow`,
signerHost: `${TORUS_SIGNER_BASE_URL}/api/sign`,
legacyMetadataHost: 'https://metadata.tor.us',
network: TORUS_NETWORK,
clientId: WEB3AUTH_CLIENT_ID,
})
Logger.debug(TAG, `getting node details for verifier ${verifier} and sub ${sub}`)
const { torusNodeEndpoints, torusNodePub, torusIndexes } = await nodeDetailManager.getNodeDetails(
Expand All @@ -50,13 +42,12 @@ export async function getTorusPrivateKey({ verifier, jwt }: { verifier: string;
jwt
)
Logger.debug(TAG, `got shares of private key`)
const sharesEthAddressLower = shares.ethAddress.toLowerCase()
if (
typeof torusPubKey === 'string'
? sharesEthAddressLower !== torusPubKey.toLowerCase()
: sharesEthAddressLower !== torusPubKey.address.toLowerCase()
) {
const sharesEthAddressLower = shares.finalKeyData.evmAddress?.toLowerCase()
if (sharesEthAddressLower !== torusPubKey.finalKeyData.evmAddress.toLowerCase()) {
throw new Error('sharesEthAddressLower does not match torusPubKey')
}
return shares.privKey.toString()
if (!shares.finalKeyData.privKey) {
throw new Error('private key missing from share data')
}
return shares.finalKeyData.privKey
}

0 comments on commit e916b99

Please sign in to comment.