Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/k8s-client/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
export interface K8sClientConfig {
baseUrl?: string
getToken?: () => Promise<string>
onUnauthorized?: () => void
}

function defaultOnUnauthorized(): void {
if (typeof window === "undefined") return
const rd = window.location.pathname + window.location.search + window.location.hash
window.location.assign(`/oauth2/start?rd=${encodeURIComponent(rd)}`)
}

export class K8sApiError extends Error {
Expand All @@ -24,10 +31,19 @@ export class K8sApiError extends Error {
export class K8sClient {
private baseUrl: string
private getToken?: () => Promise<string>
private onUnauthorized: () => void
private unauthorizedHandled = false

constructor(config: K8sClientConfig = {}) {
this.baseUrl = config.baseUrl ?? ""
this.getToken = config.getToken
this.onUnauthorized = config.onUnauthorized ?? defaultOnUnauthorized
}

private handleUnauthorized(): void {
if (this.unauthorizedHandled) return
this.unauthorizedHandled = true
this.onUnauthorized()
}
Comment on lines +43 to 47
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If onUnauthorized is overridden with a custom handler that does not trigger a full page reload (e.g., showing a login modal, performing a silent token refresh, or navigating via a SPA router), unauthorizedHandled will remain true indefinitely. This prevents any subsequent session expirations from triggering the unauthorized handler again during the lifetime of the SPA.

To support these scenarios, we should expose a public method to reset this flag once re-authentication succeeds. Additionally, you may want to automatically reset this.unauthorizedHandled = false upon any successful request (where res.ok is true) in request and watch.

  public resetUnauthorized(): void {
    this.unauthorizedHandled = false
  }

  private handleUnauthorized(): void {
    if (this.unauthorizedHandled) return
    this.unauthorizedHandled = true
    this.onUnauthorized()
  }


private async request<T>(path: string, init?: RequestInit): Promise<T> {
Expand Down Expand Up @@ -63,6 +79,7 @@ export class K8sClient {
} catch {
body = `Server returned ${res.status} ${res.statusText}`
}
if (res.status === 401) this.handleUnauthorized()
throw new K8sApiError(res.status, body)
}

Expand Down Expand Up @@ -256,6 +273,7 @@ export class K8sClient {
})

if (!res.ok) {
if (res.status === 401) this.handleUnauthorized()
throw new K8sApiError(
res.status,
await res.json().catch(() => res.statusText),
Expand Down
Loading