Authentication Redirect Preservation
Overview
The rts-common package now includes built-in support for preserving the original URL (including query parameters and hash fragments) during OIDC authentication flows. This ensures users return to the exact page they were trying to access after completing authentication.
How It Works
When a user visits a protected route while unauthenticated, the system automatically preserves their intended destination:
User visits: /dashboard?tab=analytics&period=7d
↓
URL stored in sessionStorage
↓
Redirected to OIDC login
↓
After authentication: /dashboard?tab=analytics&period=7d ✅Implementation
Automatic Behavior (No Configuration Required)
The AppBase component now automatically:
- Stores the URL before authentication redirect
- Restores the URL after successful authentication
- Preserves query parameters, hash fragments, and path
How It Works
1. URL Storage (Before Authentication)
When a user visits a protected route while unauthenticated:
// In AppBase component
useEffect(() => {
if (!isAuthenticated && !isLoading && !isOidcCallback()) {
storeRedirectUrl(); // Stores current URL to sessionStorage
}
}, [isAuthenticated, isLoading]);2. OIDC Configuration
The redirect URI is set to the base URL (OIDC spec compliant):
const oidcConfig: AuthProviderProps = {
authority: config?.oidcConfig?.authority || '',
client_id: config?.oidcConfig?.clientId || '',
redirect_uri: getBaseRedirectUri(), // e.g., https://app.com/
// ... other config
};3. URL Restoration (After Authentication)
After successful authentication callback:
onSigninCallback: () => {
const originalUrl = restoreRedirectUrl();
if (originalUrl) {
// Navigate to original URL with query params
window.history.replaceState({}, document.title, originalUrl);
} else {
// Fallback: clean up OIDC params
window.history.replaceState({}, document.title, window.location.pathname);
}
}API Reference
Exported Utilities
The package exports the following utilities for advanced use cases:
storeRedirectUrl()
Stores the current URL in sessionStorage.
import { storeRedirectUrl } from '@devdenvino/rts-common';
// Manually store URL before custom redirect
storeRedirectUrl();restoreRedirectUrl()
Retrieves and clears the stored URL.
import { restoreRedirectUrl } from '@devdenvino/rts-common';
const originalUrl = restoreRedirectUrl();
if (originalUrl) {
router.navigate({ to: originalUrl });
}getRedirectUrl()
Gets the stored URL without clearing it.
import { getRedirectUrl } from '@devdenvino/rts-common';
const storedUrl = getRedirectUrl();
console.log('User was trying to access:', storedUrl);clearRedirectUrl()
Clears the stored URL.
import { clearRedirectUrl } from '@devdenvino/rts-common';
clearRedirectUrl();isOidcCallback()
Checks if the current URL contains OIDC callback parameters.
import { isOidcCallback } from '@devdenvino/rts-common';
if (isOidcCallback()) {
console.log('This is an OIDC callback');
}getBaseRedirectUri()
Gets the base redirect URI (origin + pathname).
import { getBaseRedirectUri } from '@devdenvino/rts-common';
const baseUri = getBaseRedirectUri();
// Returns: https://app.com/path (without query params)buildRedirectUri(excludeParams?)
Builds a redirect URI with preserved query parameters.
import { buildRedirectUri } from '@devdenvino/rts-common';
// Exclude specific params
const uri = buildRedirectUri(['temp', 'debug']);
// Returns: https://app.com/path?keep=this (excludes 'temp' and 'debug')Usage Examples
Example 1: Basic Usage (Automatic)
No code changes required! Just use AppBase:
import { AppBase } from '@devdenvino/rts-common';
function App() {
return (
<AppBase>
<YourApp />
</AppBase>
);
}Query parameters are automatically preserved! ✅
Example 2: Custom Authentication Flow
If you have a custom authentication flow:
import { storeRedirectUrl, restoreRedirectUrl } from '@devdenvino/rts-common';
function CustomAuthButton() {
const handleLogin = () => {
// Store URL before custom redirect
storeRedirectUrl();
// Your custom login logic
window.location.href = '/custom-login';
};
return <button onClick={handleLogin}>Login</button>;
}
// After authentication
function AuthCallback() {
useEffect(() => {
const originalUrl = restoreRedirectUrl();
if (originalUrl) {
router.navigate({ to: originalUrl });
}
}, []);
return <div>Processing...</div>;
}Example 3: Conditional Redirect Preservation
import { storeRedirectUrl, getRedirectUrl } from '@devdenvino/rts-common';
function ProtectedRoute() {
const { isAuthenticated } = useAuth();
const location = useLocation();
useEffect(() => {
if (!isAuthenticated) {
// Only store specific routes
if (location.pathname.startsWith('/dashboard')) {
storeRedirectUrl();
}
}
}, [isAuthenticated, location]);
// ... rest of component
}Testing
Test Scenario 1: Query Parameters
- Log out
- Visit:
http://localhost:3000/dashboard?tab=analytics - Authenticate
- Expected: Return to
/dashboard?tab=analytics✅
Test Scenario 2: Hash Fragments
- Log out
- Visit:
http://localhost:3000/docs#section-3 - Authenticate
- Expected: Return to
/docs#section-3✅
Test Scenario 3: Complex URL
- Log out
- Visit:
http://localhost:3000/search?q=test&filter=active#results - Authenticate
- Expected: Return to
/search?q=test&filter=active#results✅
Debugging
Check browser console for logs:
[RTS Auth] Stored redirect URL: /dashboard?tab=analytics
[RTS Auth] Signin callback triggered
[RTS Auth] Restoring to original URL: /dashboard?tab=analyticsCheck sessionStorage:
// In browser console
sessionStorage.getItem('rts_auth_redirect_url')Technical Details
Storage Mechanism
- Uses
sessionStorage(cleared when tab closes) - Key:
rts_auth_redirect_url - Stores: Full path + query + hash
OIDC Compliance
redirect_uriset to base URL (no query params)- Complies with OAuth 2.0 / OIDC specifications
- Works with all standard OIDC providers
Security Considerations
- URLs stored in
sessionStorage(notlocalStorage) - Automatically cleared after use
- OIDC params (
code,state,session_state) excluded - No sensitive data should be in query parameters
Browser Compatibility
- All modern browsers (Chrome 49+, Firefox 44+, Safari 10+, Edge 14+)
- Uses standard Web APIs (sessionStorage, URLSearchParams)
- No polyfills required
Troubleshooting
Query Parameters Still Lost
Check:
- Verify you're using the latest version of
rts-common - Check browser console for
[RTS Auth]logs - Inspect sessionStorage for
rts_auth_redirect_url
Solution:
# Update rts-common
pnpm update @devdenvino/rts-commonInfinite Redirect Loop
Cause: OIDC callback detection failing
Solution:
// Clear sessionStorage
sessionStorage.clear();
// Or manually clear the redirect URL
import { clearRedirectUrl } from '@devdenvino/rts-common';
clearRedirectUrl();URL Not Restored
Check:
- Ensure
AppBaseis wrapping your app - Verify OIDC callback is triggering
- Check that URL was stored before redirect
Debug:
import { getRedirectUrl } from '@devdenvino/rts-common';
console.log('Stored URL:', getRedirectUrl());Features
- ✅ Automatic redirect preservation
- ✅ Exported auth-redirect utilities
- ✅ Integrated with AppBase component
- ✅ OIDC spec compliant redirect_uri
- ✅ Comprehensive logging for debugging
Support
For issues or questions:
- Check the troubleshooting guide
- Review browser console logs
- Open an issue in the rts-common repository