Skip to content

Commit a492b45

Browse files
committed
Make sure logging in's side-effects are reflected across multiple tabs
1 parent 3bc71e3 commit a492b45

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

templates/Foobara/Auth/LoginCommand.ts.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default class LoginCommand<Inputs, Result, Error extends FoobaraError<any
77
extends RemoteCommand<Inputs, Result, Error> {
88
async _handleResponse (response: Response): Promise<Outcome<Result, Error>> {
99
if (response.ok) {
10-
const accessToken: string | null = response.headers.get('X-Access-Token')
10+
const accessToken: string | null = response.headers.get('x-access-token')
1111

1212
if (accessToken != null) {
1313
handleLogin(this.urlBase, accessToken)

templates/Foobara/Auth/utils/accessTokens.ts.erb

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type Query from '../../../base/Query'
22
import type RemoteCommand from '../../../base/RemoteCommand'
33
import { forEachQuery } from '../../../base/QueryCache'
44

5+
// TODO: Why is this in here? It seems general-purpose not auth-specific
56
function dirtyAllQueries () {
67
forEachQuery((query: Query<RemoteCommand<any, any, any>>) => {
78
query.setDirty()
@@ -10,29 +11,53 @@ function dirtyAllQueries () {
1011

1112
const accessTokens = new Map<string, string>()
1213

14+
interface AuthMessage {
15+
type: 'login' | 'logout'
16+
baseUrl: string
17+
accessToken?: string
18+
}
19+
20+
const login = (baseUrl: string, accessToken: string): void => {
21+
accessTokens.set(baseUrl, accessToken)
22+
dirtyAllQueries()
23+
}
24+
1325
const logout = (urlBase: string): void => {
1426
accessTokens.delete(urlBase)
1527
dirtyAllQueries()
1628
}
29+
30+
// These functions gets overridden with a form that broadcasts the event if BroadcastChannel is available
31+
let handleLogin: (baseUrl: string, accessToken: string) => void = login
1732
let handleLogout: (baseUrl: string) => void = logout
1833

1934
const tokenForUrl = (baseUrl: string): string | undefined => accessTokens.get(baseUrl)
20-
const handleLogin: (baseUrl: string, accessToken: string) => void = (baseUrl, accessToken) => {
21-
accessTokens.set(baseUrl, accessToken)
22-
dirtyAllQueries()
23-
}
2435

2536
if (typeof BroadcastChannel !== 'undefined') {
2637
const logoutChannel = new BroadcastChannel('foobara-auth-events')
2738

28-
logoutChannel.addEventListener('message', (event: MessageEvent<string>) => {
29-
accessTokens.delete(event.data)
30-
deleteAllQueries()
39+
logoutChannel.addEventListener('message', (event: MessageEvent<AuthMessage>) => {
40+
const { type, baseUrl, accessToken } = event.data
41+
42+
if (type === 'login') {
43+
if (accessToken != null) {
44+
login(baseUrl, accessToken)
45+
}
46+
} else if (type === 'logout') {
47+
logout(baseUrl)
48+
} else {
49+
throw new Error(`Unknown auth message type: ${JSON.stringify(type)}`)
50+
}
3151
})
3252

3353
handleLogout = (baseUrl: string) => {
3454
logout(baseUrl)
35-
logoutChannel.postMessage(baseUrl)
55+
logoutChannel.postMessage({ baseUrl, type: 'logout' })
56+
}
57+
handleLogin = (baseUrl: string, accessToken: string) => {
58+
login(baseUrl, accessToken)
59+
// TODO: is it safe to send an accessToken over BroadcastChannel?
60+
logoutChannel.postMessage({ baseUrl, type: 'login', accessToken })
3661
}
3762
}
3863

0 commit comments

Comments
 (0)