Skip to content

Conversation

@dsupru
Copy link

@dsupru dsupru commented Dec 1, 2025

Hi again !
Following up on #1137
This PR implements the suggestion to return “insufficient credentials” when require_2fa is set and the current auth mode is password-only (1FA).

I tested it with my setup and it worked as expected.
I added a currentAuthMode state variable to the authenticationModel struct. Since it already has fields like currentBrokerId and currentSessionId, this seemed like a reasonable place for it, but please let me know if you have any reservations. I think this is cleaner than adding an env variable, which feels brittle and likely to break in the future. Here i'm assuming "password" auth mode is 1FA (defined here). That’s not perfect, but it should be easier to evolve later without breaking changes.

I also considered passing require_2fa down to the brokers and returning authNext if the auth mode is password, but that seemed a bit more invasive, and we’d still need logic somewhere to error out with insufficient creds. Happy to hear which design you prefer.

the pam config I used for testing:

auth    [success=ok user_unknown=ignore default=bad] pam_succeed_if.so user != root quiet_success
auth    [success=1 cred_insufficient=ignore ignore=ignore default=die authinfo_unavail=ignore] \
        pam_authd.so debug=true require_2fa=true
 
auth    required pam_u2f.so cue
 
auth    requisite pam_nologin.so
auth    optional  pam_gnome_keyring.so
image ^ when i log in with a password and the config above

I'll keep #1137 open in case you prefer that approach, but please let me know if we can move forward with either of these. Super interested in getting this out, let me know what else I can do to get this to a mergeable state 🙂
I suppose before (or after?) merging it you'd like to update the docs, please point where I can suggest that edit

@dsupru dsupru requested a review from a team as a code owner December 1, 2025 03:17
@dsupru dsupru force-pushed the feature/optional-2fa branch from f19baed to 0cf8b71 Compare December 1, 2025 03:33
@dsupru dsupru changed the title Add support for optional 2FA argument Add support for optional require_2fa argument Dec 1, 2025
@dsupru
Copy link
Author

dsupru commented Dec 2, 2025

Gentle nudge @adombeck , @3v1n0

@adombeck
Copy link
Contributor

adombeck commented Dec 2, 2025

I'll let @3v1n0 handle this because he's the expert for PAM

@dsupru
Copy link
Author

dsupru commented Dec 16, 2025

@3v1n0 another ping. Please let me know what your thoughts on this change are. I think it adds quite a bit of value for all business Ubuntu customers

@3v1n0 3v1n0 force-pushed the feature/optional-2fa branch 2 times, most recently from 1806209 to f145e6d Compare December 19, 2025 08:34
@3v1n0
Copy link
Collaborator

3v1n0 commented Dec 19, 2025

@dsupru hey, I've worked a bit on this, and I pushed here how I was thinking to handle this.

Give it a try, however my feeling is that even by doing this or exposing the authentication modes as PAM values, we're ending up with an hacky solution and potentially not future proof.

The fact is:

  • At PAM level we don't know exactly what are the authentication methods that have been used: their names are just IDs that are provided by the broker and that can change at any given time
  • We may only be sure that we authenticated using a web-authentication, or using multiple input fields, or a new password has been set.

The proposed solution here is "good enough", but if some broker will support a multi-factor authentication system based on typing some PIN that you got through SMS (or any other device), then we won't be able to know that an MFA method has been used.

Reason why, I think that the proper solution to this would be leaving this driven by the broker (as everything else in authd, since it's the only one that really knows what's going on), and so introducing another error message that the broker can return and thus make authd to react to that.

dependabot bot and others added 4 commits December 19, 2025 09:42
Bumps the minor-updates group in /tools with 1 update: [github.com/golangci/golangci-lint/v2](https://github.com/golangci/golangci-lint).


Updates `github.com/golangci/golangci-lint/v2` from 2.6.2 to 2.7.2
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/main/CHANGELOG.md)
- [Commits](golangci/golangci-lint@v2.6.2...v2.7.2)

---
updated-dependencies:
- dependency-name: github.com/golangci/golangci-lint/v2
  dependency-version: 2.7.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the minor-updates group with 1 update: google.golang.org/protobuf.


Updates `google.golang.org/protobuf` from 1.36.10 to 1.36.11

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Downstream PAM modules can branch off cred_insufficient to require a
second auth factor.

Add an option to require multi-factor authentication

Co-Authored-By: Marco Trevisan <mail@3v1n0.net>
@3v1n0 3v1n0 force-pushed the feature/optional-2fa branch from f145e6d to 05d4031 Compare December 19, 2025 08:42
switch msg.access {
case auth.Granted:
m.maybeUpdateVerifiedAuthLayouts(m.currentLayout)
if m.requireMFA && len(m.verifiedAuthLayouts) < 2 &&
Copy link
Author

Choose a reason for hiding this comment

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

Doesn't quite work for the "device authentication" auth flow: a user is presented with a qr code layout, and then the new password layout. That adds up to 1 factor (layouts.QrCode), so a user will be locked out if they ever loose their 2nd factor device for example.

@dsupru
Copy link
Author

dsupru commented Dec 29, 2025

Thank you so much for working on this @3v1n0 !

The main concern with the proposed change is that the “device authentication” method (QR-code layout) is now counted as a single factor.
I agree that this is arguably the more correct modeling, since whether it’s effectively single or multi-factor depends on how the IdP is configured, and we shouldn’t assume it implies 2FA.

That said, treating device authentication as a single factor has a downside: users may not have a viable self-service recovery path if they lose their 2nd factor. This might be workable in smaller orgs, but at larger companies it’s common to require a recovery flow users can complete on their own. Even for my org, I can see cases like “someone loses a YubiKey while remote and can’t log in to their device.”

Also, I can approximate the same behavior today by always requiring a second factor (e.g., an inserted YubiKey) via PAM configuration.

If we can’t treat device authentication as 2-factor within authd, it may indeed make sense to implement this at the daemon level like you suggested. I took a quick look at OIDC: the amr claim may indicate which authentication methods were used (assuming the provider supplies it). If "mfa" is present among the Authentication Method Reference values, that seems like the cleanest way to distinguish MFA-backed logins.

I’m planning to spend some time around New Year’s to put together a PR, but if you have bandwidth, I’d appreciate another set of eyes on it.

Thank you and happy new year!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants