# Description of Changes
When password login is disabled UI changes to have central style SSO
button

<img width="2057" height="1369" alt="image"
src="https://github.com/user-attachments/assets/8f65f778-0809-4c54-a9c4-acf3a67cfa63"
/>

Auto SSO login functionality

Massively increases auth debugging visibility: verbose console logging
in ErrorBoundary, AuthProvider, Landing, AuthCallback.

Improves OAuth/SAML testability: adds Keycloak docker-compose setups +
realm JSON exports + start/validate scripts for OAuth and SAML
environments.

Hardens license upload path handling: better logs + safer directory
traversal protection by normalizing absolute paths before startsWith
check.

UI polish for SSO-only login: new “single provider” centered layout +
updated button styles (pill buttons, variants, icon wrapper, arrow).


<!--
Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)
-->

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
Anthony Stirling
2026-02-05 12:26:41 +00:00
committed by GitHub
parent a844c7d09e
commit 00136f9e20
22 changed files with 1951 additions and 88 deletions
+38 -14
View File
@@ -36,6 +36,17 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<AuthError | null>(null);
// Debug: Track state transitions
useEffect(() => {
console.log('[Auth] State changed:', {
loading,
hasSession: !!session,
hasError: !!error,
userId: session?.user?.id,
timestamp: new Date().toISOString()
});
}, [loading, session, error]);
/**
* Refresh current session
*/
@@ -92,10 +103,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
*/
useEffect(() => {
let mounted = true;
const mountId = Math.random().toString(36).substring(7);
console.log(`[Auth:${mountId}] 🔵 AuthProvider mounted`);
const initializeAuth = async () => {
try {
console.debug('[Auth] Initializing auth...');
console.debug(`[Auth:${mountId}] Initializing auth...`);
// Clear any platform-specific cached auth on login page init.
if (typeof window !== 'undefined' && window.location.pathname.startsWith('/login')) {
await clearPlatformAuthOnLoginInit();
@@ -134,7 +147,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
// Listen for jwt-available event (triggered by desktop auth or other sources)
const handleJwtAvailable = () => {
console.debug('[Auth] JWT available event received, refreshing session');
console.log(`[Auth:${mountId}] ════════════════════════════════════`);
console.log(`[Auth:${mountId}] 🔄 JWT available event received`);
console.log(`[Auth:${mountId}] Current state: loading=${loading}, hasSession=${!!session}`);
console.log(`[Auth:${mountId}] Setting loading=true to stabilize auth state`);
setLoading(true); // Prevent unstable renders during auth state transition
setError(null);
console.log(`[Auth:${mountId}] Refreshing session...`);
void initializeAuth();
};
@@ -143,38 +162,43 @@ export function AuthProvider({ children }: { children: ReactNode }) {
// Subscribe to auth state changes
const { data: { subscription } } = springAuth.onAuthStateChange(
async (event: AuthChangeEvent, newSession: Session | null) => {
if (!mounted) return;
if (!mounted) {
console.log(`[Auth:${mountId}] ⚠️ Auth state change ignored (unmounted): ${event}`);
return;
}
console.debug('[Auth] Auth state change:', {
event,
hasSession: !!newSession,
userId: newSession?.user?.id,
email: newSession?.user?.email,
timestamp: new Date().toISOString(),
});
console.log(`[Auth:${mountId}] ════════════════════════════════════`);
console.log(`[Auth:${mountId}] 📢 Auth state change event: ${event}`);
console.log(`[Auth:${mountId}] Has session: ${!!newSession}`);
console.log(`[Auth:${mountId}] User: ${newSession?.user?.email || 'none'}`);
console.log(`[Auth:${mountId}] Timestamp: ${new Date().toISOString()}`);
// Schedule state update
setTimeout(() => {
if (mounted) {
console.log(`[Auth:${mountId}] Applying session update (event: ${event})`);
setSession(newSession);
setError(null);
// Handle specific events
if (event === 'SIGNED_OUT') {
console.debug('[Auth] User signed out, clearing session');
console.log(`[Auth:${mountId}] ✓ User signed out, session cleared`);
} else if (event === 'SIGNED_IN') {
console.debug('[Auth] User signed in successfully');
console.log(`[Auth:${mountId}] ✓ User signed in successfully`);
} else if (event === 'TOKEN_REFRESHED') {
console.debug('[Auth] Token refreshed');
console.log(`[Auth:${mountId}] ✓ Token refreshed`);
} else if (event === 'USER_UPDATED') {
console.debug('[Auth] User updated');
console.log(`[Auth:${mountId}] ✓ User updated`);
}
} else {
console.log(`[Auth:${mountId}] ⚠️ Session update skipped (unmounted during timeout)`);
}
}, 0);
}
);
return () => {
console.log(`[Auth:${mountId}] 🔴 AuthProvider unmounting`);
mounted = false;
window.removeEventListener('jwt-available', handleJwtAvailable);
subscription.unsubscribe();
@@ -312,6 +312,9 @@ class SpringAuthClient {
*/
async signOut(): Promise<{ error: AuthError | null }> {
try {
if (typeof window !== 'undefined') {
window.sessionStorage.setItem('stirling_sso_auto_login_logged_out', '1');
}
const response = await apiClient.post('/api/v1/auth/logout', null, {
headers: {
'X-XSRF-TOKEN': this.getCsrfToken() || '',