diff --git a/server/SHServ/Controllers/AuthController.php b/server/SHServ/Controllers/AuthController.php index 5742078..ae408d9 100644 --- a/server/SHServ/Controllers/AuthController.php +++ b/server/SHServ/Controllers/AuthController.php @@ -58,6 +58,9 @@ $_SESSION['shserv_user_id'] = $localUserId; // Persist tokens to DB now that we have the local user ID + $accessToken = null; + $expiresIn = 0; + $sessionToken = $_SESSION['shserv_auth_token'] ?? null; $tokenData = $_SESSION['shserv_last_token_set'] ?? null; if ($sessionToken && $tokenData) { @@ -69,14 +72,20 @@ expiresIn: $expiresAt ? (int) $expiresAt->format('U') - time() : 0, expiresAt: $expiresAt, ); + $accessToken = $tokenSet->accessToken; + $expiresIn = $tokenSet->expiresIn; $dbStore = new DbTokenStore(app()->thin_builder); $dbStore->put($sessionToken, $tokenSet, $localUserId); unset($_SESSION['shserv_last_token_set']); } - // Redirect back to app + // Redirect back to app — append access_token for cross-domain SPA $context = $_SESSION['gauth_state'][$state]['context'] ?? []; $returnTo = $context['return_to'] ?? '/'; + if ($accessToken) { + $sep = str_contains($returnTo, '?') ? '&' : '?'; + $returnTo .= $sep . 'access_token=' . urlencode($accessToken) . '&expires_in=' . $expiresIn; + } return $this->utils()->redirect($returnTo); } diff --git a/webclient/src/api/auth.js b/webclient/src/api/auth.js index 8196716..9877afa 100644 --- a/webclient/src/api/auth.js +++ b/webclient/src/api/auth.js @@ -1,24 +1,49 @@ +const STORAGE_KEY = "shserv_access_token"; + let _accessToken = null; /** * Set the current OAuth access token for Bearer authentication. + * Persists to localStorage so the token survives page reloads. * @param {string|null} token */ export function setAccessToken(token) { _accessToken = token || null; + try { + if (_accessToken) { + localStorage.setItem(STORAGE_KEY, _accessToken); + } else { + localStorage.removeItem(STORAGE_KEY); + } + } catch { + // localStorage may be unavailable (private mode, etc.) + } } /** * Get the current access token. + * Falls back to localStorage if the in-memory value was lost (e.g. page reload). * @returns {string|null} */ export function getAccessToken() { - return _accessToken; + if (_accessToken) { + return _accessToken; + } + try { + return localStorage.getItem(STORAGE_KEY) || null; + } catch { + return null; + } } /** - * Clear the stored access token. + * Clear the stored access token (memory + localStorage). */ export function clearAccessToken() { _accessToken = null; + try { + localStorage.removeItem(STORAGE_KEY); + } catch { + // ignore + } } diff --git a/webclient/src/features/auth/pages/LoginPage.vue b/webclient/src/features/auth/pages/LoginPage.vue index 811e910..3db7481 100644 --- a/webclient/src/features/auth/pages/LoginPage.vue +++ b/webclient/src/features/auth/pages/LoginPage.vue @@ -34,14 +34,28 @@