diff --git a/src/prefect/server/api/server.py b/src/prefect/server/api/server.py index 1cbae4af2af4..29c20576d4dd 100644 --- a/src/prefect/server/api/server.py +++ b/src/prefect/server/api/server.py @@ -360,10 +360,13 @@ def create_ui_app(ephemeral: bool) -> FastAPI: mimetypes.add_type("application/javascript", ".js") @ui_app.get(f"{stripped_base_url}/ui-settings") - def ui_settings(): + def ui_settings() -> dict[str, Any]: return { "api_url": prefect.settings.PREFECT_UI_API_URL.value(), "csrf_enabled": prefect.settings.PREFECT_SERVER_CSRF_PROTECTION_ENABLED.value(), + "auth": "BASIC" + if prefect.settings.PREFECT_SERVER_API_AUTH_STRING.value() + else None, "flags": [], } diff --git a/tests/server/test_app.py b/tests/server/test_app.py index 49c2f652c367..742b7f443031 100644 --- a/tests/server/test_app.py +++ b/tests/server/test_app.py @@ -3,6 +3,7 @@ from prefect.server.api.server import create_app from prefect.settings import ( + PREFECT_SERVER_API_AUTH_STRING, PREFECT_SERVER_CSRF_PROTECTION_ENABLED, PREFECT_UI_API_URL, temporary_settings, @@ -28,6 +29,7 @@ def test_app_exposes_ui_settings(): assert response.json() == { "api_url": PREFECT_UI_API_URL.value(), "csrf_enabled": PREFECT_SERVER_CSRF_PROTECTION_ENABLED.value(), + "auth": "BASIC" if PREFECT_SERVER_API_AUTH_STRING.value() else None, "flags": [], } diff --git a/ui/src/maps/uiSettings.ts b/ui/src/maps/uiSettings.ts index c4a5a0865880..e888f1b6ebd7 100644 --- a/ui/src/maps/uiSettings.ts +++ b/ui/src/maps/uiSettings.ts @@ -7,6 +7,7 @@ export const mapSettingsResponseToSettings: MapFunction { - if (!authenticated) { + + + api.admin.authCheck().then(status_code => { + if (status_code == 401) { if (router.currentRoute.value.name !== 'login') { showToast('Authentication failed.', 'error', { timeout: false }) + router.push({ + name: 'login', + query: { redirect: router.currentRoute.value.fullPath } + }) } - router.push({ - name: 'login', - query: { redirect: router.currentRoute.value.fullPath } - }) } else { api.health.isHealthy().then(healthy => { if (!healthy) { diff --git a/ui/src/services/adminApi.ts b/ui/src/services/adminApi.ts index 005aece79708..81a62fafd43a 100644 --- a/ui/src/services/adminApi.ts +++ b/ui/src/services/adminApi.ts @@ -1,5 +1,6 @@ import { Api } from '@prefecthq/prefect-ui-library' import { ServerSettings } from '@/models/ServerSettings' +import { UiSettings } from './uiSettings' export class AdminApi extends Api { protected override routePrefix = '/admin' @@ -11,13 +12,19 @@ export class AdminApi extends Api { public async getVersion(): Promise { return await this.get('/version').then(({ data }) => data) } - - public async authCheck(): Promise { + public async authCheck(): Promise { + const auth = await UiSettings.get('auth') + if (!auth) { + return 200 + } try { - await this.getVersion() - return true - } catch { - return false + const res = await this.get('/version') + return res.status + } catch (error: any) { + if (error.response) { + return error.response.status + } + return 500 } } } diff --git a/ui/src/services/csrfTokenApi.ts b/ui/src/services/csrfTokenApi.ts index 6fe36540b862..a61d6973e591 100644 --- a/ui/src/services/csrfTokenApi.ts +++ b/ui/src/services/csrfTokenApi.ts @@ -56,7 +56,14 @@ export class CsrfTokenApi extends Api { const refresh = async (): Promise => { try { - const response = await this.get(`/csrf-token?client=${this.clientId}`) + + const password = localStorage.getItem('prefect-password') + const response = await this.get(`/csrf-token?client=${this.clientId}`, + { + headers: password ? { + 'Authorization': `Basic ${password}` + } : undefined + }) this.csrfToken = mapper.map('CsrfTokenResponse', response.data, 'CsrfToken') this.ongoingRefresh = null diff --git a/ui/src/services/uiSettings.ts b/ui/src/services/uiSettings.ts index e85ac3491770..642e2b7e3d29 100644 --- a/ui/src/services/uiSettings.ts +++ b/ui/src/services/uiSettings.ts @@ -7,6 +7,7 @@ import { FeatureFlag } from '@/utilities/permissions' export type Settings = { apiUrl: string, csrfEnabled: boolean, + auth: string, flags: FeatureFlag[], } diff --git a/ui/src/types/settingsResponse.ts b/ui/src/types/settingsResponse.ts index a0f28b10b3a4..58bab7fad180 100644 --- a/ui/src/types/settingsResponse.ts +++ b/ui/src/types/settingsResponse.ts @@ -3,5 +3,6 @@ import { FlagResponse } from '@/types/flagResponse' export type SettingsResponse = { api_url: string, csrf_enabled: boolean, + auth: string | null, flags: FlagResponse[], } \ No newline at end of file