Skip to content

Commit 7f6a5e5

Browse files
authored
Merge pull request #397 from oasisprotocol/ml/fix-account-switching
Fix account switching
2 parents 84313d8 + 7585a95 commit 7f6a5e5

File tree

3 files changed

+109
-66
lines changed

3 files changed

+109
-66
lines changed

.changelog/397.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix account switching

src/components/RainbowKitProviderWithAuth/index.tsx

Lines changed: 75 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { sapphire, sapphireTestnet } from 'viem/chains'
1414
import { AccountAvatar } from '../AccountAvatar'
1515
import type { FC, PropsWithChildren } from 'react'
1616
import { useNetwork } from '../../hooks/useNetwork.ts'
17+
import { useEffect, useMemo, useRef } from 'react'
1718

1819
const rainbowKitTheme: Theme = {
1920
...darkTheme(),
@@ -29,79 +30,89 @@ export const RainbowKitProviderWithAuth: FC<PropsWithChildren> = ({ children })
2930
const chainId = useNetwork('mainnet') === 'mainnet' ? sapphire.id : sapphireTestnet.id
3031
const { chainModalOpen, openChainModal } = useChainModal()
3132

32-
const authenticationAdapter = createAuthenticationAdapter({
33-
getNonce: async () => {
34-
return await fetchNonce(address!)
35-
},
33+
// Use ref to avoid recreating the adapter when modal state changes
34+
const chainModalOpenRef = useRef(chainModalOpen)
35+
useEffect(() => {
36+
chainModalOpenRef.current = chainModalOpen
37+
}, [chainModalOpen])
3638

37-
createMessage: ({ nonce, address, chainId }) => {
38-
const hostname = window.location.hostname
39-
let domain: string
39+
const authenticationAdapter = useMemo(
40+
() =>
41+
createAuthenticationAdapter({
42+
getNonce: async () => {
43+
return await fetchNonce(address!)
44+
},
4045

41-
if (hostname === 'rofl.app') {
42-
domain = 'rofl.app'
43-
} else if (hostname === 'stg.rofl.app') {
44-
domain = 'stg.rofl.app'
45-
} else if (
46-
hostname === 'dev.rofl.app' ||
47-
hostname.endsWith('.rofl-app.pages.dev') ||
48-
hostname === 'localhost'
49-
) {
50-
domain = 'dev.rofl.app'
51-
} else {
52-
domain = 'rofl.app'
53-
}
46+
createMessage: ({ nonce, address, chainId }) => {
47+
const hostname = window.location.hostname
48+
let domain: string
5449

55-
const uri = `https://${domain}`
56-
const statement = 'Sign in to ROFL App Backend'
50+
if (hostname === 'rofl.app') {
51+
domain = 'rofl.app'
52+
} else if (hostname === 'stg.rofl.app') {
53+
domain = 'stg.rofl.app'
54+
} else if (
55+
hostname === 'dev.rofl.app' ||
56+
hostname.endsWith('.rofl-app.pages.dev') ||
57+
hostname === 'localhost'
58+
) {
59+
domain = 'dev.rofl.app'
60+
} else {
61+
domain = 'rofl.app'
62+
}
5763

58-
return createSiweMessage({
59-
address,
60-
domain,
61-
statement,
62-
uri,
63-
version: '1',
64-
chainId,
65-
issuedAt: new Date(),
66-
nonce,
67-
})
68-
},
64+
const uri = `https://${domain}`
65+
const statement = 'Sign in to ROFL App Backend'
6966

70-
verify: async ({ message, signature }) => {
71-
try {
72-
if (currentChainId !== chainId) {
73-
if (!chainModalOpen) {
74-
openChainModal?.()
75-
}
76-
return false
77-
}
67+
return createSiweMessage({
68+
address,
69+
domain,
70+
statement,
71+
uri,
72+
version: '1',
73+
chainId,
74+
issuedAt: new Date(),
75+
nonce,
76+
})
77+
},
78+
79+
verify: async ({ message, signature }) => {
80+
try {
81+
if (currentChainId !== chainId) {
82+
if (!chainModalOpenRef.current) {
83+
openChainModal?.()
84+
}
85+
return false
86+
}
7887

79-
const token = await login({ message, signature })
88+
const token = await login({ message, signature })
8089

81-
try {
82-
window.localStorage.setItem('jwt', token)
83-
window.dispatchEvent(new Event('storage'))
90+
try {
91+
window.localStorage.setItem('jwt', token)
92+
window.dispatchEvent(new Event('storage'))
8493

85-
return true
86-
} catch {
87-
// Ignore failures
88-
}
89-
return false
90-
} catch (error) {
91-
console.error('Authentication failed:', error)
92-
return false
93-
}
94-
},
94+
return true
95+
} catch {
96+
// Ignore failures
97+
}
98+
return false
99+
} catch (error) {
100+
console.error('Authentication failed:', error)
101+
return false
102+
}
103+
},
95104

96-
signOut: async () => {
97-
try {
98-
window.localStorage.removeItem('jwt')
99-
window.dispatchEvent(new Event('storage'))
100-
} catch {
101-
// Ignore failures
102-
}
103-
},
104-
})
105+
signOut: async () => {
106+
try {
107+
window.localStorage.removeItem('jwt')
108+
window.dispatchEvent(new Event('storage'))
109+
} catch {
110+
// Ignore failures
111+
}
112+
},
113+
}),
114+
[address, currentChainId, chainId, openChainModal],
115+
)
105116

106117
return (
107118
<RainbowKitAuthenticationProvider adapter={authenticationAdapter} status={status}>

src/contexts/RoflAppBackendAuth/Provider.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,38 @@ export function RoflAppBackendAuthProvider({ children }: { children: ReactNode }
4242
}, [isInitialLoad, isConnected, token])
4343

4444
useEffect(() => {
45+
// Wallet disconnected - clear token
46+
if (!address) {
47+
setToken(null)
48+
setIsTokenExpired(false)
49+
return
50+
}
51+
4552
const currentToken = window.localStorage.getItem('jwt')
46-
if (currentToken && address) {
53+
if (currentToken) {
4754
const expired = isJWTExpired(currentToken, address)
4855
setIsTokenExpired(expired)
56+
57+
try {
58+
const jwt = JSON.parse(atob(currentToken.split('.')[1]))
59+
const tokenAddress = jwt.address?.toLowerCase()
60+
// Clear token if address mismatch (user switched accounts)
61+
if (tokenAddress && tokenAddress !== address.toLowerCase()) {
62+
window.localStorage.removeItem('jwt')
63+
setToken(null)
64+
setIsTokenExpired(false)
65+
} else if (expired) {
66+
// Clear expired token with matching address
67+
window.localStorage.removeItem('jwt')
68+
setToken(null)
69+
setIsTokenExpired(false)
70+
}
71+
} catch {
72+
// Invalid token - clear it
73+
window.localStorage.removeItem('jwt')
74+
setToken(null)
75+
setIsTokenExpired(false)
76+
}
4977
}
5078
}, [address, token])
5179

@@ -56,7 +84,10 @@ export function RoflAppBackendAuthProvider({ children }: { children: ReactNode }
5684
setIsTokenExpired(expired)
5785

5886
if (expired) {
59-
// Token expired, requiring re-authentication
87+
// Clear expired token
88+
window.localStorage.removeItem('jwt')
89+
setToken(null)
90+
setIsTokenExpired(false)
6091
} else if (currentToken !== token) {
6192
setToken(currentToken)
6293
setIsTokenExpired(false)

0 commit comments

Comments
 (0)