mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-14 02:55:41 +00:00
chore(backend): bump allauth version (#9714)
* bump allauth * add trust * add device trust handling * fix style * Update api_version.py --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
@ -1,12 +1,15 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 343
|
INVENTREE_API_VERSION = 344
|
||||||
|
|
||||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
INVENTREE_API_TEXT = """
|
||||||
|
v344 -> 2025-06-02 : https://github.com/inventree/InvenTree/pull/9714
|
||||||
|
- Updates alauth version and adds device trust as a factor
|
||||||
|
|
||||||
v343 -> 2025-06-02 : https://github.com/inventree/InvenTree/pull/9717
|
v343 -> 2025-06-02 : https://github.com/inventree/InvenTree/pull/9717
|
||||||
- Add ISO currency codes to the description text for currency options
|
- Add ISO currency codes to the description text for currency options
|
||||||
|
|
||||||
|
@ -1327,6 +1327,7 @@ ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
|
|||||||
|
|
||||||
HEADLESS_ONLY = True
|
HEADLESS_ONLY = True
|
||||||
HEADLESS_TOKEN_STRATEGY = 'InvenTree.auth_overrides.DRFTokenStrategy'
|
HEADLESS_TOKEN_STRATEGY = 'InvenTree.auth_overrides.DRFTokenStrategy'
|
||||||
|
HEADLESS_CLIENTS = 'browser'
|
||||||
MFA_ENABLED = get_boolean_setting(
|
MFA_ENABLED = get_boolean_setting(
|
||||||
'INVENTREE_MFA_ENABLED', 'mfa_enabled', True
|
'INVENTREE_MFA_ENABLED', 'mfa_enabled', True
|
||||||
) # TODO re-implement
|
) # TODO re-implement
|
||||||
@ -1336,6 +1337,7 @@ MFA_SUPPORTED_TYPES = get_setting(
|
|||||||
['totp', 'recovery_codes'],
|
['totp', 'recovery_codes'],
|
||||||
typecast=list,
|
typecast=list,
|
||||||
)
|
)
|
||||||
|
MFA_TRUST_ENABLED = True
|
||||||
|
|
||||||
LOGOUT_REDIRECT_URL = get_setting(
|
LOGOUT_REDIRECT_URL = get_setting(
|
||||||
'INVENTREE_LOGOUT_REDIRECT_URL', 'logout_redirect_url', 'index'
|
'INVENTREE_LOGOUT_REDIRECT_URL', 'logout_redirect_url', 'index'
|
||||||
|
@ -423,8 +423,8 @@ django==4.2.21 \
|
|||||||
# djangorestframework
|
# djangorestframework
|
||||||
# djangorestframework-simplejwt
|
# djangorestframework-simplejwt
|
||||||
# drf-spectacular
|
# drf-spectacular
|
||||||
django-allauth[mfa, openid, saml, socialaccount]==65.4.1 \
|
django-allauth[mfa, openid, saml, socialaccount]==65.9.0 \
|
||||||
--hash=sha256:60b32aef7dbbcc213319aa4fd8f570e985266ea1162ae6ef7a26a24efca85c8c
|
--hash=sha256:a06bca9974df44321e94c33bcf770bb6f924d1a44b57defbce4d7ec54a55483e
|
||||||
# via -r src/backend/requirements.in
|
# via -r src/backend/requirements.in
|
||||||
django-cleanup==9.0.0 \
|
django-cleanup==9.0.0 \
|
||||||
--hash=sha256:19f8b0e830233f9f0f683b17181f414672a0f48afe3ea3cc80ba47ae40ad880c \
|
--hash=sha256:19f8b0e830233f9f0f683b17181f414672a0f48afe3ea3cc80ba47ae40ad880c \
|
||||||
|
@ -30,6 +30,7 @@ export enum ApiEndpoints {
|
|||||||
auth_recovery = 'auth/v1/account/authenticators/recovery-codes',
|
auth_recovery = 'auth/v1/account/authenticators/recovery-codes',
|
||||||
auth_mfa_reauthenticate = 'auth/v1/auth/2fa/reauthenticate',
|
auth_mfa_reauthenticate = 'auth/v1/auth/2fa/reauthenticate',
|
||||||
auth_totp = 'auth/v1/account/authenticators/totp',
|
auth_totp = 'auth/v1/account/authenticators/totp',
|
||||||
|
auth_trust = 'auth/v1/auth/2fa/trust',
|
||||||
auth_reauthenticate = 'auth/v1/auth/reauthenticate',
|
auth_reauthenticate = 'auth/v1/auth/reauthenticate',
|
||||||
auth_email = 'auth/v1/account/email',
|
auth_email = 'auth/v1/account/email',
|
||||||
auth_email_verify = 'auth/v1/auth/email/verify',
|
auth_email_verify = 'auth/v1/auth/email/verify',
|
||||||
|
@ -13,7 +13,8 @@ export enum FlowEnum {
|
|||||||
ProviderToken = 'provider_token',
|
ProviderToken = 'provider_token',
|
||||||
MfaAuthenticate = 'mfa_authenticate',
|
MfaAuthenticate = 'mfa_authenticate',
|
||||||
Reauthenticate = 'reauthenticate',
|
Reauthenticate = 'reauthenticate',
|
||||||
MfaReauthenticate = 'mfa_reauthenticate'
|
MfaReauthenticate = 'mfa_reauthenticate',
|
||||||
|
MfaTrust = 'mfa_trust'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Flow {
|
export interface Flow {
|
||||||
|
@ -262,26 +262,19 @@ export function handleReset(
|
|||||||
export function handleMfaLogin(
|
export function handleMfaLogin(
|
||||||
navigate: NavigateFunction,
|
navigate: NavigateFunction,
|
||||||
location: Location<any>,
|
location: Location<any>,
|
||||||
values: { code: string },
|
values: { code: string; remember?: boolean },
|
||||||
setError: (message: string | undefined) => void
|
setError: (message: string | undefined) => void
|
||||||
) {
|
) {
|
||||||
const { setAuthenticated, fetchUserState } = useUserState.getState();
|
|
||||||
const { setAuthContext } = useServerApiState.getState();
|
const { setAuthContext } = useServerApiState.getState();
|
||||||
|
|
||||||
authApi(apiUrl(ApiEndpoints.auth_login_2fa), undefined, 'post', {
|
authApi(apiUrl(ApiEndpoints.auth_login_2fa), undefined, 'post', {
|
||||||
code: values.code
|
code: values.code
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setError(undefined);
|
handleSuccessFullAuth(response, navigate, location, setError);
|
||||||
setAuthContext(response.data?.data);
|
|
||||||
setAuthenticated();
|
|
||||||
|
|
||||||
fetchUserState().finally(() => {
|
|
||||||
observeProfile();
|
|
||||||
followRedirect(navigate, location?.state);
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
// Already logged in, but with a different session
|
||||||
if (err?.response?.status == 409) {
|
if (err?.response?.status == 409) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: t`Already logged in`,
|
title: t`Already logged in`,
|
||||||
@ -289,6 +282,19 @@ export function handleMfaLogin(
|
|||||||
color: 'red',
|
color: 'red',
|
||||||
autoClose: false
|
autoClose: false
|
||||||
});
|
});
|
||||||
|
// MFA trust flow pending
|
||||||
|
} else if (err?.response?.status == 401) {
|
||||||
|
const mfa_trust = err.response.data.data.flows.find(
|
||||||
|
(flow: any) => flow.id == FlowEnum.MfaTrust
|
||||||
|
);
|
||||||
|
if (mfa_trust?.is_pending) {
|
||||||
|
setAuthContext(err.response.data.data);
|
||||||
|
authApi(apiUrl(ApiEndpoints.auth_trust), undefined, 'post', {
|
||||||
|
trust: values.remember ?? false
|
||||||
|
}).then((response) => {
|
||||||
|
handleSuccessFullAuth(response, navigate, location, setError);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const errors = err.response?.data?.errors;
|
const errors = err.response?.data?.errors;
|
||||||
let msg = t`An error occurred`;
|
let msg = t`An error occurred`;
|
||||||
@ -351,6 +357,35 @@ export const checkLoginState = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function handleSuccessFullAuth(
|
||||||
|
response?: any,
|
||||||
|
navigate?: NavigateFunction,
|
||||||
|
location?: Location<any>,
|
||||||
|
setError?: (message: string | undefined) => void
|
||||||
|
) {
|
||||||
|
const { setAuthenticated, fetchUserState } = useUserState.getState();
|
||||||
|
const { setAuthContext } = useServerApiState.getState();
|
||||||
|
|
||||||
|
if (setError) {
|
||||||
|
// If an error function is provided, clear any previous errors
|
||||||
|
setError(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.data?.data) {
|
||||||
|
setAuthContext(response.data?.data);
|
||||||
|
}
|
||||||
|
setAuthenticated();
|
||||||
|
|
||||||
|
fetchUserState().finally(() => {
|
||||||
|
observeProfile();
|
||||||
|
fetchGlobalStates(navigate);
|
||||||
|
|
||||||
|
if (navigate) {
|
||||||
|
followRedirect(navigate, location?.state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the value of the CSRF cookie, if available
|
* Return the value of the CSRF cookie, if available
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { Button, TextInput } from '@mantine/core';
|
import { Button, Checkbox, TextInput } from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
@ -8,7 +8,7 @@ import { handleMfaLogin } from '../../functions/auth';
|
|||||||
import { Wrapper } from './Layout';
|
import { Wrapper } from './Layout';
|
||||||
|
|
||||||
export default function Mfa() {
|
export default function Mfa() {
|
||||||
const simpleForm = useForm({ initialValues: { code: '' } });
|
const simpleForm = useForm({ initialValues: { code: '', remember: false } });
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [loginError, setLoginError] = useState<string | undefined>(undefined);
|
const [loginError, setLoginError] = useState<string | undefined>(undefined);
|
||||||
@ -23,6 +23,12 @@ export default function Mfa() {
|
|||||||
{...simpleForm.getInputProps('code')}
|
{...simpleForm.getInputProps('code')}
|
||||||
error={loginError}
|
error={loginError}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={t`Remember this device`}
|
||||||
|
name='remember'
|
||||||
|
description={t`If enabled, you will not be asked for MFA on this device for 30 days.`}
|
||||||
|
{...simpleForm.getInputProps('remember', { type: 'checkbox' })}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type='submit'
|
type='submit'
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
Reference in New Issue
Block a user