WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Conversation

@sheensantoscapadngan
Copy link
Member

@sheensantoscapadngan sheensantoscapadngan commented Nov 18, 2025

Description 📣

This PR implements WebAuthn (passkey) support and adds MFA session verification for PAM account access. The implementation adds WebAuthn as a third MFA method alongside email and TOTP, with proper challenge-based verification and replay protection. A new MFA session flow allows users to verify their identity before accessing sensitive PAM accounts.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Tests 🛠️

# Here's some code block to paste some code snippets

@maidul98
Copy link
Collaborator

maidul98 commented Nov 18, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@gitguardian
Copy link

gitguardian bot commented Nov 20, 2025

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
22397427 Triggered Generic Private Key 382bd1a backend/bdd/pebble/localhost/key.pem View secret
22397428 Triggered Generic Private Key 382bd1a backend/bdd/pebble/pebble.minica.key.pem View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@sheensantoscapadngan sheensantoscapadngan changed the title Feat/webauth and session mfa feat: webauthn and PAM session mfa Nov 20, 2025
@sheensantoscapadngan sheensantoscapadngan marked this pull request as ready for review November 21, 2025 14:21
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 21, 2025

Greptile Overview

Greptile Summary

This PR implements WebAuthn (passkey) support and adds MFA session verification for PAM account access. The implementation adds WebAuthn as a third MFA method alongside email and TOTP, with proper challenge-based verification and replay protection. A new MFA session flow allows users to verify their identity before accessing sensitive PAM accounts.

Key changes:

  • Added WebAuthn credential storage with proper public key cryptography and counter-based replay protection
  • Implemented MFA session service with Redis-backed temporary sessions (5-minute TTL)
  • Added requireMfa field to PAM accounts to optionally enforce MFA before granting access
  • Created dedicated MFA session verification page for out-of-band MFA flows
  • WebAuthn challenges stored in Redis with 5-minute expiration

Critical security issues found:

  • MFA bypass vulnerability in PAM account access logic - accounts with requireMfa=false allow access without any MFA verification
  • Migration defaults requireMfa to false, leaving all existing PAM accounts unprotected until manually enabled
  • MFA method selection creates email MFA sessions even when users have no MFA configured

Confidence Score: 2/5

  • This PR has critical security vulnerabilities in the MFA enforcement logic that could allow unauthorized PAM account access
  • The MFA bypass vulnerability in pam-account-service.ts allows attackers to access PAM accounts without MFA verification when requireMfa=false. Since the migration defaults this to false for all existing accounts, this creates immediate security exposure. The WebAuthn implementation itself is solid, but the integration with PAM access control has fundamental flaws in the security model.
  • Pay critical attention to backend/src/ee/services/pam-account/pam-account-service.ts (MFA bypass logic) and backend/src/db/migrations/20251119124200_add-require-mfa-to-pam-account.ts (insecure defaults)

Important Files Changed

File Analysis

Filename Score Overview
backend/src/services/webauthn/webauthn-service.ts 4/5 WebAuthn service implementation with proper challenge validation, credential storage, and counter-based replay protection
backend/src/services/mfa-session/mfa-session-service.ts 4/5 MFA session service with proper user verification and session ownership checks
backend/src/ee/services/pam-account/pam-account-service.ts 3/5 PAM account service with MFA session support - contains potential session bypass and unclear MFA enforcement logic
backend/src/db/migrations/20251118190904_add-webauthn-support.ts 5/5 WebAuthn credentials table migration with proper foreign keys and unique constraints
backend/src/db/migrations/20251119124200_add-require-mfa-to-pam-account.ts 5/5 Added requireMfa boolean column to PAM accounts, defaults to false
backend/src/server/routes/v2/mfa-session-router.ts 5/5 MFA session routes with proper authentication and rate limiting

Copy link
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.

62 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +567 to +575
// Determine which MFA method to use
// Priority: org-enforced > user-selected > email as fallback
const orgMfaMethod = org.enforceMfa
? ((org.selectedMfaMethod as MfaMethod | null) ?? MfaMethod.EMAIL)
: undefined;
const userMfaMethod = actorUser.isMfaEnabled
? ((actorUser.selectedMfaMethod as MfaMethod | null) ?? MfaMethod.EMAIL)
: undefined;
const mfaMethod = (orgMfaMethod ?? userMfaMethod ?? MfaMethod.EMAIL) as MfaMethod;
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty sure this logic is flawed. The line:

((org.selectedMfaMethod as MfaMethod | null) ?? MfaMethod.EMAIL)

will make it so that orgMfaMethod becomes MfaMethod.EMAIL even if org.selectedMfaMethod is null rather than falling back to userMfaMethod. Should probably be ((org.selectedMfaMethod as MfaMethod | null) ?? undefined) or something

// Verify the session is for the same account
if (mfaSession.resourceId !== accountId) {
throw new BadRequestError({
message: "MFA session is for a different resource"
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe the error should say its for a different account?

handler: async (req) => {
return server.services.webAuthn.verifyRegistrationResponse({
userId: req.permission.id,
registrationResponse: req.body.registrationResponse as unknown as RegistrationResponseJSON,
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty sure you don't need this type assertion if you make clientExtensionResults have a default like:

clientExtensionResults: z.record(z.unknown()).default({})

handler: async (req) => {
return server.services.webAuthn.verifyAuthenticationResponse({
userId: req.permission.id,
authenticationResponse: req.body.authenticationResponse as unknown as AuthenticationResponseJSON
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above

Copy link
Contributor

Choose a reason for hiding this comment

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

This file should not have been edited. rotationCredentialsConfigured should still be passed, otherwise the tooltip functionality is useless. The idea is to grey it out if it's not configured

<GenericAccountFields />
<SqlAccountFields isUpdate={isUpdate} />
<RotateAccountFields rotationCredentialsConfigured={rotationCredentialsConfigured} />
{rotationCredentialsConfigured && <RotateAccountFields />}
Copy link
Contributor

Choose a reason for hiding this comment

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

rotationCredentialsConfigured should stay as a parameter. We just want to grey out the option rather than fully hide it.

message: "MFA session not found or expired"
});
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe adding a check like if (mfaMethod !== mfaSession.mfaMethod) ... may be beneficial to prevent possible exploit attempts?

Comment on lines +4 to +6
export const verifyCredentialOwnership = (userId: string, credentialUserId: string): boolean => {
return userId === credentialUserId;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

This function feels a little unnecessary... maybe we just use an inline === check?

transports: cred.transports as AuthenticatorTransportFuture[]
})),
authenticatorSelection: {
authenticatorAttachment: "platform",
Copy link
Contributor

Choose a reason for hiding this comment

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

Is using "platform" here the best choice? From my tiny bit of research I think this only works for platform-based methods like windows hello, touch id, etc. And not stuff like YubiKeys.

Comment on lines +7 to +26
export enum MfaSessionStatus {
PENDING = "PENDING",
ACTIVE = "ACTIVE"
}

export type TMfaSessionStatusResponse = {
status: MfaSessionStatus;
mfaMethod: MfaMethod;
};

export type TVerifyMfaSessionRequest = {
mfaSessionId: string;
mfaToken: string;
mfaMethod: MfaMethod;
};

export type TVerifyMfaSessionResponse = {
success: boolean;
message: string;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it'd be better to put the enum and types into dedicated file, and not queries.tsx? Maybe into types or helper file

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.

4 participants