identity access management

Implementing Passwordless Authentication with FIDO2

Deploy FIDO2/WebAuthn passwordless authentication using security keys and platform authenticators. Covers WebAuthn API integration, FIDO2 server configuration, passkey enrollment, biometric authentication, and migration from password-based systems aligned with NIST SP 800-63B AAL3.

access-controlauthenticationfido2iamidentitypasswordlesswebauthn
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Deploy FIDO2/WebAuthn passwordless authentication using security keys and platform authenticators. Covers WebAuthn API integration, FIDO2 server configuration, passkey enrollment, biometric authentication, and migration from password-based systems aligned with NIST SP 800-63B AAL3.

When to Use

  • When deploying or configuring implementing passwordless authentication with fido2 capabilities in your environment
  • When establishing security controls aligned to compliance requirements
  • When building or improving security architecture for this domain
  • When conducting security assessments that require this implementation

Prerequisites

  • Familiarity with identity access management concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Objectives

  • Implement comprehensive implementing passwordless authentication with fido2 capability
  • Establish automated discovery and monitoring processes
  • Integrate with enterprise IAM and security tools
  • Generate compliance-ready documentation and reports
  • Align with NIST 800-53 access control requirements

Security Controls

Control NIST 800-53 Description
Account Management AC-2 Lifecycle management
Access Enforcement AC-3 Policy-based access control
Least Privilege AC-6 Minimum necessary permissions
Audit Logging AU-3 Authentication and access events
Identification IA-2 User and service identification

Verification

  • Implementation tested in non-production environment
  • Security policies configured and enforced
  • Audit logging enabled and forwarding to SIEM
  • Documentation and runbooks complete
  • Compliance evidence generated
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md3.0 KB

API Reference: Implementing Passwordless Authentication with FIDO2

WebAuthn Registration Flow

// 1. Server generates challenge
const options = await navigator.credentials.create({
  publicKey: {
    challenge: new Uint8Array(32),
    rp: { name: "Example Corp", id: "example.com" },
    user: { id: userId, name: "user@example.com", displayName: "User" },
    pubKeyCredParams: [
      { type: "public-key", alg: -7 },   // ES256
      { type: "public-key", alg: -257 }, // RS256
    ],
    authenticatorSelection: {
      authenticatorAttachment: "platform",  // or "cross-platform"
      residentKey: "required",              // for passkeys
      userVerification: "required",
    },
    attestation: "direct",
  }
});

WebAuthn Authentication Flow

const assertion = await navigator.credentials.get({
  publicKey: {
    challenge: serverChallenge,
    rpId: "example.com",
    allowCredentials: [],  // empty for discoverable credentials (passkeys)
    userVerification: "required",
  }
});

python-fido2 Server Library

from fido2.server import Fido2Server
from fido2.webauthn import PublicKeyCredentialRpEntity
 
rp = PublicKeyCredentialRpEntity(id="example.com", name="Example")
server = Fido2Server(rp)
 
# Registration
registration_data, state = server.register_begin(user, credentials)
auth_data = server.register_complete(state, response)
 
# Authentication
request_data, state = server.authenticate_begin(credentials)
server.authenticate_complete(state, credentials, credential_id, client_data, auth_data, signature)

FIDO2 Authenticator Types

Type Example Attachment Passkey Support
Platform Windows Hello, Touch ID platform Yes
Roaming YubiKey, Titan Key cross-platform Yes (FIDO2)
Software 1Password, iCloud Keychain platform Yes

COSE Algorithm Identifiers

COSE ID Algorithm Use
-7 ES256 (P-256) Preferred for FIDO2
-257 RS256 Legacy compatibility
-8 EdDSA (Ed25519) Strong, compact
-35 ES384 (P-384) Higher security

NIST SP 800-63B AAL Levels

Level Requirements FIDO2 Mapping
AAL1 Single factor Not applicable
AAL2 Two factors FIDO2 + PIN/biometric
AAL3 Hardware crypto + verifier impersonation resistance FIDO2 hardware key

Azure AD FIDO2 Configuration

# Enable FIDO2 in Azure AD
Set-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration `
  -AuthenticationMethodConfigurationId "fido2" `
  -State "enabled" `
  -AdditionalProperties @{
    isSelfServiceRegistrationAllowed = $true
    isAttestationEnforced = $true
  }

References

standards.md0.9 KB

Standards - FIDO2 Passwordless Authentication

FIDO Standards

NIST Standards

  • NIST SP 800-63B: AAL3 - Hardware-based phishing-resistant authenticator
  • NIST SP 800-53 Rev 5: IA-2(6), IA-2(8) Replay-resistant authentication
  • NIST SP 800-157: PIV Derived Credentials

CISA Guidance

  • Phishing-Resistant MFA: Required for federal agencies under EO 14028
  • OMB M-22-09: Federal zero trust strategy requiring phishing-resistant MFA

Vendor Resources

workflows.md1.5 KB

FIDO2 Passwordless Authentication Workflows

Workflow 1: Security Key Enrollment

  1. User receives FIDO2 security key (YubiKey, Titan Key)
  2. User navigates to enrollment portal
  3. System generates WebAuthn registration challenge
  4. Browser prompts user to insert/tap security key
  5. User verifies with PIN or biometric on key
  6. Key generates unique public/private key pair
  7. Public key registered with relying party
  8. User tests authentication with enrolled key

Workflow 2: Passkey Authentication Flow

  1. User visits login page, enters username
  2. Server sends WebAuthn authentication challenge
  3. Browser prompts for authenticator (key, biometric, passkey)
  4. User verifies identity (touch key, scan fingerprint, enter PIN)
  5. Authenticator signs challenge with private key
  6. Server validates signature with stored public key
  7. User authenticated, session created

Workflow 3: Migration from Passwords to Passwordless

  1. Phase 1: Deploy FIDO2 to pilot group (IT, security teams)
  2. Phase 2: Enable coexistence (password + FIDO2)
  3. Phase 3: Expand FIDO2 enrollment to all users
  4. Phase 4: Set FIDO2-only policy per group
  5. Phase 5: Disable password authentication for migrated groups
  6. Phase 6: Monitor for fallback authentication attempts

Workflow 4: Lost/Stolen Key Recovery

  1. User reports lost security key
  2. Admin disables lost key in identity provider
  3. User authenticates via backup method (recovery codes, backup key)
  4. User enrolls replacement security key
  5. Old key permanently revoked
  6. Security team reviews for unauthorized usage of lost key

Scripts 1

agent.py3.6 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""FIDO2 Passwordless Auth Agent - audits FIDO2 deployment readiness and credential status."""

import json
import argparse
import logging
import subprocess
from datetime import datetime

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)


def graph_api(token, endpoint):
    cmd = ["curl", "-s", "-H", f"Authorization: Bearer {token}",
           f"https://graph.microsoft.com/v1.0{endpoint}"]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
    return json.loads(result.stdout) if result.stdout else {}


def get_fido2_policy(token):
    return graph_api(token, "/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/fido2")


def get_registrations(token):
    return graph_api(token, "/reports/authenticationMethods/userRegistrationDetails")


def audit_fido2_policy(policy):
    findings = []
    if policy.get("state") != "enabled":
        findings.append({"issue": f"FIDO2 policy state: {policy.get('state', 'unknown')}", "severity": "high"})
    if not policy.get("keyRestrictions", {}).get("aaGuids"):
        findings.append({"issue": "No AAGUID restrictions set", "severity": "medium"})
    if not policy.get("isAttestationEnforced"):
        findings.append({"issue": "Attestation not enforced", "severity": "medium"})
    return findings


def analyze_adoption(registrations):
    users = registrations.get("value", [])
    total = len(users)
    fido2 = sum(1 for u in users if "fido2" in str(u.get("methodsRegistered", [])).lower())
    passwordless = sum(1 for u in users if u.get("isPasswordlessCapable", False))
    return {
        "total_users": total, "fido2_registered": fido2, "passwordless_capable": passwordless,
        "fido2_adoption_rate": round(fido2 / max(total, 1) * 100, 1),
    }


def check_rp_config(rp_url):
    cmd = ["curl", "-s", f"{rp_url}/.well-known/webauthn"]
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
    findings = []
    try:
        config = json.loads(result.stdout)
    except json.JSONDecodeError:
        config = {}
        findings.append({"issue": "WebAuthn well-known not configured", "severity": "high"})
    return {"config": config, "findings": findings}


def generate_report(policy, policy_findings, adoption, rp):
    return {
        "timestamp": datetime.utcnow().isoformat(),
        "fido2_policy_state": policy.get("state", "unknown"),
        "policy_findings": policy_findings, "adoption_metrics": adoption,
        "rp_config": rp,
        "total_findings": len(policy_findings) + len(rp.get("findings", [])),
    }


def main():
    parser = argparse.ArgumentParser(description="FIDO2 Passwordless Authentication Audit Agent")
    parser.add_argument("--token", required=True, help="Graph API bearer token")
    parser.add_argument("--rp-url", help="WebAuthn relying party URL")
    parser.add_argument("--output", default="fido2_audit_report.json")
    args = parser.parse_args()
    policy = get_fido2_policy(args.token)
    policy_findings = audit_fido2_policy(policy)
    registrations = get_registrations(args.token)
    adoption = analyze_adoption(registrations)
    rp = check_rp_config(args.rp_url) if args.rp_url else {"config": {}, "findings": []}
    report = generate_report(policy, policy_findings, adoption, rp)
    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    logger.info("FIDO2: adoption %.1f%%, %d findings", adoption["fido2_adoption_rate"], report["total_findings"])
    print(json.dumps(report, indent=2, default=str))

if __name__ == "__main__":
    main()
Keep exploring