🚸(auth) implement first draft of silent login with prompt=None

Add initial naive implementation of silent login functionality using
prompt=None parameter to request OIDC provider to skip login screen and
return proper HTTP error response instead.
This commit is contained in:
lebaudantoine
2025-05-25 00:10:09 +02:00
parent 4de26c331b
commit ec663b601f
4 changed files with 54 additions and 3 deletions
+2
View File
@@ -467,6 +467,8 @@ class Base(Configuration):
)
# OIDC - Authorization Code Flow
OIDC_AUTHENTICATE_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationRequestView"
OIDC_CALLBACK_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationCallbackView"
OIDC_CREATE_USER = values.BooleanValue(
default=True,
environ_name="OIDC_CREATE_USER",
@@ -2,6 +2,10 @@ import { UseQueryOptions, useQuery } from '@tanstack/react-query';
import { APIError, errorCauses, fetchAPI } from '@/api';
import { DEFAULT_QUERY_RETRY } from '@/core';
import {
attemptSilentLogin,
canAttemptSilentLogin,
} from '@/features/auth/silentLogin';
import { User } from './types';
@@ -18,6 +22,15 @@ import { User } from './types';
export const getMe = async (): Promise<User> => {
const response = await fetchAPI(`users/me/`);
if (!response.ok && response.status == 401 && canAttemptSilentLogin()) {
const currentLocation = window.location.href;
attemptSilentLogin(3600);
while (window.location.href === currentLocation) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
if (!response.ok) {
throw new APIError(
`Couldn't fetch user data: ${response.statusText}`,
@@ -0,0 +1,35 @@
import { gotoLogin } from '@/features/auth';
const SILENT_LOGIN_RETRY_KEY = 'silent-login-retry';
const isRetryAllowed = () => {
const lastRetryDate = localStorage.getItem(SILENT_LOGIN_RETRY_KEY);
if (!lastRetryDate) {
return true;
}
const now = new Date();
return now.getTime() > Number(lastRetryDate);
};
const setNextRetryTime = (retryIntervalInSeconds: number) => {
const now = new Date();
const nextRetryTime = now.getTime() + retryIntervalInSeconds * 1000;
localStorage.setItem(SILENT_LOGIN_RETRY_KEY, String(nextRetryTime));
};
const initiateSilentLogin = () => {
const currentPath = window.location.pathname;
gotoLogin(currentPath, true);
};
export const canAttemptSilentLogin = () => {
return isRetryAllowed();
};
export const attemptSilentLogin = (retryIntervalInSeconds: number) => {
if (!isRetryAllowed()) {
return;
}
setNextRetryTime(retryIntervalInSeconds);
initiateSilentLogin();
};
@@ -1,10 +1,11 @@
import { backendUrl } from '@/api';
import { terminateCrispSession } from '@/services/Crisp';
import { LOGIN_URL, LOGOUT_URL } from './conf';
export const gotoLogin = (returnTo = '/') => {
const authenticateUrl = LOGIN_URL + `?returnTo=${backendUrl() + returnTo}`;
export const gotoLogin = (returnTo = '/', isSilent = false) => {
const authenticateUrl =
LOGIN_URL +
`?silent=${encodeURIComponent(isSilent)}&returnTo=${window.location.origin + returnTo}`;
window.location.replace(authenticateUrl);
};