-
Notifications
You must be signed in to change notification settings - Fork 350
Prototype silent authentication #992
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
base: main
Are you sure you want to change the base?
Changes from all commits
7b363d4
4f3e0ef
f66a8e3
4de26c3
ec663b6
4cecfec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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", | ||
|
@@ -551,6 +553,9 @@ class Base(Configuration): | |
environ_name="OIDC_STORE_REFRESH_TOKEN_KEY", | ||
environ_prefix=None, | ||
) | ||
OIDC_REDIRECT_FIELD_NAME = values.Value( | ||
"returnTo", environ_name="OIDC_REDIRECT_FIELD_NAME", environ_prefix=None | ||
) | ||
Comment on lines
+556
to
+558
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, should be added in |
||
|
||
# WARNING: Enabling this setting allows multiple user accounts to share the same email | ||
# address. This may cause security issues and is not recommended for production use when | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -38,7 +38,10 @@ export const getConfig = async (): Promise<ConfigResponse> => { | |||||
const response = await fetchAPI(`config/`); | ||||||
|
||||||
if (!response.ok) { | ||||||
throw new APIError('Failed to get the doc', await errorCauses(response)); | ||||||
throw new APIError( | ||||||
'Failed to get the configurations', | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
await errorCauses(response), | ||||||
); | ||||||
} | ||||||
|
||||||
const config = response.json() as Promise<ConfigResponse>; | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
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'; | ||
|
||
|
@@ -16,6 +21,16 @@ 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be in const in the conf I think, for everybody to know that it is something that we could configure, wdyt? |
||
|
||
while (window.location.href === currentLocation) { | ||
await new Promise((resolve) => setTimeout(resolve, 100)); | ||
} | ||
Comment on lines
+27
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
} | ||
|
||
if (!response.ok) { | ||
throw new APIError( | ||
`Couldn't fetch user data: ${response.statusText}`, | ||
|
@@ -34,6 +49,13 @@ export function useAuthQuery( | |
queryKey: [KEY_AUTH], | ||
queryFn: getMe, | ||
staleTime: 1000 * 60 * 15, // 15 minutes | ||
retry: (failureCount, error) => { | ||
// we assume that a 401 means the user is not logged in | ||
if (error.status == 401) { | ||
return false; | ||
} | ||
return failureCount < DEFAULT_QUERY_RETRY; | ||
}, | ||
...queryConfig, | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,12 @@ | ||
import { terminateCrispSession } from '@/services/Crisp'; | ||
|
||
import { LOGIN_URL, LOGOUT_URL, PATH_AUTH_LOCAL_STORAGE } from './conf'; | ||
import { LOGIN_URL, LOGOUT_URL } from './conf'; | ||
|
||
export const getAuthUrl = () => { | ||
const path_auth = localStorage.getItem(PATH_AUTH_LOCAL_STORAGE); | ||
if (path_auth) { | ||
localStorage.removeItem(PATH_AUTH_LOCAL_STORAGE); | ||
return path_auth; | ||
} | ||
}; | ||
|
||
export const setAuthUrl = () => { | ||
if (window.location.pathname !== '/') { | ||
localStorage.setItem(PATH_AUTH_LOCAL_STORAGE, window.location.pathname); | ||
} | ||
}; | ||
|
||
export const gotoLogin = (withRedirect = true) => { | ||
if (withRedirect) { | ||
setAuthUrl(); | ||
} | ||
|
||
window.location.replace(LOGIN_URL); | ||
export const gotoLogin = (returnTo = '/', isSilent = false) => { | ||
const authenticateUrl = | ||
LOGIN_URL + | ||
`?silent=${encodeURIComponent(isSilent)}&returnTo=${window.location.origin + returnTo}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The redirection does not seems to work, |
||
window.location.replace(authenticateUrl); | ||
}; | ||
|
||
export const gotoLogout = () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add them in
/docs/env.md
.