diff --git a/apps/laboratory/src/pages/library/multichain-siwx.tsx b/apps/laboratory/src/pages/library/multichain-siwx.tsx index 9c9a5e53cb..6623e90b6a 100644 --- a/apps/laboratory/src/pages/library/multichain-siwx.tsx +++ b/apps/laboratory/src/pages/library/multichain-siwx.tsx @@ -8,7 +8,7 @@ import { AppKitButtons } from '../../components/AppKitButtons' import { HuobiWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets' import { MultiChainTestsEthersSolana } from '../../components/MultiChainTestsEthersSolana' import { mainnet } from '@reown/appkit/networks' -import { DefaultSIWX } from '@reown/appkit-siwx' +import { CloudAuth, DefaultSIWX } from '@reown/appkit-siwx' const networks = ConstantsUtil.AllNetworks @@ -28,7 +28,9 @@ const modal = createAppKit({ }, termsConditionsUrl: 'https://reown.com/terms-of-service', privacyPolicyUrl: 'https://reown.com/privacy-policy', - siwx: new DefaultSIWX() + siwx: new DefaultSIWX({ + storage: new CloudAuth() + }) }) ThemeStore.setModal(modal) diff --git a/packages/siwx/src/storages/CloudAuth.ts b/packages/siwx/src/storages/CloudAuth.ts new file mode 100644 index 0000000000..581df6cae9 --- /dev/null +++ b/packages/siwx/src/storages/CloudAuth.ts @@ -0,0 +1,101 @@ +import { + ApiController, + BlockchainApiController, + type SIWXMessage, + type SIWXSession +} from '@reown/appkit-core' +import type { SIWXStorage } from '../core/SIWXStorage.js' +import { ConstantsUtil, type CaipNetworkId } from '@reown/appkit-common' + +export class CloudAuth implements SIWXStorage { + add(session: SIWXSession): Promise { + return this.request('authenticate', { + message: session.message, + signature: session.signature, + clientId: BlockchainApiController.state.clientId + }) + } + + delete(_chainId: string, _address: string): Promise { + return this.request('sign-out', undefined) + } + + async get(chainId: CaipNetworkId, address: string): Promise { + try { + const siweSession = await this.request('me', undefined) + const siweCaipNetworkId = `eip155:${siweSession?.chainId}` + + if (!siweSession || siweCaipNetworkId !== chainId || siweSession.address !== address) { + return [] + } + + const session: SIWXSession = { + data: { + accountAddress: siweSession.address, + chainId: siweCaipNetworkId + } as SIWXMessage.Data, + message: '', + signature: '' + } + + return [session] + } catch { + return [] + } + } + + set(_sessions: SIWXSession[]): Promise { + throw new Error('Set is not available for CloudAuth') + } + + private async request( + key: Key, + params: CloudAuth.Requests[Key]['body'] + ): Promise { + const response = await fetch(`${ConstantsUtil.W3M_API_URL}/auth/v1/${key}`, { + method: RequestMethod[key], + body: JSON.stringify(params), + headers: ApiController._getApiHeaders() satisfies { 'x-project-id': string } + }) + + if (response.headers.get('content-type')?.includes('application/json')) { + return response.json() + } + + throw new Error(await response.text()) + } +} + +const RequestMethod = { + nonce: 'GET', + me: 'GET', + authenticate: 'POST', + 'update-user-metadata': 'PATCH', + 'sign-out': 'POST' +} satisfies { [key in CloudAuth.RequestKey]: CloudAuth.Requests[key]['method'] } + +export namespace CloudAuth { + export type Request = { + method: Method + body: Params + response: Response + } + + export type Requests = { + nonce: Request<'GET', undefined, { nonce: string }> + me: Request<'GET', undefined, { address: string; chainId: string }> + authenticate: Request< + 'POST', + { + message: string + signature: string + clientId?: string | null + }, + never + > + 'update-user-metadata': Request<'PATCH', Record, unknown> + 'sign-out': Request<'POST', undefined, never> + } + + export type RequestKey = keyof Requests +} diff --git a/packages/siwx/src/storages/index.ts b/packages/siwx/src/storages/index.ts index 9af82f0252..893e6dd407 100644 --- a/packages/siwx/src/storages/index.ts +++ b/packages/siwx/src/storages/index.ts @@ -1 +1,2 @@ export * from './LocalStorage.js' +export * from './CloudAuth.js'