Skip to content

[Browser MFA] Add Browser MFA to Connect#64887

Merged
danielashare merged 1 commit into
masterfrom
danielashare/browser-mfa-connect
Mar 28, 2026
Merged

[Browser MFA] Add Browser MFA to Connect#64887
danielashare merged 1 commit into
masterfrom
danielashare/browser-mfa-connect

Conversation

@danielashare
Copy link
Copy Markdown
Contributor

This PR adds Browser MFA support to Connect and renames more SSOMFA* specific types/funcs to be more generic now they're used by Browser and SSO MFA. The RFD for this feature can be found here. Tracking issue #63987.

Manual Test Plan

Test Environment

Running Teleport locally from a PoC branch (danielashare/tsh-browser-mfa-sso) because this branch doesn't have the web UI changes. SSO tested with keycloak running locally.

Test Cases

  • User can login using Browser MFA
  • User can per-session MFA using Browser MFA
  • User can still SSO login
  • User can still per-session MFA with SSO MFA
  • User can still WebAuthn login
  • User can still per-session MFA with webauthn
  • User can still login with TOTP
  • User can still passwordless login

@danielashare danielashare self-assigned this Mar 20, 2026
@danielashare danielashare added no-changelog Indicates that a PR does not require a changelog entry backport/branch/v18 labels Mar 20, 2026
@danielashare danielashare force-pushed the danielashare/browser-mfa-connect branch 2 times, most recently from a862d28 to c7db1e2 Compare March 20, 2026 12:11
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

// Fire Webauthn goroutine.
if promptWebauthn {
wg.Add(1)
go func() {
defer wg.Done()
resp, err := p.promptWebauthn(ctx, chal)
respC <- libmfa.MFAGoroutineResponse{Resp: resp, Err: trace.Wrap(err, "Webauthn authentication failed")}

P1 Badge Do not start native WebAuthn alongside Browser MFA

When Connect receives both WebauthnChallenge and BrowserMFAChallenge for the same user—which is the normal browser-passkey case in lib/auth/auth.go—this code still launches the native WebAuthn goroutine immediately. That means users who choose Browser MFA because their credential only exists in the browser (for example Touch ID/password-manager passkeys) can still lose the whole ceremony to a native WebAuthn failure: HandleMFAPromptGoroutines treats wancli.ErrUsingNonRegisteredDevice as terminal in lib/client/mfa/prompt.go:114-116, so the browser flow never gets a chance to succeed. In practice, the new Browser MFA option stays broken for exactly the users it is meant to unblock.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@danielashare danielashare force-pushed the danielashare/browser-mfa-login-finish branch from 7dabf4b to c9a119c Compare March 23, 2026 16:50
Comment thread lib/teleterm/daemon/mfaprompt.go
@ravicious ravicious self-requested a review March 23, 2026 17:13
@danielashare danielashare requested a review from gzdunek March 23, 2026 21:45
@zmb3
Copy link
Copy Markdown
Collaborator

zmb3 commented Mar 24, 2026

@danielashare can you ping me after this one is rebased and ready for re-review?

@danielashare danielashare force-pushed the danielashare/browser-mfa-connect branch from c7db1e2 to 170cf79 Compare March 25, 2026 07:54
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 170cf79940

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/client/sso/ceremony.go Outdated
@danielashare
Copy link
Copy Markdown
Contributor Author

@zmb3 rebased and ready for review, thanks

@danielashare danielashare requested a review from zmb3 March 25, 2026 08:23
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 523b2234b5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
go func() {
defer wg.Done()

resp, err := p.promptMfa(ctx, chal)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pass Browser-only challenge to Browser MFA ceremony

This Browser MFA branch forwards the full chal into promptMfa, but MFACeremony.Run gives priority to SSOChallenge when both are present. In environments where a challenge includes both SSO and Browser MFA options, selecting Browser MFA in Connect will still execute the SSO path (expecting an SSO token) and can fail instead of returning a browser WebAuthn response.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We need to have p.promptSSO and p.promptBrowser separate, like the CLI prompt

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.

Good catch

Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
Comment on lines +151 to +153
if promptBrowserMfa {
wg.Add(1)
go func() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid deadlock when adding Browser MFA goroutine

Introducing this additional goroutine can increase concurrent MFA responders to four (app prompt, WebAuthn, SSO, Browser), but HandleMFAPromptGoroutines only has a 2-slot response buffer and returns after the first success before wg.Wait(). In mixed-method challenges (notably when WebAuthn, SSO, and Browser are all available), one sender can block forever on respC <- ..., causing the wait to hang and the login flow to stall.

Useful? React with 👍 / 👎.

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 comments that Codex left seem valid, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, this looks like a pre-existing issue. Opened a separate PR to address it. If you want to just bump the buffer up to 4 for the sake of this PR, that seems sufficient.

Comment thread lib/teleterm/apiserver/handler/handler_clusters.go Outdated
Comment thread web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.tsx Outdated
Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
Comment on lines +151 to +153
if promptBrowserMfa {
wg.Add(1)
go func() {
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 comments that Codex left seem valid, no?

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.

Is there any way we can test it? Grzegorz added e2e tests for headless auth, perhaps something similar could be utilized here? Seeing how browser MFA utilizes both Connect and the Web UI too.

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.

An e2e test could be a follow-up to this PR (so that it's not blocked on this), but it'd definitely be great to have it.

Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
go func() {
defer wg.Done()

resp, err := p.promptMfa(ctx, chal)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We need to have p.promptSSO and p.promptBrowser separate, like the CLI prompt

Comment thread e
Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
Comment on lines +151 to +153
if promptBrowserMfa {
wg.Add(1)
go func() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, this looks like a pre-existing issue. Opened a separate PR to address it. If you want to just bump the buffer up to 4 for the sake of this PR, that seems sufficient.

@danielashare danielashare force-pushed the danielashare/browser-mfa-login-finish branch from f9fa433 to bdcba07 Compare March 27, 2026 12:28
@danielashare danielashare force-pushed the danielashare/browser-mfa-connect branch from 523b223 to 5e60f4e Compare March 27, 2026 15:25
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd1a673600

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e16dcca98

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
Comment on lines +150 to +153
// Fire Browser MFA goroutine.
if promptBrowserMfa {
wg.Add(1)
go func() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid running SSO and Browser MFA ceremonies concurrently

When a challenge includes both SSOChallenge and BrowserMFAChallenge, this path now starts both callback-based ceremonies in parallel against the same c.cfg.MFACeremony instance. Both Run calls ultimately wait on the same redirector callback channel, so whichever goroutine reads the single callback first may treat it as the wrong response type and fail, while the intended goroutine then waits until timeout. In that mixed-method scenario, users can see nondeterministic login stalls/failures despite completing MFA successfully in the browser.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8a3aa84da

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread web/packages/teleterm/src/ui/ModalsHost/modals/ReAuthenticate/ReAuthenticate.tsx Outdated
Copy link
Copy Markdown
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

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

LGTM once Codex feedback is evaluated.

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.

An e2e test could be a follow-up to this PR (so that it's not blocked on this), but it'd definitely be great to have it.

Comment thread lib/teleterm/daemon/mfaprompt.go Outdated
// Fire Browser MFA goroutine.
if promptBrowserMfa {
wg.Add(1)
go func() {
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 edge case of starting these in parallel is that if I actually have a hardware key plugged in but for whatever reason I want to use browser MFA (like now when I'm testing the implementation 😏), then Connect makes my YubiKey blink. So when I select browser MFA and I want to auth in browser with my YubiKey, I cannot use it there. I have to cancel the dialog in the browser and then try again for it to effectively "steal" YubiKey from Connect.

The current implementation should work fine for its intended purpose, that is when the user has no MFA available outside of the browser. I'm just noting it down in case we need a more elaborate implementation in the future, e.g. selecting the browser MFA option shuts down the goroutine that prompts hardware keys.

Comment thread lib/client/mfa/prompt.go
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.

This is outside of scope of this PR, but while testing I've noticed that neither tsh nor Connect react to hardware key insertion. Connect even says "Insert and press your key". But if there was no key at the time when the prompt was shown, inserting a key is going to have no effect. I'll create an issue for this on Monday.

@danielashare danielashare force-pushed the danielashare/browser-mfa-login-finish branch from 5ff3627 to a12fb73 Compare March 27, 2026 22:28
Base automatically changed from danielashare/browser-mfa-login-finish to master March 28, 2026 11:58
@danielashare danielashare force-pushed the danielashare/browser-mfa-connect branch from 689863c to 5b19ccc Compare March 28, 2026 12:06
@danielashare danielashare added this pull request to the merge queue Mar 28, 2026
Merged via the queue into master with commit 96b1b1b Mar 28, 2026
45 checks passed
@danielashare danielashare deleted the danielashare/browser-mfa-connect branch March 28, 2026 20:04
@backport-bot-workflows
Copy link
Copy Markdown
Contributor

@danielashare See the table below for backport results.

Branch Result
branch/v18 Failed

danielashare added a commit that referenced this pull request May 6, 2026
[Browser MFA] Add protobuf and config (#63831)

[Browser MFA] Add proto for Browser MFA feature (#64048)

[Browser MFA] Add CompleteBrowserMFAChallenge gRPC (#63873)

[Browser MFA] Rename browser mfa config name (#64980)

[Browser MFA] Add BrowserMFARequestID to CreateAuthenticateChallenge (#63945)

[Browser MFA] Add Browser MFA to challenge request flow (#63936)

[Browser MFA] Add initial requests for browser MFA process to client tools (#64301)

[Browser MFA] Add tsh callback handling for webauthn response (#64461)

[Browser MFA] Add Browser MFA to presence checks (#65052)

[Browser MFA] Add browser MFA path to MFA finish flow (#64523)

[Browser MFA] Add Browser MFA to Connect (#64887)

[Browser MFA] Add Browser MFA UI (#64692)

[Browser MFA] Fix formatting in moderated sessions (#65236)

[Browser MFA] Add Browser MFA ceremony tests
ivan-bax pushed a commit to ivan-bax/teleport that referenced this pull request May 22, 2026
[Browser MFA] Add protobuf and config (gravitational#63831)

[Browser MFA] Add proto for Browser MFA feature (gravitational#64048)

[Browser MFA] Add CompleteBrowserMFAChallenge gRPC (gravitational#63873)

[Browser MFA] Rename browser mfa config name (gravitational#64980)

[Browser MFA] Add BrowserMFARequestID to CreateAuthenticateChallenge (gravitational#63945)

[Browser MFA] Add Browser MFA to challenge request flow (gravitational#63936)

[Browser MFA] Add initial requests for browser MFA process to client tools (gravitational#64301)

[Browser MFA] Add tsh callback handling for webauthn response (gravitational#64461)

[Browser MFA] Add Browser MFA to presence checks (gravitational#65052)

[Browser MFA] Add browser MFA path to MFA finish flow (gravitational#64523)

[Browser MFA] Add Browser MFA to Connect (gravitational#64887)

[Browser MFA] Add Browser MFA UI (gravitational#64692)

[Browser MFA] Fix formatting in moderated sessions (gravitational#65236)

[Browser MFA] Add Browser MFA ceremony tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/branch/v18 desktop-access no-changelog Indicates that a PR does not require a changelog entry size/md ui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants