Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added feature for oidc auto redirection #6066

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@ OPENID_NAME_CLAIM=

OPENID_BUTTON_LABEL=
OPENID_IMAGE_URL=
# Set to true to automatically redirect to the OpenID provider when a user visits the login page
# This will bypass the login form completely for users, only use this if OpenID is your only authentication method
OPENID_AUTO_REDIRECT=false

# LDAP
LDAP_URL=
Expand Down
1 change: 1 addition & 0 deletions api/server/routes/__tests__/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ afterEach(() => {
delete process.env.OPENID_ISSUER;
delete process.env.OPENID_SESSION_SECRET;
delete process.env.OPENID_BUTTON_LABEL;
delete process.env.OPENID_AUTO_REDIRECT;
delete process.env.OPENID_AUTH_URL;
delete process.env.GITHUB_CLIENT_ID;
delete process.env.GITHUB_CLIENT_SECRET;
Expand Down
1 change: 1 addition & 0 deletions api/server/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ router.get('/', async function (req, res) {
!!process.env.OPENID_SESSION_SECRET,
openidLabel: process.env.OPENID_BUTTON_LABEL || 'Continue with OpenID',
openidImageUrl: process.env.OPENID_IMAGE_URL,
openidAutoRedirect: isEnabled(process.env.OPENID_AUTO_REDIRECT),
serverDomain: process.env.DOMAIN_SERVER || 'http://localhost:3080',
emailLoginEnabled,
registrationEnabled: !ldap?.enabled && isEnabled(process.env.ALLOW_REGISTRATION),
Expand Down
11 changes: 10 additions & 1 deletion api/server/routes/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
return;
}
await setAuthTokens(req.user._id, res);

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
// On successful login, let's clear any openid redirect flags
res.cookie('successful_login', 'true', {

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
maxAge: 1000, // very short-lived, just for client-side detection
httpOnly: false // client needs to read this

Check failure

Code scanning / ESLint

Require or disallow trailing commas Error

Missing trailing comma.
});

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
res.redirect(domains.client);
} catch (err) {
logger.error('Error in setting authentication tokens:', err);
Expand All @@ -31,7 +38,9 @@
router.get('/error', (req, res) => {
// A single error message is pushed by passport when authentication fails.
logger.error('Error in OAuth authentication:', { message: req.session.messages.pop() });
res.redirect(`${domains.client}/login`);

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
// Redirect to login page with auth_failed parameter to prevent infinite redirect loops
res.redirect(`${domains.client}/login?auth_failed=true`);
});

/**
Expand Down
44 changes: 43 additions & 1 deletion client/src/components/Auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
import { useOutletContext } from 'react-router-dom';
import { useEffect, useRef } from 'react';
import { useAuthContext } from '~/hooks/AuthContext';
import type { TLoginLayoutContext } from '~/common';
import { ErrorMessage } from '~/components/Auth/ErrorMessage';
import { getLoginError } from '~/utils';
import { getLoginError, shouldRedirectToOpenID, clearOpenIDRedirectFlag, getCookie } from '~/utils';
import { useLocalize } from '~/hooks';
import LoginForm from './LoginForm';

function Login() {
const localize = useLocalize();
const { error, setError, login } = useAuthContext();
const { startupConfig } = useOutletContext<TLoginLayoutContext>();
const redirectAttemptedRef = useRef(false);

// Auto-redirect to OpenID provider if enabled
// This is controlled by the OPENID_AUTO_REDIRECT environment variable
// When enabled, users will be automatically redirected to the OpenID provider
// without seeing the login form at all
useEffect(() => {
// Check for URL parameters that indicate a failed auth attempt
const urlParams = new URLSearchParams(window.location.search);
const authFailed = urlParams.get('auth_failed') === 'true';

// Use the utility function to determine if we should redirect
if (
shouldRedirectToOpenID({
redirectAttempted: redirectAttemptedRef.current,
openidLoginEnabled: startupConfig?.openidLoginEnabled,
openidAutoRedirect: startupConfig?.openidAutoRedirect,
serverDomain: startupConfig?.serverDomain,
authFailed
})
Comment on lines +32 to +33

Check failure

Code scanning / ESLint

Require or disallow trailing commas Error

Missing trailing comma.
) {
// Mark that we've attempted to redirect in this component instance
redirectAttemptedRef.current = true;

// Log and redirect
console.log('Auto-redirecting to OpenID provider...');
window.location.href = `${startupConfig?.serverDomain}/oauth/openid`;
}
}, [startupConfig]);

// Clear the redirect flag after successful login (when the cookie is present)
useEffect(() => {
const successfulLogin = getCookie('successful_login');
if (successfulLogin) {
// Clear the redirect flag in localStorage
clearOpenIDRedirectFlag();

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
// Clear the cookie since we've processed it
document.cookie = 'successful_login=; Max-Age=0; path=/;';
}
}, []);

return (
<>
Expand Down
68 changes: 68 additions & 0 deletions client/src/utils/localStorage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { LocalStorageKeys, TConversation } from 'librechat-data-provider';

// Key for tracking OpenID redirect attempts
export const OPENID_REDIRECT_KEY = 'openid_redirect_attempted';
// Cooldown period in milliseconds (5 minutes)
export const OPENID_REDIRECT_COOLDOWN = 5 * 60 * 1000;

export function getLocalStorageItems() {
const items = {
lastSelectedModel: localStorage.getItem(LocalStorageKeys.LAST_MODEL) ?? '',
Expand All @@ -24,6 +29,69 @@
};
}

/**
* Handles the OpenID redirect logic to prevent infinite redirect loops
* @param conditions Object containing conditions that must be met for redirect
* @returns Boolean indicating whether to proceed with the redirect
*/
export function shouldRedirectToOpenID({
redirectAttempted,
openidLoginEnabled,
openidAutoRedirect,
serverDomain,
authFailed = false,
}: {
redirectAttempted: boolean;
openidLoginEnabled?: boolean;
openidAutoRedirect?: boolean;
serverDomain?: string;
authFailed?: boolean;
}): boolean {
// Get timestamp of last redirect attempt from localStorage
const lastRedirectAttempt = localStorage.getItem(OPENID_REDIRECT_KEY);
const currentTime = Date.now();

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
// Only redirect if all conditions are met
if (
!redirectAttempted &&
openidLoginEnabled &&
openidAutoRedirect &&
serverDomain &&
!authFailed &&
(!lastRedirectAttempt || currentTime - parseInt(lastRedirectAttempt, 10) > OPENID_REDIRECT_COOLDOWN)
) {
// Store the current timestamp in localStorage
localStorage.setItem(OPENID_REDIRECT_KEY, currentTime.toString());
return true;
}

Check failure

Code scanning / ESLint

Disallow trailing whitespace at the end of lines Error

Trailing spaces not allowed.
return false;
}

/**
* Clears the OpenID redirect tracking flag
*/
export function clearOpenIDRedirectFlag(): void {
localStorage.removeItem(OPENID_REDIRECT_KEY);
}

/**
* Gets a cookie value by name
* @param name The name of the cookie
* @returns The cookie value or null if not found
*/
export function getCookie(name: string): string | null {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
const part = parts.pop();
if (part) {
return part.split(';').shift() || null;
}
}
return null;
}

export function clearLocalStorage(skipFirst?: boolean) {
const keys = Object.keys(localStorage);
keys.forEach((key) => {
Expand Down
1 change: 1 addition & 0 deletions packages/data-provider/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ export type TStartupConfig = {
appleLoginEnabled: boolean;
openidLabel: string;
openidImageUrl: string;
openidAutoRedirect: boolean;
/** LDAP Auth Configuration */
ldap?: {
/** LDAP enabled */
Expand Down
Loading