Fix refresh token
This commit is contained in:
124
src/main.js
124
src/main.js
@@ -7,7 +7,8 @@ import { createPinia } from 'pinia';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
import { AuthService } from './service/AuthService.js';
|
||||
import { TokenRefreshManager } from './service/TokenRefreshManager.js';
|
||||
|
||||
import '@/assets/styles.scss';
|
||||
import '@/assets/tailwind.css';
|
||||
@@ -20,9 +21,9 @@ import ConfirmationService from 'primevue/confirmationservice';
|
||||
import ToastService from 'primevue/toastservice';
|
||||
import { LoadingStore } from './stores/LoadingStore.js';
|
||||
|
||||
|
||||
|
||||
|
||||
// Declare variables that will be used globally
|
||||
let authInstance = null;
|
||||
let tokenRefreshManager = null;
|
||||
|
||||
config({
|
||||
editorConfig: {
|
||||
@@ -55,9 +56,6 @@ var auth = createAuth({
|
||||
axios.defaults.baseURL = import.meta.env.VITE_BACKEND_URL;
|
||||
//axios.defaults.baseURL = 'http://localhost:8081';
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(import.meta.env.VITE_BACKEND_URL);
|
||||
|
||||
const app = createApp(App);
|
||||
@@ -95,7 +93,7 @@ const preset = definePreset(Nora, {
|
||||
},
|
||||
formField: {
|
||||
hoverBorderColor: '{primary.color}',
|
||||
borderColor: '{primary.color}',
|
||||
borderColor: '{primary.color}'
|
||||
}
|
||||
},
|
||||
dark: {
|
||||
@@ -115,7 +113,7 @@ const preset = definePreset(Nora, {
|
||||
},
|
||||
formField: {
|
||||
hoverBorderColor: '{primary.color}',
|
||||
borderColor: '{primary.color}',
|
||||
borderColor: '{primary.color}'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -137,7 +135,7 @@ app.use(PrimeVue, {
|
||||
prefix: 'p',
|
||||
darkModeSelector: '.app-dark',
|
||||
cssLayer: false
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
app.use(ToastService);
|
||||
@@ -148,20 +146,108 @@ app.component('BlockViewer', BlockViewer);
|
||||
|
||||
app.mount('#app');
|
||||
|
||||
// Store auth instance for interceptor and token refresh manager after app is mounted
|
||||
authInstance = app.config.globalProperties.$auth;
|
||||
tokenRefreshManager = new TokenRefreshManager(authInstance);
|
||||
|
||||
// Start token refresh timer when user is authenticated
|
||||
if (authInstance.check()) {
|
||||
tokenRefreshManager.startRefreshTimer();
|
||||
}
|
||||
|
||||
// Listen for successful login to start token refresh timer
|
||||
window.addEventListener('auth-login-success', () => {
|
||||
console.log('[Main] Login success event received, starting token refresh timer');
|
||||
if (tokenRefreshManager) {
|
||||
tokenRefreshManager.startRefreshTimer();
|
||||
}
|
||||
});
|
||||
|
||||
// Stop token refresh timer on logout
|
||||
window.addEventListener('auth-logout', () => {
|
||||
console.log('[Main] Logout event received, stopping token refresh timer');
|
||||
if (tokenRefreshManager) {
|
||||
tokenRefreshManager.stopRefreshTimer();
|
||||
}
|
||||
});
|
||||
|
||||
const loadingStore = LoadingStore();
|
||||
|
||||
axios.interceptors.request.use(function (config) {
|
||||
axios.interceptors.request.use(
|
||||
function (config) {
|
||||
loadingStore.another_loading = true;
|
||||
return config
|
||||
}, function (error) {
|
||||
return config;
|
||||
},
|
||||
function (error) {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
axios.interceptors.response.use(function (response) {
|
||||
axios.interceptors.response.use(
|
||||
function (response) {
|
||||
loadingStore.another_loading = false;
|
||||
return response;
|
||||
},
|
||||
async function (error) {
|
||||
loadingStore.another_loading = false;
|
||||
|
||||
return response;
|
||||
}, function (error) {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
// Don't handle auth-related URLs to prevent infinite loops
|
||||
const isAuthUrl = error.config && (error.config.url.includes('/api/auth/') || error.config.url.includes('/msauth/'));
|
||||
|
||||
// Handle 403 errors by attempting token refresh (but not for auth URLs)
|
||||
if (error.response && error.response.status === 403 && !isAuthUrl) {
|
||||
console.log('[Interceptor] 403 error detected, attempting token refresh...');
|
||||
|
||||
// Check if we have an authenticated user and authInstance is available
|
||||
if (authInstance && authInstance.check && authInstance.check()) {
|
||||
try {
|
||||
// Use our custom AuthService to refresh tokens (both MSAL and classic)
|
||||
const refreshResult = await AuthService.refreshToken(authInstance);
|
||||
|
||||
if (refreshResult.success && refreshResult.token) {
|
||||
console.log('[Interceptor] Token refresh successful, updating auth and retrying request...');
|
||||
|
||||
// Update the auth token
|
||||
authInstance.token(null, refreshResult.token, false);
|
||||
|
||||
// Update the original request with new token
|
||||
error.config.headers['Authorization'] = `Bearer ${refreshResult.token}`;
|
||||
|
||||
// Retry the original request
|
||||
return axios.request(error.config);
|
||||
} else {
|
||||
console.error('[Interceptor] Token refresh failed:', refreshResult.error);
|
||||
throw new Error(refreshResult.error);
|
||||
}
|
||||
} catch (refreshError) {
|
||||
console.error('[Interceptor] Token refresh process failed:', refreshError);
|
||||
|
||||
// If refresh fails, logout and redirect to login
|
||||
try {
|
||||
// Stop token refresh timer
|
||||
window.dispatchEvent(new CustomEvent('auth-logout'));
|
||||
|
||||
await AuthService.logout();
|
||||
if (authInstance && authInstance.logout) {
|
||||
authInstance.logout({
|
||||
redirect: '/auth/login'
|
||||
});
|
||||
} else {
|
||||
window.location.href = '/auth/login';
|
||||
}
|
||||
} catch (logoutError) {
|
||||
console.error('[Interceptor] Logout failed:', logoutError);
|
||||
// Force redirect to login
|
||||
window.location.href = '/auth/login';
|
||||
}
|
||||
|
||||
return Promise.reject(refreshError);
|
||||
}
|
||||
} else {
|
||||
console.log('[Interceptor] No authenticated user for non-auth 403, ignoring...');
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -61,6 +61,11 @@ const router = createRouter({
|
||||
name: 'app-browser',
|
||||
component: () => import('@/views/pages/ApplicationBrowser.vue')
|
||||
},
|
||||
// {
|
||||
// path: '/filesystem-browser',
|
||||
// name: 'filesystem-browser',
|
||||
// component: () => import('@/views/pages/FileSystemBrowser.vue')
|
||||
// },
|
||||
{
|
||||
path: '/mdcanvas',
|
||||
name: 'mdcanvas',
|
||||
@@ -95,4 +100,17 @@ const router = createRouter({
|
||||
]
|
||||
});
|
||||
|
||||
// Navigation guard to handle logout when going to login page
|
||||
router.beforeEach((to, from, next) => {
|
||||
// Only trigger logout event when coming from an authenticated route to login
|
||||
// and not on initial page load or when already on login/callback pages
|
||||
if (to.name === 'login' && from.name && from.name !== 'login' && from.name !== 'test' && from.meta?.auth === true) {
|
||||
// User is navigating to login page from an authenticated route
|
||||
// This indicates a logout or session expiry
|
||||
console.log('[Router] Navigating from authenticated route to login, triggering logout event');
|
||||
window.dispatchEvent(new CustomEvent('auth-logout'));
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
215
src/service/AuthService.js
Normal file
215
src/service/AuthService.js
Normal file
@@ -0,0 +1,215 @@
|
||||
import { msalInstance, msalrequest } from '@/views/pages/auth/MsalConfig';
|
||||
import axios from 'axios';
|
||||
|
||||
export class AuthService {
|
||||
/**
|
||||
* Attempts to refresh tokens based on the authentication type
|
||||
* @param {Object} authInstance - The vue-auth instance
|
||||
* @returns {Promise<{success: boolean, token?: string, error?: string}>}
|
||||
*/
|
||||
static async refreshToken(authInstance = null) {
|
||||
try {
|
||||
console.log('[AuthService] Starting token refresh process...');
|
||||
|
||||
// Check if we have MSAL accounts first
|
||||
await msalInstance.initialize();
|
||||
const accounts = msalInstance.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
// User logged in with MSAL - use MSAL refresh
|
||||
console.log('[AuthService] Using MSAL token refresh...');
|
||||
return await this.refreshMsalToken();
|
||||
} else if (authInstance && authInstance.check()) {
|
||||
// User logged in with classic authentication - use vue-auth refresh
|
||||
console.log('[AuthService] Using classic authentication token refresh...');
|
||||
return await this.refreshClassicToken(authInstance);
|
||||
} else {
|
||||
console.warn('[AuthService] No valid authentication method found');
|
||||
return { success: false, error: 'No valid authentication method found' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AuthService] Token refresh failed:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes token using classic username/password authentication
|
||||
* @param {Object} authInstance - The vue-auth instance
|
||||
* @returns {Promise<{success: boolean, token?: string, error?: string}>}
|
||||
*/
|
||||
static async refreshClassicToken(authInstance) {
|
||||
try {
|
||||
console.log('[AuthService] Attempting classic token refresh...');
|
||||
|
||||
const refreshResponse = await authInstance.refresh();
|
||||
console.log('[AuthService] Classic refresh response:', refreshResponse);
|
||||
|
||||
// Check multiple possible response structures
|
||||
let token = null;
|
||||
|
||||
if (refreshResponse && refreshResponse.data) {
|
||||
// Try different token field names
|
||||
token = refreshResponse.data.token || refreshResponse.data.accessToken || refreshResponse.data.access_token;
|
||||
|
||||
// If still no token, check if the token is directly in data
|
||||
if (!token && typeof refreshResponse.data === 'string') {
|
||||
token = refreshResponse.data;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check if token is in headers (as backend sets it there)
|
||||
if (!token && refreshResponse && refreshResponse.headers) {
|
||||
token = refreshResponse.headers.authorization || refreshResponse.headers.Authorization;
|
||||
}
|
||||
|
||||
if (token) {
|
||||
console.log('[AuthService] Classic token refresh successful');
|
||||
return {
|
||||
success: true,
|
||||
token: token,
|
||||
response: refreshResponse
|
||||
};
|
||||
} else {
|
||||
console.error('[AuthService] No token found in classic refresh response:', refreshResponse);
|
||||
return { success: false, error: 'No token in refresh response' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AuthService] Classic token refresh failed:', error);
|
||||
return { success: false, error: 'Classic token refresh failed' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to refresh the MSAL token and exchange it for a new backend token
|
||||
* @returns {Promise<{success: boolean, token?: string, error?: string}>}
|
||||
*/
|
||||
static async refreshMsalToken() {
|
||||
try {
|
||||
console.log('[AuthService] Starting MSAL token refresh process...');
|
||||
|
||||
// Get all accounts from MSAL
|
||||
await msalInstance.initialize();
|
||||
const accounts = msalInstance.getAllAccounts();
|
||||
|
||||
if (accounts.length === 0) {
|
||||
console.warn('[AuthService] No MSAL accounts found for MSAL refresh');
|
||||
return { success: false, error: 'No MSAL accounts found' };
|
||||
}
|
||||
|
||||
const account = accounts[0];
|
||||
console.log('[AuthService] Using account:', account.username);
|
||||
|
||||
// Try to acquire token silently
|
||||
let tokenResponse;
|
||||
try {
|
||||
tokenResponse = await msalInstance.acquireTokenSilent({
|
||||
scopes: msalrequest.scopes,
|
||||
account: account
|
||||
});
|
||||
console.log('[AuthService] MSAL token acquired silently');
|
||||
} catch (silentError) {
|
||||
console.warn('[AuthService] Silent token acquisition failed, trying interactive...');
|
||||
|
||||
// If silent fails, try interactive
|
||||
try {
|
||||
tokenResponse = await msalInstance.acquireTokenPopup({
|
||||
scopes: msalrequest.scopes,
|
||||
account: account
|
||||
});
|
||||
console.log('[AuthService] MSAL token acquired interactively');
|
||||
} catch (interactiveError) {
|
||||
console.error('[AuthService] Interactive token acquisition failed:', interactiveError);
|
||||
return { success: false, error: 'Token acquisition failed' };
|
||||
}
|
||||
}
|
||||
|
||||
if (!tokenResponse || !tokenResponse.accessToken) {
|
||||
console.error('[AuthService] No access token received');
|
||||
return { success: false, error: 'No access token received' };
|
||||
}
|
||||
|
||||
// Exchange the MSAL token for a backend token
|
||||
try {
|
||||
const exchangeResponse = await axios.post(
|
||||
'/msauth/exchange',
|
||||
{},
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenResponse.accessToken}` }
|
||||
}
|
||||
);
|
||||
|
||||
if (exchangeResponse.data && exchangeResponse.data.token) {
|
||||
console.log('[AuthService] MSAL token refresh successful');
|
||||
return {
|
||||
success: true,
|
||||
token: exchangeResponse.data.token,
|
||||
response: exchangeResponse
|
||||
};
|
||||
} else {
|
||||
console.error('[AuthService] No token in MSAL exchange response');
|
||||
return { success: false, error: 'No token in exchange response' };
|
||||
}
|
||||
} catch (exchangeError) {
|
||||
console.error('[AuthService] MSAL token exchange failed:', exchangeError);
|
||||
return { success: false, error: 'Token exchange failed' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AuthService] MSAL token refresh failed:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current MSAL session is still valid
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
static async isSessionValid() {
|
||||
try {
|
||||
await msalInstance.initialize();
|
||||
const accounts = msalInstance.getAllAccounts();
|
||||
|
||||
if (accounts.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to acquire token silently to check if session is valid
|
||||
try {
|
||||
await msalInstance.acquireTokenSilent({
|
||||
scopes: msalrequest.scopes,
|
||||
account: accounts[0]
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn('[AuthService] Session validation failed:', error);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AuthService] Session validation error:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out from MSAL
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async logout() {
|
||||
try {
|
||||
await msalInstance.initialize();
|
||||
const accounts = msalInstance.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
const logoutRequest = {
|
||||
account: accounts[0],
|
||||
mainWindowRedirectUri: window.location.origin + '/auth/login'
|
||||
};
|
||||
|
||||
await msalInstance.logoutPopup(logoutRequest);
|
||||
console.log('[AuthService] MSAL logout completed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AuthService] Logout error:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/service/TokenRefreshManager.js
Normal file
106
src/service/TokenRefreshManager.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import { AuthService } from './AuthService.js';
|
||||
|
||||
export class TokenRefreshManager {
|
||||
constructor(authInstance) {
|
||||
this.authInstance = authInstance;
|
||||
this.refreshInterval = null;
|
||||
this.isRefreshing = false;
|
||||
// Refresh every 50 minutes (tokens expire after 1 hour)
|
||||
this.REFRESH_INTERVAL_MS = 50 * 60 * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the automatic token refresh timer
|
||||
*/
|
||||
startRefreshTimer() {
|
||||
console.log('[TokenRefreshManager] Starting automatic token refresh timer...');
|
||||
|
||||
// Clear any existing timer
|
||||
this.stopRefreshTimer();
|
||||
|
||||
this.refreshInterval = setInterval(async () => {
|
||||
await this.performRefresh();
|
||||
}, this.REFRESH_INTERVAL_MS);
|
||||
|
||||
console.log(`[TokenRefreshManager] Timer set to refresh every ${this.REFRESH_INTERVAL_MS / 60000} minutes`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the automatic token refresh timer
|
||||
*/
|
||||
stopRefreshTimer() {
|
||||
if (this.refreshInterval) {
|
||||
clearInterval(this.refreshInterval);
|
||||
this.refreshInterval = null;
|
||||
console.log('[TokenRefreshManager] Token refresh timer stopped');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a token refresh
|
||||
*/
|
||||
async performRefresh() {
|
||||
if (this.isRefreshing) {
|
||||
console.log('[TokenRefreshManager] Refresh already in progress, skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
// Only refresh if user is authenticated
|
||||
if (!this.authInstance || !this.authInstance.check()) {
|
||||
console.log('[TokenRefreshManager] User not authenticated, stopping refresh timer');
|
||||
this.stopRefreshTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRefreshing = true;
|
||||
|
||||
try {
|
||||
console.log('[TokenRefreshManager] Performing scheduled token refresh...');
|
||||
|
||||
const refreshResult = await AuthService.refreshToken(this.authInstance);
|
||||
|
||||
if (refreshResult.success && refreshResult.token) {
|
||||
// Update the auth token silently
|
||||
this.authInstance.token(null, refreshResult.token, false);
|
||||
console.log('[TokenRefreshManager] Token refreshed successfully');
|
||||
} else {
|
||||
console.error('[TokenRefreshManager] Scheduled refresh failed:', refreshResult.error);
|
||||
|
||||
// Stop the timer and logout on failure
|
||||
this.stopRefreshTimer();
|
||||
|
||||
try {
|
||||
// Dispatch logout event to stop other timers
|
||||
window.dispatchEvent(new CustomEvent('auth-logout'));
|
||||
|
||||
await AuthService.logout();
|
||||
this.authInstance.logout({
|
||||
redirect: '/auth/login'
|
||||
});
|
||||
} catch (logoutError) {
|
||||
console.error('[TokenRefreshManager] Logout failed:', logoutError);
|
||||
window.location.href = '/auth/login';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[TokenRefreshManager] Refresh error:', error);
|
||||
this.stopRefreshTimer();
|
||||
} finally {
|
||||
this.isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually triggers a token refresh
|
||||
* @returns {Promise<boolean>} Success status
|
||||
*/
|
||||
async manualRefresh() {
|
||||
if (this.isRefreshing) {
|
||||
console.log('[TokenRefreshManager] Manual refresh requested but already in progress');
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.performRefresh();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ onMounted(async () => {
|
||||
console.log('[Callback] After initialize on callback');
|
||||
} catch (e) {
|
||||
console.error('[Callback] Error during MSAL initialization:', e);
|
||||
message.value = "Error during MSAL initialization.";
|
||||
message.value = 'Error during MSAL initialization.';
|
||||
visible.value = true;
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
// Wait 2 second to avoid race condition
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
message.value = 'Logging in to the application...';
|
||||
|
||||
// Token exchange function with retry
|
||||
@@ -63,7 +63,7 @@ onMounted(async () => {
|
||||
} catch (err) {
|
||||
if (!retry) {
|
||||
console.warn('[Callback] First attempt failed, waiting 1500ms and retrying...');
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
return tryTokenExchange(true);
|
||||
} else {
|
||||
throw err;
|
||||
@@ -85,6 +85,9 @@ onMounted(async () => {
|
||||
const userResponse = await auth.fetch();
|
||||
console.log('[Callback] User fetch response:', userResponse);
|
||||
|
||||
// Start token refresh timer after successful authentication
|
||||
window.dispatchEvent(new CustomEvent('auth-login-success'));
|
||||
|
||||
const userData = userResponse.data?.data;
|
||||
console.log('[Callback] userResponse.data.data:', userData);
|
||||
|
||||
@@ -116,7 +119,6 @@ onMounted(async () => {
|
||||
visible.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -45,7 +45,7 @@ const tryTokenExchange = async (accessToken, retry = false) => {
|
||||
} catch (err) {
|
||||
if (!retry) {
|
||||
console.warn('[loginAD] First attempt failed, waiting 1500ms and retrying...');
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
return tryTokenExchange(accessToken, true);
|
||||
} else {
|
||||
throw err;
|
||||
@@ -63,7 +63,7 @@ const loginAD = async () => {
|
||||
console.log('[loginAD] Token MSAL ottenuto:', token);
|
||||
|
||||
// Inserisci una pausa per evitare race condition
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
|
||||
let exchangeResponse;
|
||||
try {
|
||||
@@ -76,6 +76,9 @@ const loginAD = async () => {
|
||||
const resp = await auth.fetch();
|
||||
console.log('[loginAD] User fetch response:', resp.data.data);
|
||||
|
||||
// Start token refresh timer after successful authentication
|
||||
window.dispatchEvent(new CustomEvent('auth-login-success'));
|
||||
|
||||
const userData = resp.data.data;
|
||||
if (!userData.selectedProject) {
|
||||
console.log('[loginAD] No project selected → projects-list');
|
||||
@@ -96,7 +99,7 @@ const loginAD = async () => {
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[loginAD] Error:', e);
|
||||
error.value = "Error while login AD. Contact the administrator.";
|
||||
error.value = 'Error while login AD. Contact the administrator.';
|
||||
visible.value = true;
|
||||
}
|
||||
};
|
||||
@@ -115,6 +118,10 @@ const loginMsal = async () => {
|
||||
|
||||
const logoutAD = async () => {
|
||||
console.log('[logoutAD] Logout AD...');
|
||||
|
||||
// Stop token refresh timer
|
||||
window.dispatchEvent(new CustomEvent('auth-logout'));
|
||||
|
||||
const logoutRequest = {
|
||||
account: msaccount.value,
|
||||
mainWindowRedirectUri: window.location.href
|
||||
@@ -142,6 +149,10 @@ const login_old = async () => {
|
||||
})
|
||||
.then((response) => {
|
||||
console.log('[login_old] Login response:', response.data.data);
|
||||
|
||||
// Start token refresh timer after successful authentication
|
||||
window.dispatchEvent(new CustomEvent('auth-login-success'));
|
||||
|
||||
if (!response.data.data.selectedProject) {
|
||||
console.log('[login_old] No project selected, redirect to projects-list');
|
||||
router.push({ name: 'projects-list' });
|
||||
@@ -169,9 +180,9 @@ const login_old = async () => {
|
||||
<div class="bg-surface-50 dark:bg-surface-950 flex items-center justify-center min-h-screen min-w-[100vw] overflow-hidden">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div style="border-radius: 56px; padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
||||
<div class="w-full bg-surface-0 dark:bg-surface-900 py-20 px-8 sm:px-20" style="border-radius: 53px">
|
||||
<div class="logo-container mb-8">
|
||||
<svg width="85" height="63" viewBox="0 0 85 63" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<div class="w-full bg-surface-0 dark:bg-surface-900 py-12 px-6 sm:px-12" style="border-radius: 53px">
|
||||
<div class="logo-container mb-6">
|
||||
<svg width="70" height="45" viewBox="0 0 85 63" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
@@ -189,36 +200,36 @@ const login_old = async () => {
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-12">
|
||||
<h1 class="text-3xl font-semibold">Welcome to</h1>
|
||||
<h1 class="text-4xl font-bold text-primary">WizardAI - WORKFLOW</h1>
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-2xl font-semibold">Welcome to</h1>
|
||||
<h1 class="text-3xl font-bold text-primary">WizardAI - WORKFLOW</h1>
|
||||
</div>
|
||||
|
||||
<!-- Username & Password Section -->
|
||||
<div class="mb-10 w-full max-w-xl">
|
||||
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-l font-medium mb-2">Username</label>
|
||||
<InputText id="email1" type="text" placeholder="Username" class="w-full mb-6" style="padding: 1rem" v-model="username" />
|
||||
<div class="mb-6 w-full max-w-md">
|
||||
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-sm font-medium mb-2">Username</label>
|
||||
<InputText id="email1" type="text" placeholder="Username" class="w-full mb-4" style="padding: 0.75rem" v-model="username" />
|
||||
|
||||
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-l mb-2">Password</label>
|
||||
<Password id="password1" v-model="password" placeholder="Password" :toggleMask="true" class="w-full mb-6" inputClass="w-full" :inputStyle="{ padding: '1rem' }" />
|
||||
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-sm mb-2">Password</label>
|
||||
<Password id="password1" v-model="password" placeholder="Password" :toggleMask="true" class="w-full mb-4" inputClass="w-full" :inputStyle="{ padding: '0.75rem' }" />
|
||||
|
||||
<Button @click="login_old" label="Sign In with Username and Password" class="w-full text-xl mb-4" />
|
||||
<Button @click="login_old" label="Sign In with Username and Password" class="w-full text-sm mb-3" />
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="my-6 w-full max-w-xl flex items-center justify-center">
|
||||
<div class="my-4 w-full max-w-md flex items-center justify-center">
|
||||
<hr class="flex-grow border-t border-gray-300 dark:border-gray-700" />
|
||||
<span class="mx-4 text-gray-500 dark:text-gray-400">Sign in with Microsoft Azure AD</span>
|
||||
<span class="mx-3 text-sm text-gray-500 dark:text-gray-400">Sign in with Microsoft Azure AD</span>
|
||||
<hr class="flex-grow border-t border-gray-300 dark:border-gray-700" />
|
||||
</div>
|
||||
|
||||
<!-- Azure AD Section -->
|
||||
<div class="w-full max-w-xl">
|
||||
<Button @click="loginMsal" v-if="msaccount == null" label="Sign In with Microsoft AD" class="w-full text-l mb-4" />
|
||||
<div class="w-full max-w-md">
|
||||
<Button @click="loginMsal" v-if="msaccount == null" label="Sign In with Microsoft AD" class="w-full text-sm mb-3" />
|
||||
|
||||
<Button @click="loginAD" v-if="msaccount" :label="'Login ' + msaccount.username" class="w-full text-l mb-4" />
|
||||
<Button @click="loginAD" v-if="msaccount" :label="'Login ' + msaccount.username" class="w-full text-sm mb-3" />
|
||||
|
||||
<Button @click="logoutAD" v-if="msaccount" label="Logout AD" severity="warn" class="w-full text-l mb-4" />
|
||||
<Button @click="logoutAD" v-if="msaccount" label="Logout AD" severity="warn" class="w-full text-sm mb-3" />
|
||||
</div>
|
||||
|
||||
<!-- Error Message -->
|
||||
@@ -244,8 +255,8 @@ const login_old = async () => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
margin-top: -40px;
|
||||
margin-bottom: 15px;
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
|
||||
Reference in New Issue
Block a user