From 09f741557bf4faa5ee60ffa388b8082e4815e713 Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Thu, 23 Jan 2025 11:31:10 -0500 Subject: [PATCH] feat: sso testing page and form disable on submit --- web/components/SsoButton.ce.vue | 61 ++++++- web/pages/login.vue | 309 +++++++++++++++++++++++++++++++- 2 files changed, 362 insertions(+), 8 deletions(-) diff --git a/web/components/SsoButton.ce.vue b/web/components/SsoButton.ce.vue index 33d33ba06..3ef8f9fc3 100644 --- a/web/components/SsoButton.ce.vue +++ b/web/components/SsoButton.ce.vue @@ -8,15 +8,29 @@ export interface Props { } const props = defineProps(); +type CurrentState = 'loading' | 'idle' | 'error'; + +const currentState = ref('idle'); +const error = ref(null); + const isSsoEnabled = computed( () => props['ssoenabled'] === true || props['ssoenabled'] === 'true' || props.ssoEnabled ); -const enterCallbackTokenIntoField = (token: string) => { +const getInputFields = (): { + form: HTMLFormElement; + passwordField: HTMLInputElement; + usernameField: HTMLInputElement; +} => { + const form = document.querySelector('form[action="/login"]') as HTMLFormElement; const passwordField = document.querySelector('input[name=password]') as HTMLInputElement; const usernameField = document.querySelector('input[name=username]') as HTMLInputElement; - const form = document.querySelector('form[action="/login"]') as HTMLFormElement; + return { form, passwordField, usernameField }; +}; +const enterCallbackTokenIntoField = (token: string) => { + const { form, passwordField, usernameField } = getInputFields(); + console.trace(passwordField, usernameField, form); if (!passwordField || !usernameField || !form) { console.warn('Could not find form, username, or password field'); } else { @@ -39,6 +53,20 @@ const generateStateToken = (): string => { return state; }; +const disableFormOnSubmit = () => { + const { form } = getInputFields(); + if (form) { + form.style.display = 'none'; + } +}; + +const reEnableFormOnError = () => { + const { form } = getInputFields(); + if (form) { + form.style.display = 'block'; + } +}; + onMounted(async () => { try { const search = new URLSearchParams(window.location.search); @@ -47,6 +75,8 @@ onMounted(async () => { const sessionState = getStateToken(); if (code && state === sessionState) { + disableFormOnSubmit(); + currentState.value = 'loading'; const token = await fetch(new URL('/api/oauth2/token', ACCOUNT), { method: 'POST', body: new URLSearchParams({ @@ -62,14 +92,31 @@ onMounted(async () => { window.history.replaceState({}, document.title, window.location.pathname); window.location.search = ''; } + } else { + throw new Error('Failed to fetch token'); } } } catch (err) { console.error('Error fetching token', err); + + currentState.value = 'error'; + error.value = 'Error fetching token'; + reEnableFormOnError(); } finally { } }); +const buttonText = computed(() => { + switch (currentState.value) { + case 'loading': + return 'Signing you in...'; + case 'error': + return 'Error'; + default: + return 'Log In With Unraid.net'; + } +}); + const navigateToExternalSSOUrl = () => { const url = new URL('sso', ACCOUNT); const callbackUrlLogin = new URL('login', window.location.origin); @@ -84,9 +131,13 @@ const navigateToExternalSSOUrl = () => { diff --git a/web/pages/login.vue b/web/pages/login.vue index aa7dd808b..9e4f840d1 100644 --- a/web/pages/login.vue +++ b/web/pages/login.vue @@ -1,4 +1,5 @@ \ No newline at end of file + + <?=$var['NAME']?>/Login + + + +
+ +
+

SERVER NAME

+

COMMENT

+ +
Case Icon
+ +
+
+

+ + +

+

+ +

+
+ +
+
+
+ + +