From 6dbcffcddb6d2c299789c0c838ab925c36935d78 Mon Sep 17 00:00:00 2001 From: Heiko Burkhardt Date: Wed, 22 Nov 2023 09:45:19 +0100 Subject: [PATCH] feat: siwe --- .../dsManager/steps/getBillboardProfile.ts | 1 + .../test/helper/mockUserProfile.ts | 1 + packages/lib/crypto/src/KeyCreation.test.ts | 9 ---- packages/lib/crypto/src/KeyCreation.ts | 20 +++++++-- packages/lib/profile/src/Profile.test.ts | 2 + packages/lib/profile/src/Profile.ts | 29 ++++++++++--- .../src/profileKeys/createProfileKeys.test.ts | 12 +++--- .../src/components/SignIn/bl.tsx | 41 ++++++++++--------- .../react/src/session/SignIn/signProfile.ts | 6 ++- .../src/session/SignIn/signProfileKeyPair.ts | 5 ++- 10 files changed, 79 insertions(+), 47 deletions(-) diff --git a/packages/billboard-client/src/service/dsManager/steps/getBillboardProfile.ts b/packages/billboard-client/src/service/dsManager/steps/getBillboardProfile.ts index abd04e51e..d2631ff19 100644 --- a/packages/billboard-client/src/service/dsManager/steps/getBillboardProfile.ts +++ b/packages/billboard-client/src/service/dsManager/steps/getBillboardProfile.ts @@ -24,6 +24,7 @@ export async function getBillboardProfile( const wallet = new ethers.Wallet(billboard.privateKey); const storageKeyCreationMessage = getStorageKeyCreationMessage( '0xca8f04fdc80d659997f69b02', + wallet.address, ); const storageKeySig = await wallet.signMessage( storageKeyCreationMessage, diff --git a/packages/billboard-client/test/helper/mockUserProfile.ts b/packages/billboard-client/test/helper/mockUserProfile.ts index df0c195e4..a5c710647 100644 --- a/packages/billboard-client/test/helper/mockUserProfile.ts +++ b/packages/billboard-client/test/helper/mockUserProfile.ts @@ -25,6 +25,7 @@ export const mockUserProfile = async ( }> => { const storageKeyCreationMessage = getStorageKeyCreationMessage( '0xca8f04fdc80d659997f69b02', + wallet.address, ); const storageKeySig = await wallet.signMessage(storageKeyCreationMessage); diff --git a/packages/lib/crypto/src/KeyCreation.test.ts b/packages/lib/crypto/src/KeyCreation.test.ts index 4e418d1f9..bd53693c4 100644 --- a/packages/lib/crypto/src/KeyCreation.test.ts +++ b/packages/lib/crypto/src/KeyCreation.test.ts @@ -8,15 +8,6 @@ import { getStorageKeyCreationMessage, } from './KeyCreation'; -test('should get a correct storage key creation message', async () => { - const message = await getStorageKeyCreationMessage('99'); - expect(message).toEqual( - 'Connect the dm3 app with your wallet.' + - ' Keys for secure communication are derived from the signature.' + - ' No paid transaction will be executed.\nNonce: 99', - ); -}); - test('should get a correct storage key', async () => { const sig = '0xb9b0a77f501c6db70c5e8f7d1e6be1642b8b7e897d681c69921569cb84b0a' + diff --git a/packages/lib/crypto/src/KeyCreation.ts b/packages/lib/crypto/src/KeyCreation.ts index 7039e2cbd..e9cd35a7b 100644 --- a/packages/lib/crypto/src/KeyCreation.ts +++ b/packages/lib/crypto/src/KeyCreation.ts @@ -25,11 +25,23 @@ export async function createSigningKeyPair(seed?: string): Promise { }; } -export function getStorageKeyCreationMessage(nonce: string) { +export function getStorageKeyCreationMessage(nonce: string, address: string) { + // TODO: during linked profile implementation these values should be fetched from env + const statement = + `Connect the DM3 MESSENGER with your wallet. Sign in with a signature. ` + + `Keys for secure communication are derived from this signature.\n\n` + + `(There is no paid transaction initiated. The signature is used off-chain only.)`; + const domain = 'dm3.chat'; + const uri = 'https://dm3.chat'; + const version = '1'; + return ( - `Connect the dm3 app with your wallet.` + - ` Keys for secure communication are derived from the signature.` + - ` No paid transaction will be executed.\nNonce: ${nonce}` + `${domain} wants you to sign in with your Ethereum account:\n` + + `${ethers.utils.getAddress(address)}\n\n` + + `${statement}\n\n` + + `URI: ${uri}\n` + + `Version: ${version}\n` + + `Nonce: ${nonce}` ); } diff --git a/packages/lib/profile/src/Profile.test.ts b/packages/lib/profile/src/Profile.test.ts index da7215c2c..89c492e2a 100644 --- a/packages/lib/profile/src/Profile.test.ts +++ b/packages/lib/profile/src/Profile.test.ts @@ -35,6 +35,7 @@ const getProfileData = async (): Promise<{ const createUserProfileMessage = getProfileCreationMessage( stringify(profile), + wallet.address, ); const signature = await wallet.signMessage(createUserProfileMessage); @@ -140,6 +141,7 @@ describe('Account', () => { const wallet = ethers.Wallet.createRandom(); const createUserProfileMessage = getProfileCreationMessage( stringify(profile), + wallet.address, ); const signature = await wallet.signMessage( createUserProfileMessage, diff --git a/packages/lib/profile/src/Profile.ts b/packages/lib/profile/src/Profile.ts index 7fc509964..813fd297c 100644 --- a/packages/lib/profile/src/Profile.ts +++ b/packages/lib/profile/src/Profile.ts @@ -45,11 +45,22 @@ export const PROFILE_RECORD_NAME = 'network.dm3.profile'; * signs a profile with an ethereum account key * @param stringifiedProfile stringified dm3 user profile object */ -export function getProfileCreationMessage(stringifiedProfile: string) { +export function getProfileCreationMessage( + stringifiedProfile: string, + address: string, +) { + const domain = 'dm3.chat'; + const uri = 'https://dm3.chat'; + const version = '1'; + return ( - `Please sign this message to link your dm3 profile with your Wallet. ` + - `(This signature will not trigger any transaction or cost gas fees.) ` + - `Your dm3 profile:\n\n${stringifiedProfile}` + `${domain} wants you register your dm3 profile with your Ethereum account:\n` + + `${ethers.utils.getAddress(address)}\n\n` + + `Register your dm3 profile. This is required only once!\n` + + `(There is no paid transaction initiated. The signature is used off-chain only.)\n\n` + + `URI: ${uri}\n` + + `Version: ${version}\n` + + `dm3 Profile: ${stringifiedProfile}` ); } @@ -116,6 +127,7 @@ export function checkUserProfileWithAddress( ): boolean { const createUserProfileMessage = getProfileCreationMessage( stringify(profile), + accountAddress, ); return ( @@ -192,12 +204,15 @@ export function isSameEnsName( async function createKeyPairsFromSig( sign: (msg: string) => Promise, - + address: string, nonce: string, storageKey?: string, ): Promise { if (!storageKey) { - const storageKeyCreationMessage = getStorageKeyCreationMessage(nonce); + const storageKeyCreationMessage = getStorageKeyCreationMessage( + nonce, + address, + ); const signature = await sign(storageKeyCreationMessage); const newStorageKey = await createStorageKey(signature); @@ -235,6 +250,7 @@ export async function createProfile( const keys = await createKeyPairsFromSig( (msg: string) => signer(msg, accountAddress), + accountAddress, nonce, storageKey, ); @@ -247,6 +263,7 @@ export async function createProfile( const profileCreationMessage = getProfileCreationMessage( stringify(profile), + accountAddress, ); const profileSig = await signer(profileCreationMessage, accountAddress); return { diff --git a/packages/lib/profile/src/profileKeys/createProfileKeys.test.ts b/packages/lib/profile/src/profileKeys/createProfileKeys.test.ts index a7b9c8dc9..6aeccdeca 100644 --- a/packages/lib/profile/src/profileKeys/createProfileKeys.test.ts +++ b/packages/lib/profile/src/profileKeys/createProfileKeys.test.ts @@ -10,7 +10,7 @@ test(`Should create keys`, async () => { '0xac58f2f021d6f148fd621b355edbd0ebadcf9682019015ef1219cf9c0c2ddc8b', ); - const nonceMsg = getStorageKeyCreationMessage(nonce); + const nonceMsg = getStorageKeyCreationMessage(nonce, wallet.address); const signedMessage = await wallet.signMessage(nonceMsg); const keys = await createProfileKeys( @@ -20,15 +20,15 @@ test(`Should create keys`, async () => { expect(keys).toEqual({ encryptionKeyPair: { - privateKey: 'g79U2C3cVkSHwrdDAF1klEdhdAtZXdspTBOoAohYPlQ=', - publicKey: 'PKz2kFF0zqaWD4/vIMiADZmGKcf4tKo5/Wq9KDPlBlo=', + privateKey: '0wMyWrdDXCfuwRq4nm6IHqZ7hMbPOb5DsTt1C85w+zE=', + publicKey: 'JBvXxZY4BOnKK4J2s42ZpAJaFd/nmB5Sq7EB+jfA6H8=', }, signingKeyPair: { - publicKey: 'Z35n4cFXhCdDHmLwVPYHwHwiJlYr+Ga0dbPWj8/mxAE=', privateKey: - 'mxwp2Ygys2U3ary7cL0dDbh6TwYl3nEeDVzEaFS01NZnfmfhwVeEJ0MeYvBU9gfAfCImViv4ZrR1s9aPz+bEAQ==', + 'ti4w8V+E6x4Z63XIMA9ZM0lXKhMjTaxP/qjARC8c4CxRQS5qo2AEYU+ZeFyB0bksaPBX1K5/QA/dregbbmFgQQ==', + publicKey: 'UUEuaqNgBGFPmXhcgdG5LGjwV9Suf0AP3a3oG25hYEE=', }, - storageEncryptionKey: 'mxwp2Ygys2U3ary7cL0dDbh6TwYl3nEeDVzEaFS01NY=', + storageEncryptionKey: 'ti4w8V+E6x4Z63XIMA9ZM0lXKhMjTaxP/qjARC8c4Cw=', storageEncryptionNonce: '0', }); }); diff --git a/packages/messenger-widget/src/components/SignIn/bl.tsx b/packages/messenger-widget/src/components/SignIn/bl.tsx index 22fb9f8cb..1662f7dbc 100644 --- a/packages/messenger-widget/src/components/SignIn/bl.tsx +++ b/packages/messenger-widget/src/components/SignIn/bl.tsx @@ -98,8 +98,8 @@ export const checkState = async ( const browserDataFile = state.connection.account && state.uiState.browserStorageBackup ? await localforage.getItem( - getBrowserStorageKey(state.connection.account.ensName), - ) + getBrowserStorageKey(state.connection.account.ensName), + ) : null; const isCollectingSignInData = @@ -171,7 +171,10 @@ export async function createKeyPairsFromSignature( throw Error('No eth address'); } - const storageKeyCreationMessage = getStorageKeyCreationMessage(nonce); + const storageKeyCreationMessage = getStorageKeyCreationMessage( + nonce, + ethAddress, + ); const signature = await personalSign( provider, @@ -347,7 +350,7 @@ export async function signProfile( ): Promise { try { const profileCreationMessage = - getProfileCreationMessage(stringifiedProfile); + getProfileCreationMessage(stringifiedProfile, address); return await personalSign(provider, address, profileCreationMessage); } catch (error: any) { const err = error?.message.split(':'); @@ -481,12 +484,12 @@ export async function getDatabase( }> { return profileExists ? getExistingDatebase( - storageLocation, - storageToken, - state, - dispatch, - setSignInBtnContent, - ) + storageLocation, + storageToken, + state, + dispatch, + setSignInBtnContent, + ) : createNewDatabase(state, setSignInBtnContent); } @@ -814,16 +817,16 @@ export async function getWeb3Provider(provider: unknown): Promise<{ }> { return provider ? { - provider: new ethers.providers.Web3Provider( - provider as - | ethers.providers.ExternalProvider - | ethers.providers.JsonRpcFetchFunc, - ), - connectionState: ConnectionState.AccountConnectReady, - } + provider: new ethers.providers.Web3Provider( + provider as + | ethers.providers.ExternalProvider + | ethers.providers.JsonRpcFetchFunc, + ), + connectionState: ConnectionState.AccountConnectReady, + } : { - connectionState: ConnectionState.ConnectionRejected, - }; + connectionState: ConnectionState.ConnectionRejected, + }; } function handleNewProvider( diff --git a/packages/react/src/session/SignIn/signProfile.ts b/packages/react/src/session/SignIn/signProfile.ts index f725090c4..57b34ade2 100644 --- a/packages/react/src/session/SignIn/signProfile.ts +++ b/packages/react/src/session/SignIn/signProfile.ts @@ -9,8 +9,10 @@ export async function signProfile( stringifiedProfile: string, ): Promise { try { - const profileCreationMessage = - getProfileCreationMessage(stringifiedProfile); + const profileCreationMessage = getProfileCreationMessage( + stringifiedProfile, + address, + ); return await personalSign(provider, address, profileCreationMessage); } catch (e) { throw Error("Can't signIn profile"); diff --git a/packages/react/src/session/SignIn/signProfileKeyPair.ts b/packages/react/src/session/SignIn/signProfileKeyPair.ts index bca870f6d..3353f7619 100644 --- a/packages/react/src/session/SignIn/signProfileKeyPair.ts +++ b/packages/react/src/session/SignIn/signProfileKeyPair.ts @@ -18,7 +18,10 @@ export async function createKeyPairsFromSig( throw Error('No eth address'); } - const storageKeyCreationMessage = getStorageKeyCreationMessage(nonce); + const storageKeyCreationMessage = getStorageKeyCreationMessage( + nonce, + ethAddress, + ); const signature = await personalSign( provider,