Skip to content

feat(auth): Show Last used label on SSO sign-in method#17093

Merged
charlesBochet merged 14 commits intotwentyhq:mainfrom
ManikanthMartha:feature/last-used-sso
Jan 13, 2026
Merged

feat(auth): Show Last used label on SSO sign-in method#17093
charlesBochet merged 14 commits intotwentyhq:mainfrom
ManikanthMartha:feature/last-used-sso

Conversation

@ManikanthMartha
Copy link
Copy Markdown
Contributor

@ManikanthMartha ManikanthMartha commented Jan 12, 2026

Fixes #17006

Changes

Added lastAuthenticateWorkspaceSsoMethodState Recoil atom to track the last used SSO method, persisted in localStorage
Updated SignInUpWithGoogle component to display a "Last" pill badge when Google was the last auth method
Updated SignInUpWithMicrosoft component to display a "Last" pill badge when Microsoft was the last auth method
Updated SignInUpWithSSO component to display a "Last" pill badge when SSO was the last auth method.

The state is saved after successful authentication redirect, ensuring it persists across sessions

Implementation Details

Uses existing localStorageEffect for persistence across browser sessions
Leverages the existing Pill component from twenty-ui with blue accent color
The label is positioned on the right border of the button using absolute positioning
State is saved in the useAuth hook during Google/Microsoft sign-in and in the SSO login component

image

Note

Highlights the most recently used authentication method and preserves it across sessions.

  • Introduces AuthenticatedMethod enum and lastAuthenticatedMethodState (persisted via localStorageEffect) and preserves it through useAuth.clearSession
  • Displays a "Last" pill via LastUsedPill on SignInUpWithGoogle, SignInUpWithMicrosoft, SignInUpWithSSO, and SignInUpWithCredentials when appropriate; adds StyledSSOButtonContainer for badge positioning
  • Adds useHasMultipleAuthMethods to detect when to show the badge; threads isGlobalScope to relevant components
  • Sets last-used method on click/submit in SSO and credentials flows (useSignInUp, SSO button handlers)
  • Refactors SignInUpGlobalScopeForm to use SignInUpWithCredentials and updates SignInUp title logic for global scope (e.g., "Welcome to Twenty")

Written by Cursor Bugbot for commit 70d5527. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings January 12, 2026 11:43
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds visual indicators ("Last" badges) to authentication buttons to help users identify which SSO method they used previously. The implementation creates a new Recoil atom that persists the last authenticated method to localStorage and displays a blue pill badge on the corresponding sign-in button.

Changes:

  • Added lastAuthenticatedMethodState Recoil atom with localStorage persistence to track last used auth method
  • Updated Google, Microsoft, and SSO sign-in components to show "Last" pill badges when they were the last used method
  • Modified logout flow in useAuth to preserve the last authenticated method across sessions

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/twenty-front/src/modules/auth/states/lastAuthenticatedMethodState.ts Defines new Recoil atom for tracking last authenticated method with localStorage persistence
packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpWithSSO.tsx Adds "Last" pill badge display and sets auth method on SSO button click
packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpWithMicrosoft.tsx Adds "Last" pill badge display and sets auth method on Microsoft button click
packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpWithGoogle.tsx Adds "Last" pill badge display and sets auth method on Google button click
packages/twenty-front/src/modules/auth/hooks/useAuth.ts Preserves last authenticated method during logout by manually saving/restoring from localStorage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +45 to +49
// Save to localStorage synchronously before redirect to ensure it persists
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
JSON.stringify('sso' as LastAuthenticatedMethod),
);
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for saving the last authenticated method to localStorage is duplicated across all three sign-in components (SSO, Microsoft, and Google). This manual localStorage manipulation bypasses the Recoil atom's effects. Consider using useSetRecoilState to set the lastAuthenticatedMethodState value instead, which would automatically handle persistence through the localStorageEffect and eliminate code duplication.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +31
const StyledButtonContainer = styled.div`
position: relative;
width: 100%;
`;

const StyledLastUsedPill = styled(Pill)`
position: absolute;
right: -${({ theme }) => theme.spacing(2)};
top: -${({ theme }) => theme.spacing(2)};
background: ${({ theme }) => theme.color.blue};
color: ${({ theme }) => theme.font.color.inverted};
`;
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styled components StyledButtonContainer and StyledLastUsedPill are duplicated. This is the same issue as in the other sign-in components - these should be extracted into a shared styled component file.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +56
const handleClick = () => {
// Save to localStorage synchronously before redirect to ensure it persists
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
JSON.stringify('google' as LastAuthenticatedMethod),
);
signInWithGoogle({ action });
};
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The localStorage manipulation logic is duplicated. Instead of directly using localStorage.setItem with JSON.stringify, consider using useSetRecoilState to update the lastAuthenticatedMethodState, which would handle persistence automatically through the existing localStorageEffect.

Copilot uses AI. Check for mistakes.
variant={signInUpStep === SignInUpStep.Init ? undefined : 'secondary'}
fullWidth
/>
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pill label displays "Last" but should display "Last used" to match the PR description and be more descriptive.

Suggested change
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
{isLastUsed && <StyledLastUsedPill label={t`Last used`} />}

Copilot uses AI. Check for mistakes.
variant={signInUpStep === SignInUpStep.Init ? undefined : 'secondary'}
fullWidth
/>
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pill label displays "Last" but should display "Last used" to match the PR description and be more descriptive.

Suggested change
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
{isLastUsed && <StyledLastUsedPill label={t`Last used`} />}

Copilot uses AI. Check for mistakes.
Comment on lines +21 to +32
const StyledButtonContainer = styled.div`
position: relative;
width: 100%;
`;

const StyledLastUsedPill = styled(Pill)`
position: absolute;
right: -${({ theme }) => theme.spacing(2)};
top: -${({ theme }) => theme.spacing(2)};
background: ${({ theme }) => theme.color.blue};
color: ${({ theme }) => theme.font.color.inverted};
`;
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styled components StyledButtonContainer and StyledLastUsedPill are duplicated across SignInUpWithSSO, SignInUpWithMicrosoft, and SignInUpWithGoogle components. Consider extracting these into a shared styled component file to reduce code duplication and ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +52
const handleClick = () => {
// Save to localStorage synchronously before redirect to ensure it persists
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
JSON.stringify('microsoft' as LastAuthenticatedMethod),
);
signInWithMicrosoft({ action });
};
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The localStorage manipulation logic is duplicated. Instead of directly using localStorage.setItem with JSON.stringify, consider using useSetRecoilState to update the lastAuthenticatedMethodState, which would handle persistence automatically through the existing localStorageEffect.

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +37
const StyledButtonContainer = styled.div`
position: relative;
width: 100%;
`;

const StyledLastUsedPill = styled(Pill)`
position: absolute;
right: -${({ theme }) => theme.spacing(2)};
top: -${({ theme }) => theme.spacing(2)};
background: ${({ theme }) => theme.color.blue};
color: ${({ theme }) => theme.font.color.inverted};
`;
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styled components StyledButtonContainer and StyledLastUsedPill are duplicated. This is the same issue as in the other sign-in components - these should be extracted into a shared styled component file.

Copilot uses AI. Check for mistakes.
variant={signInUpStep === SignInUpStep.Init ? undefined : 'secondary'}
fullWidth
/>
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pill label displays "Last" but according to the PR title and description, it should display "Last used". Consider updating the label to be more descriptive and match the intended design.

Suggested change
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
{isLastUsed && <StyledLastUsedPill label={t`Last used`} />}

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 12, 2026

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:32091

This environment will automatically shut down when the PR is closed or after 5 hours.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/twenty-front/src/modules/auth/hooks/useAuth.ts">

<violation number="1" location="packages/twenty-front/src/modules/auth/hooks/useAuth.ts:181">
P2: Sign-out uses unguarded localStorage/sessionStorage calls that can throw (private mode/quota), aborting cleanup before tokens/cookies are cleared.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +181 to +194
const lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);

sessionStorage.clear();
localStorage.clear();

// Restore the last authenticated method after clearing
if (lastAuthenticatedMethod) {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
lastAuthenticatedMethod,
);
}
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Sign-out uses unguarded localStorage/sessionStorage calls that can throw (private mode/quota), aborting cleanup before tokens/cookies are cleared.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/auth/hooks/useAuth.ts, line 181:

<comment>Sign-out uses unguarded localStorage/sessionStorage calls that can throw (private mode/quota), aborting cleanup before tokens/cookies are cleared.</comment>

<file context>
@@ -176,8 +177,22 @@ export const useAuth = () => {
         goToRecoilSnapshot(initialSnapshot);
 
+        // Preserve the last authenticated method before clearing localStorage
+        const lastAuthenticatedMethod = localStorage.getItem(
+          LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
+        );
</file context>
Suggested change
const lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);
sessionStorage.clear();
localStorage.clear();
// Restore the last authenticated method after clearing
if (lastAuthenticatedMethod) {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
lastAuthenticatedMethod,
);
}
let lastAuthenticatedMethod: string | null = null;
try {
lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);
} catch {
lastAuthenticatedMethod = null;
}
try {
sessionStorage.clear();
localStorage.clear();
} catch {
// ignore storage errors during sign-out
}
if (lastAuthenticatedMethod) {
try {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
lastAuthenticatedMethod,
);
} catch {
// ignore failures preserving last auth method
}
}

✅ Addressed in 6b9e078

const { redirectToSSOLoginPage } = useSSO();

const signInWithSSO = () => {
// Save to localStorage synchronously before redirect to ensure it persists
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comment

import { useSignInWithMicrosoft } from '@/auth/sign-in-up/hooks/useSignInWithMicrosoft';
import {
SignInUpStep,
signInUpStepState,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix lint

Copy link
Copy Markdown
Member

@charlesBochet charlesBochet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ManikanthMartha Thanks for opening a PR, could you first make sure the CI is red, it looks like you have added a lot of indentation that shouldn't be there

Also we don't add comments unless they are really needeed
Lastly, the design from your screenshot does not seems to be matching the one from the issue in term of colour/border. Please make sure it's pixel perfect by looking at the Figma. Could you also add a screenshot in light mode?

Comment on lines +11 to +16
const setLastAuthenticatedMethod = (method: LastAuthenticatedMethod) => {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
JSON.stringify(method),
);
};

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/twenty-front/src/modules/auth/sign-in-up/hooks/useLastAuthenticatedMethod.ts">

<violation number="1" location="packages/twenty-front/src/modules/auth/sign-in-up/hooks/useLastAuthenticatedMethod.ts:11">
P2: Setter bypasses Recoil state, writing only to localStorage so `lastAuthenticatedMethodState` stays stale until reload</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


export const useLastAuthenticatedMethod = () => {
const lastAuthenticatedMethod = useRecoilValue(lastAuthenticatedMethodState);
const setLastAuthenticatedMethod = (method: LastAuthenticatedMethod) => {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Setter bypasses Recoil state, writing only to localStorage so lastAuthenticatedMethodState stays stale until reload

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/auth/sign-in-up/hooks/useLastAuthenticatedMethod.ts, line 11:

<comment>Setter bypasses Recoil state, writing only to localStorage so `lastAuthenticatedMethodState` stays stale until reload</comment>

<file context>
@@ -0,0 +1,22 @@
+
+export const useLastAuthenticatedMethod = () => {
+  const lastAuthenticatedMethod = useRecoilValue(lastAuthenticatedMethodState);
+  const setLastAuthenticatedMethod = (method: LastAuthenticatedMethod) => {
+    localStorage.setItem(
+      LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
</file context>

✅ Addressed in 7d62fbd

return undefined;
});

goToRecoilSnapshot(initialSnapshot);

This comment was marked as outdated.

@charlesBochet
Copy link
Copy Markdown
Member

Could you please add screen recordings too? :)

@ManikanthMartha
Copy link
Copy Markdown
Contributor Author

yea here is the screen recording.

Untitled.video.-.Made.with.Clipchamp.mp4

import { atom } from 'recoil';
import { localStorageEffect } from '~/utils/recoil/localStorageEffect';

export type LastAuthenticatedMethod = 'google' | 'microsoft' | 'sso' | null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One export max per file.

All caps enum AuthenticatedMethod = {
GOOGLE = 'GOOGLE'
...
}

export const useLastAuthenticatedMethod = () => {
const lastAuthenticatedMethod = useRecoilValue(lastAuthenticatedMethodState);
const setLastAuthenticatedMethod = (method: LastAuthenticatedMethod) => {
localStorage.setItem(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The effect takes care of writing to local storage directly via the atom, you shouldn't access local storage directly

variant={signInUpStep === SignInUpStep.Init ? undefined : 'secondary'}
fullWidth
/>
{isLastUsed && <StyledLastUsedPill label={t`Last`} />}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If only a single method is enabled on that workspace then we probably shouldn't show last used

setSignInUpStep(SignInUpStep.SSOIdentityProviderSelection);
};

const isLastUsed = lastAuthenticatedMethod === 'sso';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never hardcode strings like that, instead reference the enum (e.g. AuthenticationMethod.SSO ...)

const { signInWithMicrosoft } = useSignInWithMicrosoft();

const handleClick = () => {
setLastAuthenticatedMethod('microsoft');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ennum don't hardcode the string (same for other places)

lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);
} catch {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for try/catch like this

extraLight: GRAY_SCALE_DARK.gray7,
inverted: GRAY_SCALE_DARK.gray1,
danger: COLOR_DARK.red,
indigo: COLOR_DARK.blue9,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already use these colors, try maybe theme.color.blue? etc ; I don't think we need to introduce these

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to introduce indigo as blue was not working for fonts

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, we can directly give the font color as color: ${({ theme }) => theme.color.blue}; , no need to introduce indigo

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/twenty-front/src/modules/auth/hooks/useAuth.ts">

<violation number="1" location="packages/twenty-front/src/modules/auth/hooks/useAuth.ts:181">
P1: Sign-out now unguardedly uses localStorage/sessionStorage; DOMException will abort sign-out flow leaving auth state uncleared</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +181 to +193
const lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);

sessionStorage.clear();
localStorage.clear();

if (lastAuthenticatedMethod !== null) {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
lastAuthenticatedMethod,
);
}
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Sign-out now unguardedly uses localStorage/sessionStorage; DOMException will abort sign-out flow leaving auth state uncleared

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/auth/hooks/useAuth.ts, line 181:

<comment>Sign-out now unguardedly uses localStorage/sessionStorage; DOMException will abort sign-out flow leaving auth state uncleared</comment>

<file context>
@@ -177,31 +178,18 @@ export const useAuth = () => {
-        } catch {
-          // Ignore storage errors - last auth method is non-critical
-        }
+        const lastAuthenticatedMethod = localStorage.getItem(
+          LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
+        );
</file context>
Suggested change
const lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);
sessionStorage.clear();
localStorage.clear();
if (lastAuthenticatedMethod !== null) {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
lastAuthenticatedMethod,
);
}
let lastAuthenticatedMethod: string | null = null;
try {
lastAuthenticatedMethod = localStorage.getItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
);
} catch {
lastAuthenticatedMethod = null;
}
try {
sessionStorage.clear();
localStorage.clear();
} catch {
// Ignore storage errors during sign-out
}
if (lastAuthenticatedMethod !== null) {
try {
localStorage.setItem(
LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
lastAuthenticatedMethod,
);
} catch {
// Ignore failures preserving last auth method - it's non-critical
}
}

✅ Addressed in aae2da5

Comment on lines +36 to +38
const handleClick = () => {
setLastAuthenticatedMethod(AuthenticatedMethod.GOOGLE);
signInWithGoogle({ action });

This comment was marked as outdated.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@FelixMalfait FelixMalfait force-pushed the feature/last-used-sso branch from ff6116e to 70d5527 Compare January 13, 2026 21:03
Copy link
Copy Markdown
Member

@FelixMalfait FelixMalfait left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

signInWithGoogle({ action });
};

const isLastUsed = lastAuthenticatedMethod === AuthenticatedMethod.GOOGLE;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent step check for Last Used pill display

Medium Severity

The isLastUsed calculation in SignInUpWithGoogle, SignInUpWithMicrosoft, and SignInUpWithSSO doesn't check signInUpStep === SignInUpStep.Init, while SignInUpWithCredentials does include this check. This causes inconsistent behavior: when a user progresses to the Email or Password step, the Google/Microsoft/SSO buttons will still display the "Last" pill, but the credentials button won't. All authentication method buttons visible in the same view behave differently regarding when they show the indicator.

Additional Locations (2)

Fix in Cursor Fix in Web


const token = readCaptchaToken();
try {
setLastAuthenticatedMethod(AuthenticatedMethod.EMAIL);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last authenticated method set before success confirmation

Medium Severity

The setLastAuthenticatedMethod(AuthenticatedMethod.EMAIL) call in submitCredentials is placed before the authentication calls, inside the try block. If the sign-in fails (e.g., wrong password), the catch block shows an error but doesn't revert the state. This results in the "Last" indicator incorrectly showing email as the last used method even when the authentication attempt failed.

Fix in Cursor Fix in Web

@FelixMalfait FelixMalfait added this pull request to the merge queue Jan 13, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 13, 2026
@charlesBochet charlesBochet merged commit 55cc7f8 into twentyhq:main Jan 13, 2026
64 checks passed
@twenty-eng-sync
Copy link
Copy Markdown

Hey @charlesBochet! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

@github-actions
Copy link
Copy Markdown
Contributor

Thanks @ManikanthMartha for your contribution!
This marks your 3rd PR on the repo. You're top 9% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔑 Remember sign-in methods

4 participants