mirror of
https://github.com/tailscale/tailscale.git
synced 2026-02-18 22:21:16 +01:00
client/web: revalidate auth state on focus across tabs
This introduces a cache key for the auth response that can be used to instantly propagate changes to the auth state to other browser tabs. Signed-off-by: Gesa Stupperich <gesa@tailscale.com>
This commit is contained in:
parent
470683eaf8
commit
dde3c442f3
@ -123,7 +123,10 @@ export function useAPI() {
|
||||
return apiFetch<{ url?: string }>("/up", "POST", t.data)
|
||||
.then((d) => d.url && window.open(d.url, "_blank")) // "up" login step
|
||||
.then(() => incrementMetric("web_client_node_connect"))
|
||||
.then(() => mutate("/data"))
|
||||
.then(() => {
|
||||
mutate("/data")
|
||||
mutate("/auth")
|
||||
})
|
||||
.catch(handlePostError("Failed to login"))
|
||||
|
||||
/**
|
||||
@ -134,9 +137,9 @@ export function useAPI() {
|
||||
// For logout, must increment metric before running api call,
|
||||
// as tailscaled will be unreachable after the call completes.
|
||||
incrementMetric("web_client_node_disconnect")
|
||||
return apiFetch("/local/v0/logout", "POST").catch(
|
||||
handlePostError("Failed to logout")
|
||||
)
|
||||
return apiFetch("/local/v0/logout", "POST")
|
||||
.then(() => mutate("/auth"))
|
||||
.catch(handlePostError("Failed to logout"))
|
||||
|
||||
/**
|
||||
* "new-auth-session" handles creating a new check mode session to
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { apiFetch, setSynoToken } from "src/api"
|
||||
import useSWR from "swr"
|
||||
|
||||
export type AuthResponse = {
|
||||
serverMode: AuthServerMode
|
||||
@ -49,33 +50,26 @@ export function hasAnyEditCapabilities(auth: AuthResponse): boolean {
|
||||
* useAuth reports and refreshes Tailscale auth status for the web client.
|
||||
*/
|
||||
export default function useAuth() {
|
||||
const [data, setData] = useState<AuthResponse>()
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const { data, error, mutate } = useSWR<AuthResponse>("/auth")
|
||||
const [ranSynoAuth, setRanSynoAuth] = useState<boolean>(false)
|
||||
|
||||
const loadAuth = useCallback(() => {
|
||||
setLoading(true)
|
||||
return apiFetch<AuthResponse>("/auth", "GET")
|
||||
.then((d) => {
|
||||
setData(d)
|
||||
if (d.needsSynoAuth) {
|
||||
fetch("/webman/login.cgi")
|
||||
.then((r) => r.json())
|
||||
.then((a) => {
|
||||
setSynoToken(a.SynoToken)
|
||||
setRanSynoAuth(true)
|
||||
setLoading(false)
|
||||
})
|
||||
} else {
|
||||
setLoading(false)
|
||||
}
|
||||
return d
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false)
|
||||
console.error(error)
|
||||
})
|
||||
}, [])
|
||||
const loading = !data && !error
|
||||
|
||||
// Start Synology auth flow if needed.
|
||||
useEffect(() => {
|
||||
if (data?.needsSynoAuth && !ranSynoAuth) {
|
||||
fetch("/webman/login.cgi")
|
||||
.then((r) => r.json())
|
||||
.then((a) => {
|
||||
setSynoToken(a.SynoToken)
|
||||
setRanSynoAuth(true)
|
||||
mutate()
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Synology auth error:", error)
|
||||
})
|
||||
}
|
||||
}, [data?.needsSynoAuth, ranSynoAuth, mutate])
|
||||
|
||||
const newSession = useCallback(() => {
|
||||
return apiFetch<{ authUrl?: string }>("/auth/session/new", "GET")
|
||||
@ -86,34 +80,26 @@ export default function useAuth() {
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
loadAuth()
|
||||
mutate()
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
}, [loadAuth])
|
||||
}, [mutate])
|
||||
|
||||
// Start regular auth flow.
|
||||
useEffect(() => {
|
||||
loadAuth().then((d) => {
|
||||
if (!d) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
!d.authorized &&
|
||||
hasAnyEditCapabilities(d) &&
|
||||
// Start auth flow immediately if browser has requested it.
|
||||
new URLSearchParams(window.location.search).get("check") === "now"
|
||||
) {
|
||||
newSession()
|
||||
}
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
const needsAuth =
|
||||
data &&
|
||||
!loading &&
|
||||
!data.authorized &&
|
||||
hasAnyEditCapabilities(data) &&
|
||||
new URLSearchParams(window.location.search).get("check") === "now"
|
||||
|
||||
useEffect(() => {
|
||||
loadAuth() // Refresh auth state after syno auth runs
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ranSynoAuth])
|
||||
if (needsAuth) {
|
||||
newSession()
|
||||
}
|
||||
}, [data, loading, newSession])
|
||||
|
||||
return {
|
||||
data,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user