Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
import { useCallback, useEffect, useState } from "react"import { apiFetch, setSynoToken } from "src/api"import useSWR from "swr"
export type AuthResponse = { serverMode: AuthServerMode authorized: boolean viewerIdentity?: { loginName: string nodeName: string nodeIP: string profilePicUrl?: string capabilities: { [key in PeerCapability]: boolean } } needsSynoAuth?: boolean}
export type AuthServerMode = "login" | "readonly" | "manage"
export type PeerCapability = "*" | "ssh" | "subnets" | "exitnodes" | "account"
/** * canEdit reports whether the given auth response specifies that the viewer * has the ability to edit the given capability. */export function canEdit(cap: PeerCapability, auth: AuthResponse): boolean { if (!auth.authorized || !auth.viewerIdentity) { return false } if (auth.viewerIdentity.capabilities["*"] === true) { return true // can edit all features } return auth.viewerIdentity.capabilities[cap] === true}
/** * hasAnyEditCapabilities reports whether the given auth response specifies * that the viewer has at least one edit capability. If this is true, the * user is able to go through the auth flow to authenticate a management * session. */export function hasAnyEditCapabilities(auth: AuthResponse): boolean { return Object.values(auth.viewerIdentity?.capabilities || {}).includes(true)}
/** * useAuth reports and refreshes Tailscale auth status for the web client. */export default function useAuth() { const { data, error, mutate } = useSWR<AuthResponse>("/auth") const [ranSynoAuth, setRanSynoAuth] = useState<boolean>(false)
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") .then((d) => { if (d.authUrl) { window.open(d.authUrl, "_blank") return apiFetch("/auth/session/wait", "GET") } }) .then(() => { mutate() }) .catch((error) => { console.error(error) }) }, [mutate])
// Start regular auth flow. useEffect(() => { const needsAuth = data && !loading && !data.authorized && hasAnyEditCapabilities(data) && new URLSearchParams(window.location.search).get("check") === "now"
if (needsAuth) { newSession() } }, [data, loading, newSession])
return { data, loading, newSession, }}