diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js --- a/keyserver/src/responders/user-responders.js +++ b/keyserver/src/responders/user-responders.js @@ -1,6 +1,7 @@ // @flow import invariant from 'invariant'; +import { SiweMessage } from 'siwe'; import t from 'tcomb'; import bcrypt from 'twin-bcrypt'; @@ -24,6 +25,7 @@ logInActionSources, } from 'lib/types/account-types'; import { defaultNumberPerThread } from 'lib/types/message-types'; +import type { SIWEAuthRequest, SIWEMessage } from 'lib/types/siwe-types.js'; import type { SubscriptionUpdateRequest, SubscriptionUpdateResponse, @@ -32,6 +34,7 @@ import { ServerError } from 'lib/utils/errors'; import { values } from 'lib/utils/objects'; import { promiseAll } from 'lib/utils/promises'; +import { isValidSIWEMessage } from 'lib/utils/siwe-utils.js'; import { tShape, tPlatformDetails, @@ -304,6 +307,14 @@ async function siweAuthResponder(viewer: Viewer, input: any): Promise { await validateInput(viewer, siweAuthRequestInputValidator, input); + const request: SIWEAuthRequest = input; + const { message } = request; + + // 1. Ensure that `message` is a well formed Comm SIWE Auth message. + const siweMessage: SIWEMessage = new SiweMessage(message); + if (!isValidSIWEMessage(siweMessage)) { + throw new ServerError('invalid_parameters'); + } return false; } diff --git a/landing/siwe.react.js b/landing/siwe.react.js --- a/landing/siwe.react.js +++ b/landing/siwe.react.js @@ -23,6 +23,7 @@ import { publicProvider } from 'wagmi/providers/public'; import type { SIWEWebViewMessage } from 'lib/types/siwe-types'; +import { siweStatement } from 'lib/utils/siwe-utils.js'; import { SIWENonceContext } from './siwe-nonce-context.js'; import css from './siwe.css'; @@ -66,11 +67,7 @@ async function signInWithEthereum(address: string, signer, nonce: string) { invariant(nonce, 'nonce must be present in signInWithEthereum'); - const message = createSiweMessage( - address, - 'By continuing, I accept the Comm Terms of Service: https://comm.app/terms', - nonce, - ); + const message = createSiweMessage(address, siweStatement, nonce); const signature = await signer.signMessage(message); postMessageToNativeWebView({ type: 'siwe_success', diff --git a/lib/utils/siwe-utils.js b/lib/utils/siwe-utils.js --- a/lib/utils/siwe-utils.js +++ b/lib/utils/siwe-utils.js @@ -1,5 +1,8 @@ // @flow +import type { SIWEMessage } from '../types/siwe-types.js'; +import { isDev } from './dev-utils.js'; + const siweNonceRegex: RegExp = /^[a-zA-Z0-9]{17}$/; function isValidSIWENonce(candidate: string): boolean { return siweNonceRegex.test(candidate); @@ -10,4 +13,28 @@ return ethereumAddressRegex.test(candidate); } -export { isValidSIWENonce, isValidEthereumAddress }; +const siweStatement: string = + 'By continuing, I accept the Comm Terms of Service: https://comm.app/terms'; + +const expectedDomain = isDev ? 'localhost:3000' : 'comm.app'; +const expectedURI = isDev ? 'http://localhost:3000' : 'https://comm.app'; + +// Verify that the SIWEMessage is a well formed Comm SIWE Auth message. +function isValidSIWEMessage(candidate: SIWEMessage): boolean { + return ( + candidate.statement === siweStatement && + candidate.version === '1' && + candidate.chainId === 1 && + candidate.domain === expectedDomain && + candidate.uri === expectedURI && + isValidSIWENonce(candidate.nonce) && + isValidEthereumAddress(candidate.address) + ); +} + +export { + siweStatement, + isValidSIWENonce, + isValidEthereumAddress, + isValidSIWEMessage, +};