From c244963ef6178f75e17e597f35abf73b1b121776 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 11 Mar 2025 18:05:53 +1100 Subject: [PATCH] chore: sign user out if token expired --- .../Coder-Desktop/Coder_DesktopApp.swift | 20 ++++++++++++---- Coder-Desktop/Coder-Desktop/State.swift | 23 +++++++++++++++++++ Coder-Desktop/CoderSDK/Client.swift | 8 +++---- Coder-Desktop/project.yml | 6 +++-- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift b/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift index 1d379e9..97e5e70 100644 --- a/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift +++ b/Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift @@ -40,11 +40,21 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func applicationDidFinishLaunching(_: Notification) { - menuBar = .init(menuBarExtra: FluidMenuBarExtra(title: "Coder Desktop", image: "MenuBarIcon") { - VPNMenu().frame(width: 256) - .environmentObject(self.vpn) - .environmentObject(self.state) - }) + menuBar = .init(menuBarExtra: FluidMenuBarExtra( + title: "Coder Desktop", + image: "MenuBarIcon", + onAppear: { + // If the VPN is enabled, it's likely the token isn't expired + guard case .disabled = self.vpn.state, self.state.hasSession else { return } + Task { @MainActor in + await self.state.handleTokenExpiry() + } + }, content: { + VPNMenu().frame(width: 256) + .environmentObject(self.vpn) + .environmentObject(self.state) + } + )) // Subscribe to system VPN updates NotificationCenter.default.addObserver( self, diff --git a/Coder-Desktop/Coder-Desktop/State.swift b/Coder-Desktop/Coder-Desktop/State.swift index 3e723c9..b394468 100644 --- a/Coder-Desktop/Coder-Desktop/State.swift +++ b/Coder-Desktop/Coder-Desktop/State.swift @@ -2,10 +2,12 @@ import CoderSDK import Foundation import KeychainAccess import NetworkExtension +import os import SwiftUI @MainActor class AppState: ObservableObject { + private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "AppState") let appId = Bundle.main.bundleIdentifier! // Stored in UserDefaults @@ -95,6 +97,9 @@ class AppState: ObservableObject { ) if hasSession { _sessionToken = Published(initialValue: keychainGet(for: Keys.sessionToken)) + if sessionToken == nil || sessionToken!.isEmpty == true { + clearSession() + } } } @@ -105,6 +110,24 @@ class AppState: ObservableObject { reconfigure() } + public func handleTokenExpiry() async { + if hasSession { + let client = Client(url: baseAccessURL!, token: sessionToken!) + do { + _ = try await client.user("me") + } catch let ClientError.api(apiErr) { + // Expired token + if apiErr.statusCode == 401 { + clearSession() + } + } catch { + // Some other failure, we'll show an error if they try and do something + logger.error("failed to check token validity: \(error)") + return + } + } + } + public func clearSession() { hasSession = false sessionToken = nil diff --git a/Coder-Desktop/CoderSDK/Client.swift b/Coder-Desktop/CoderSDK/Client.swift index 85bc8f3..239db14 100644 --- a/Coder-Desktop/CoderSDK/Client.swift +++ b/Coder-Desktop/CoderSDK/Client.swift @@ -104,10 +104,10 @@ public struct Client { } public struct APIError: Decodable, Sendable { - let response: Response - let statusCode: Int - let method: String - let url: URL + public let response: Response + public let statusCode: Int + public let method: String + public let url: URL var description: String { var components = ["\(method) \(url.absoluteString)\nUnexpected status code \(statusCode):\n\(response.message)"] diff --git a/Coder-Desktop/project.yml b/Coder-Desktop/project.yml index 5411b5a..c3c53f9 100644 --- a/Coder-Desktop/project.yml +++ b/Coder-Desktop/project.yml @@ -92,10 +92,12 @@ packages: url: https://github.com/SimplyDanny/SwiftLintPlugins from: 0.57.1 FluidMenuBarExtra: - # Forked so we can dynamically update the menu bar icon. + # Forked to: + # - Dynamically update the menu bar icon + # - Set onAppear/disappear handlers. # The upstream repo has a purposefully limited API url: https://github.com/coder/fluid-menu-bar-extra - revision: 020be37 + revision: 96a861a KeychainAccess: url: https://github.com/kishikawakatsumi/KeychainAccess branch: e0c7eebc5a4465a3c4680764f26b7a61f567cdaf