diff --git a/src/gui/src/initgui.gates.test.js b/src/gui/src/initgui.gates.test.js new file mode 100644 index 000000000..ae50a30c1 --- /dev/null +++ b/src/gui/src/initgui.gates.test.js @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest'; +import { readFileSync } from 'node:fs'; + +/** + * `initgui` runs the verification gates in two places: the token-in-URL path + * and the session-restore/login path. A gate added to one and not the other + * looks correct in review and is only visible by signing in the wrong way -- + * which is how the forced password change shipped running on neither the login + * form nor a normal reload. + */ +const src = readFileSync( + new URL('./initgui.js', import.meta.url), + 'utf8', +); + +const countGates = (flag) => + src.split(`whoami.${flag}`).length - 1; + +describe('the verification gates in initgui', () => { + it('runs the email gate on both paths', () => { + expect(countGates('requires_email_confirmation')).toBe(2); + }); + + it('runs the card gate on both paths', () => { + expect(countGates('requires_card_verification')).toBe(2); + }); + + it('runs the forced password change on both paths', () => { + // A seat signs in through the login form; a gate only on the + // token-in-URL path never fires for it. + expect(countGates('requires_password_change')).toBe(2); + }); + + it('loops each gate until it is cleared, so none can be dismissed', () => { + // Every gate is a `do { ... } while (!x)`; a plain `if` would let the + // window close and the account through. + const opens = src.split('UIWindowPasswordChangeRequired({').length - 1; + expect(opens).toBe(2); + for (const chunk of src.split('UIWindowPasswordChangeRequired({').slice(1)) { + expect(chunk).toContain('} while (!changed);'); + } + }); +}); diff --git a/src/gui/src/initgui.js b/src/gui/src/initgui.js index ce3e4bea4..9bb98a701 100644 --- a/src/gui/src/initgui.js +++ b/src/gui/src/initgui.js @@ -2078,6 +2078,21 @@ window.initgui = async function (options) { }); } while (!is_verified); } + // Last, matching assertVerifiedAccount's order. + if (whoami.requires_password_change) { + let changed; + do { + changed = await UIWindowPasswordChangeRequired({ + show_close_button: false, + stay_on_top: true, + has_head: false, + window_options: { + is_draggable: false, + cover_page: window.is_embedded, + }, + }); + } while (!changed); + } await window.update_auth_data( whoami.token || window.auth_token, whoami,