Skip to content

feat: fcli fod session login: Add support for TOTP and multi-factor authentication (MFA) codes in the FoD session login command - #1073

Merged
rsenden merged 9 commits into
dev/v3.xfrom
feat/v3.x/fod-mfa-auth
Aug 20, 2026
Merged

feat: fcli fod session login: Add support for TOTP and multi-factor authentication (MFA) codes in the FoD session login command#1073
rsenden merged 9 commits into
dev/v3.xfrom
feat/v3.x/fod-mfa-auth

Conversation

@SangameshV

Copy link
Copy Markdown
Contributor

Summary:

Adds support for TOTP and Multi-Factor Authentication (MFA) security codes during FoD session authentication. Users can now provide a security code as part of the FoD session login command when their FoD tenant requires additional authentication.

New CLI Options:

  • --code (-c): Optional parameter to provide a TOTP or MFA security code. Can be used interactively or as a command-line argument.
  • --totp: Optional flag indicating that the provided code is a TOTP generated by an authenticator application. If omitted, the code is treated as a standard MFA code (for example, a code received via email).

Tested the following scenarios:

  1. When the FoD tenant requires MFA or TOTP authentication and no security code is supplied, the login command now returns a clear and actionable error message explaining how to provide the required code.
  2. Users can authenticate successfully by providing a valid MFA code received through supported channels.
  3. If the provided security code is invalid, expired, or otherwise rejected by FoD, the login command returns an improved error message with guidance on retrying authentication.

Benefits:

  • Enables authentication against FoD tenants that enforce MFA or TOTP verification.
  • Improves user experience with clearer authentication guidance.
  • Provides actionable error messages for missing, invalid, or expired security codes.
  • Maintains backward compatibility for tenants that do not require additional authentication.

@SangameshV SangameshV self-assigned this Aug 13, 2026
@SangameshV
SangameshV requested a review from rsenden August 19, 2026 10:20
FoDTokenCreateResponse createTokenResponse = FoDOAuthHelper.createToken(urlConfig, loginOptions.getUserCredentials(), loginOptions.getAuthOptions().getScopes());
sessionDescriptor = new FoDSessionDescriptor(urlConfig, createTokenResponse);
} else if (loginOptions.hasUserCredentials()) {
var credBuilder = BasicFoDUserCredentials.builder()

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.

Why did you move this code for building credential objects into this class? The original code was much more compact with better separation of concerns. With the new MFA support, I'd expect a single line like this to create the token:

FoDTokenCreateResponse createTokenResponse = FoDOAuthHelper.createToken(urlConfig, loginOptions.getUserCredentials(), loginOptions.getMfaCode(), loginOptions.getAuthOptions().getScopes());

FoDOAuthHelper should be null-safe for MFA code, i.e., it should handle both full object is null, or non-null object with securityCode being null. For loginOptions::getMfaCode, probably best to return null object if securityCode option value is null.

FoDTokenCreateResponse createTokenResponse = FoDOAuthHelper.createToken(urlConfig, credBuilder.build(),
authCodeBuilder.build(), loginOptions.getAuthOptions().getScopes());
sessionDescriptor = new FoDSessionDescriptor(urlConfig, createTokenResponse);
} catch (UnexpectedHttpResponseException e) {

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.

  • Current exception handling block is too long, taking away attention from actual logic above. Closely related, it doesn't make sense to construct three local strings with static contents; better to move these to actual constants, which would automatically make exception handling code much shorter
  • Why do we have dedicated exception handling when authenticating with user credentials, but not when authenticating with client credentials? Shouldn't we throw a similar FcliSimpleException with appropriate guidance (like client credentials incorrect or expired)?

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 have had class-level constants created for the messages and have used them.

Note: Across the entire codebase, every other command throws FcliSimpleException with the message string inline at the throw site, either as a string literal directly or via String.format(...). No other command pre-defines multi-line message constants like MFA_GUIDANCE, ERROR_WITH_CODE, and ERROR_WITHOUT_CODE.

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.

Added dedicated exception handling for client credentials as well.

/**
* Basic immutable FoD user credentials with builder pattern.
*/
public static final class BasicFoDUserCredentials implements IFoDUserCredentials {

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.

I think (but please double-check) that BasicFoDUserCredentials and the new BasicFoDUserAuthCode are currently only referenced within FoDSessionLoginOptions, so from that perspective, it makes more sense to keep these as inner classes instead of separate top-level types. However, the HTTP MCP server for example defines its own HttpMcpFoDUserCredentials class with similar implementation as BasicFoDUserCredentials. As such, it may makes sense to keep the new top-level BasicFoD* classes, and change the MCP HTTP server (and any other code that creates IFoDUserCredentials) to utilize these top-level BasicFoD* classes instead of providing their own implementations of IFoDUserCredentials.

Can you please check:

  • Which classes currently implement IFoDUserCredentials and similar interfaces (like SSC variant)?
  • Whether these can be easily replaced with a single, top-level Basic* implementation

To summarize:

  • If existing implementations of IFoDUserCredentials can be easily merged, keep the top-level Basic* classes and refactor all code to use this single implementation
  • If there are significant differences between implementations and Basic* classes are only instantiated in FoDSessionLoginOptions, move those Basic* classes back to FoDSessionLoginOptions as inner classes

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.

The Basic* classes are kept as top-level classes. please review the code now.

public static final FoDTokenCreateResponse createToken(IUrlConfig urlConfig, IFoDUserCredentials uc, String... scopes) {
Map<String,Object> formData = generateTokenRequest(uc, scopes);
Map<String,Object> formData = generateTokenRequest(uc, IFoDUserAuthCode.NONE, scopes);
try ( var unirest = UnirestHelper.createUnirestInstance() ) {

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.

  • Indentation seems wrong (looks like this was already wrong in original code, but please fix)

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.

Done

@rsenden
rsenden merged commit cf882bb into dev/v3.x Aug 20, 2026
41 of 42 checks passed
@rsenden
rsenden deleted the feat/v3.x/fod-mfa-auth branch August 20, 2026 16:24
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.

2 participants