mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 16:13:15 +00:00
fix: stay signed in after changing the password in settings
After a password change the session kept authenticating with the old password: the JMAP client's Basic header, the server-side context the Stalwart management calls use, and the remembered login. The next request was refused and the user was sent back to the login page. A successful password change now switches all three to the new password for Basic-auth sessions. OAuth sessions are unaffected by this change.
This commit is contained in:
@@ -9,9 +9,12 @@ vi.mock('@/lib/stalwart/jmap-passthrough', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const updateBasicPassword = vi.fn(async (_password: string) => {});
|
||||
|
||||
vi.mock('@/stores/auth-store', () => ({
|
||||
useAuthStore: {
|
||||
getState: () => ({
|
||||
updateBasicPassword,
|
||||
client: {
|
||||
getAccountId: () => 'acc-primary',
|
||||
hasAccountCapability: (cap: string) => cap === 'urn:stalwart:jmap',
|
||||
@@ -270,6 +273,7 @@ describe('account-security-store', () => {
|
||||
]);
|
||||
|
||||
await useAccountSecurityStore.getState().changePassword('old', 'new', ' 123456 ');
|
||||
expect(updateBasicPassword).toHaveBeenCalledWith('new');
|
||||
|
||||
const args = mockedJmap.mock.calls[0][0][0][1];
|
||||
expect(args.update.singleton).toEqual({ currentSecret: 'old', secret: 'new', 'otpAuth/otpCode': '123456' });
|
||||
|
||||
@@ -579,6 +579,13 @@ export const useAccountSecurityStore = create<AccountSecurityState>()((set, get)
|
||||
],
|
||||
]);
|
||||
requireAccountPasswordUpdate(responses, 'Failed to change password');
|
||||
// The session still authenticates with the old password; switch it over
|
||||
// so the next request does not bounce the user to the login page.
|
||||
try {
|
||||
await useAuthStore.getState().updateBasicPassword(newPassword);
|
||||
} catch (error) {
|
||||
debug.warn('auth', 'Password changed, but the session could not switch to it:', error);
|
||||
}
|
||||
set({ isSaving: false });
|
||||
} catch (error) {
|
||||
set({
|
||||
|
||||
@@ -80,6 +80,12 @@ interface AuthState {
|
||||
checkAuth: () => Promise<void>;
|
||||
clearError: () => void;
|
||||
syncIdentities: () => void;
|
||||
/**
|
||||
* Keep a Basic-auth session working after the user changed the password:
|
||||
* the client, the server-side management context and the remembered
|
||||
* session still hold the old one, which the server now refuses.
|
||||
*/
|
||||
updateBasicPassword: (newPassword: string) => Promise<void>;
|
||||
refreshIdentities: () => Promise<void>;
|
||||
getClientForAccount: (accountId: string) => JMAPClient | undefined;
|
||||
getAllConnectedClients: () => Map<string, JMAPClient>;
|
||||
@@ -2378,6 +2384,17 @@ export const useAuthStore = create<AuthState>()(
|
||||
|
||||
clearError: () => set({ error: null }),
|
||||
|
||||
updateBasicPassword: async (newPassword) => {
|
||||
const { client, activeAccountId } = get();
|
||||
const account = activeAccountId ? useAccountStore.getState().getAccountById(activeAccountId) : undefined;
|
||||
if (!client || !account || account.authMode !== 'basic') return;
|
||||
client.updateBasicAuth(newPassword);
|
||||
await syncStalwartAuthContext(account.serverUrl, account.username, client.getAuthHeader(), account.cookieSlot);
|
||||
if (account.rememberMe || IS_LITE) {
|
||||
await persistBasicSession(account.cookieSlot, account.serverUrl, account.username, newPassword);
|
||||
}
|
||||
},
|
||||
|
||||
syncIdentities: () => {
|
||||
const identityState = useIdentityStore.getState();
|
||||
const identities = identityState.identities;
|
||||
|
||||
Reference in New Issue
Block a user