Asi implementing-passwordless-auth-with-microsoft-entra
install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/asi/skills/implementing-passwordless-auth-with-microsoft-entra" ~/.claude/skills/plurigrid-asi-implementing-passwordless-auth-with-microsoft-entra && rm -rf "$T"
manifest:
plugins/asi/skills/implementing-passwordless-auth-with-microsoft-entra/SKILL.mdsource content
Implementing Passwordless Auth with Microsoft Entra
When to Use
- Organization wants to eliminate password-based attacks (phishing, credential stuffing, brute force)
- Regulatory or internal mandate requires phishing-resistant MFA (Executive Order 14028, CISA guidance)
- Deploying FIDO2 security keys or Windows Hello for Business across the enterprise
- Migrating from legacy MFA (SMS, phone call) to phishing-resistant authentication methods
- Implementing passkey support for hybrid or cloud-joined Windows devices
- Reducing helpdesk costs from password reset requests
Do not use for environments that cannot support modern authentication protocols; legacy applications using NTLM or basic authentication must be migrated first.
Prerequisites
- Microsoft Entra ID P1 or P2 license (Azure AD Premium)
- Windows 10/11 22H2+ for Windows Hello for Business deployment
- FIDO2-compliant security keys (YubiKey 5 Series, Feitian BioPass, Google Titan)
- Microsoft Authenticator app 6.8+ for passkey support on iOS 16+/Android 14+
- Hybrid Azure AD join or Azure AD join configured for Windows devices
- Conditional Access policies configured for authentication strength
Workflow
Step 1: Configure Authentication Methods Policy
Enable passwordless authentication methods in Microsoft Entra:
# Connect to Microsoft Graph Connect-MgGraph -Scopes "Policy.ReadWrite.AuthenticationMethod", "User.ReadWrite.All" # Enable FIDO2 Security Key authentication method $fido2Policy = @{ "@odata.type" = "#microsoft.graph.fido2AuthenticationMethodConfiguration" state = "enabled" isAttestationEnforced = $true isSelfServiceRegistrationAllowed = $true keyRestrictions = @{ isEnforced = $true enforcementType = "allow" aaGuids = @( "cb69481e-8ff7-4039-93ec-0a2729a154a8", # YubiKey 5 Series "ee882879-721c-4913-9775-3dfcce97072a", # YubiKey 5 NFC "fa2b99dc-9e39-4257-8f92-4a30d23c4118", # YubiKey 5C NFC "2fc0579f-8113-47ea-b116-bb5a8db9202a", # YubiKey Bio "73bb0cd4-e502-49b8-9c6f-b59445bf720b" # Google Titan ) } includeTargets = @( @{ targetType = "group" id = "all_users" # Or specific security group ID } ) } Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration ` -AuthenticationMethodConfigurationId "fido2" ` -BodyParameter $fido2Policy # Enable Microsoft Authenticator with passkey support $authenticatorPolicy = @{ "@odata.type" = "#microsoft.graph.microsoftAuthenticatorAuthenticationMethodConfiguration" state = "enabled" featureSettings = @{ displayAppInformationRequiredState = @{ state = "enabled" includeTarget = @{ targetType = "group" id = "all_users" } } displayLocationInformationRequiredState = @{ state = "enabled" includeTarget = @{ targetType = "group" id = "all_users" } } companionAppAllowedState = @{ state = "enabled" } } includeTargets = @( @{ targetType = "group" id = "all_users" authenticationMode = "any" } ) } Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration ` -AuthenticationMethodConfigurationId "microsoftAuthenticator" ` -BodyParameter $authenticatorPolicy # Enable Windows Hello for Business $whfbPolicy = @{ "@odata.type" = "#microsoft.graph.windowsHelloForBusinessAuthenticationMethodConfiguration" state = "enabled" pinMinimumLength = 6 pinMaximumLength = 127 pinLowercaseCharactersUsage = "allowed" pinUppercaseCharactersUsage = "allowed" pinSpecialCharactersUsage = "allowed" securityKeyForSignIn = "enabled" includeTargets = @( @{ targetType = "group" id = "all_users" } ) } Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration ` -AuthenticationMethodConfigurationId "windowsHelloForBusiness" ` -BodyParameter $whfbPolicy Write-Host "Passwordless authentication methods enabled successfully"
Step 2: Configure Authentication Strength Conditional Access
Create Conditional Access policies requiring phishing-resistant authentication:
# Create custom authentication strength for phishing-resistant MFA $authStrength = @{ displayName = "Phishing-Resistant Passwordless" description = "Requires FIDO2, WHfB, or certificate-based authentication" allowedCombinations = @( "fido2", "windowsHelloForBusiness", "x509CertificateMultiFactor" ) requirementsSatisfied = "mfa" } $strengthPolicy = New-MgPolicyAuthenticationStrengthPolicy -BodyParameter $authStrength # Create Conditional Access policy requiring phishing-resistant auth $caPolicy = @{ displayName = "Require Phishing-Resistant Auth for All Apps" state = "enabledForReportingButNotEnforced" # Start in report-only conditions = @{ users = @{ includeUsers = @("All") excludeGroups = @("Passwordless-Exclusion-Group") } applications = @{ includeApplications = @("All") } clientAppTypes = @("browser", "mobileAppsAndDesktopClients") } grantControls = @{ operator = "OR" authenticationStrength = @{ id = $strengthPolicy.Id } } } New-MgIdentityConditionalAccessPolicy -BodyParameter $caPolicy # Create stricter policy for admin portals $adminPolicy = @{ displayName = "Require Security Key for Admin Access" state = "enabled" conditions = @{ users = @{ includeRoles = @( "62e90394-69f5-4237-9190-012177145e10", # Global Admin "194ae4cb-b126-40b2-bd5b-6091b380977d", # Security Admin "f28a1f50-f6e7-4571-818b-6a12f2af6b6c", # SharePoint Admin "29232cdf-9323-42fd-ade2-1d097af3e4de" # Exchange Admin ) } applications = @{ includeApplications = @( "797f4846-ba00-4fd7-ba43-dac1f8f63013", # Azure Portal "00000006-0000-0ff1-ce00-000000000000", # Microsoft 365 Admin "0000000a-0000-0000-c000-000000000000" # Entra Admin Center ) } } grantControls = @{ operator = "OR" authenticationStrength = @{ id = $strengthPolicy.Id } } sessionControls = @{ signInFrequency = @{ value = 4 type = "hours" isEnabled = $true } } } New-MgIdentityConditionalAccessPolicy -BodyParameter $adminPolicy
Step 3: Deploy Windows Hello for Business via Intune
Configure WHfB deployment through Microsoft Intune MDM:
# Create Windows Hello for Business configuration profile in Intune $whfbProfile = @{ "@odata.type" = "#microsoft.graph.windowsIdentityProtectionConfiguration" displayName = "WHfB - Enterprise Deployment" description = "Windows Hello for Business configuration for all managed devices" useSecurityKeyForSignin = $true windowsHelloForBusinessBlocked = $false pinMinimumLength = 6 pinMaximumLength = 127 pinUppercaseCharactersUsage = "allowed" pinLowercaseCharactersUsage = "allowed" pinSpecialCharactersUsage = "allowed" enhancedAntiSpoofingForFacialFeaturesEnabled = $true pinRecoveryEnabled = $true securityDeviceRequired = $true # Require TPM unlockWithBiometricsEnabled = $true useCertificatesForOnPremisesAuthEnabled = $true # For hybrid scenarios # Cloud Kerberos Trust for hybrid join (recommended over key trust) windowsHelloForBusinessAuthenticationMethod = "cloudKerberosTrust" } # Create the configuration profile $profile = New-MgDeviceManagementDeviceConfiguration -BodyParameter $whfbProfile # Assign to all Windows devices $assignment = @{ target = @{ "@odata.type" = "#microsoft.graph.allDevicesAssignmentTarget" } } New-MgDeviceManagementDeviceConfigurationAssignment ` -DeviceConfigurationId $profile.Id ` -BodyParameter $assignment # Configure Cloud Kerberos Trust (for hybrid Azure AD joined devices) # This eliminates the need for PKI infrastructure # Requires Azure AD Kerberos module Import-Module AzureADHybridAuthenticationManagement # Create Azure AD Kerberos Server object in on-premises AD $domain = "corp.local" $cloudCredential = Get-Credential -Message "Enter Azure AD Global Admin credentials" $domainCredential = Get-Credential -Message "Enter on-premises Domain Admin credentials" Set-AzureADKerberosServer ` -Domain $domain ` -CloudCredential $cloudCredential ` -DomainCredential $domainCredential # Verify Kerberos Server object Get-AzureADKerberosServer -Domain $domain -CloudCredential $cloudCredential ` -DomainCredential $domainCredential Write-Host "Cloud Kerberos Trust configured for hybrid WHfB deployment"
Step 4: Register FIDO2 Security Keys for Users
Implement security key registration workflow:
# Bulk FIDO2 security key registration via Temporary Access Pass # Step 1: Issue Temporary Access Pass for key registration function Issue-TemporaryAccessPass { param( [string]$UserId, [int]$LifetimeMinutes = 60, [bool]$IsUsableOnce = $true ) $tap = @{ "@odata.type" = "#microsoft.graph.temporaryAccessPassAuthenticationMethod" lifetimeInMinutes = $LifetimeMinutes isUsableOnce = $IsUsableOnce } $result = New-MgUserAuthenticationTemporaryAccessPassMethod ` -UserId $UserId ` -BodyParameter $tap return @{ UserId = $UserId TemporaryAccessPass = $result.TemporaryAccessPass ExpiresAt = $result.CreatedDateTime.AddMinutes($LifetimeMinutes) } } # Bulk issue TAPs for security key registration event $registrationUsers = Import-Csv "security_key_registration_list.csv" $tapResults = foreach ($user in $registrationUsers) { $tap = Issue-TemporaryAccessPass -UserId $user.UserPrincipalName [PSCustomObject]@{ User = $user.UserPrincipalName TAP = $tap.TemporaryAccessPass Expires = $tap.ExpiresAt KeySerial = $user.AssignedKeySerial } } # Export TAPs for secure distribution to registration team $tapResults | Export-Csv "tap_assignments.csv" -NoTypeInformation # Monitor FIDO2 registration progress function Get-Fido2RegistrationStatus { $allUsers = Get-MgUser -All -Property "id,userPrincipalName,department" $registrationStatus = foreach ($user in $allUsers) { $methods = Get-MgUserAuthenticationFido2Method -UserId $user.Id [PSCustomObject]@{ UserPrincipalName = $user.UserPrincipalName Department = $user.Department Fido2KeyCount = $methods.Count KeyModels = ($methods.Model -join ", ") RegistrationDates = ($methods.CreatedDateTime -join ", ") HasBackupKey = $methods.Count -ge 2 } } return $registrationStatus } $status = Get-Fido2RegistrationStatus $total = $status.Count $registered = ($status | Where-Object { $_.Fido2KeyCount -gt 0 }).Count $withBackup = ($status | Where-Object { $_.HasBackupKey }).Count Write-Host "FIDO2 Registration Progress" Write-Host " Total Users: $total" Write-Host " Registered: $registered ($([math]::Round($registered/$total*100,1))%)" Write-Host " With Backup: $withBackup ($([math]::Round($withBackup/$total*100,1))%)"
Step 5: Disable Legacy Authentication Methods
Phase out phishable authentication factors:
# Disable SMS and voice call authentication $smsPolicy = @{ "@odata.type" = "#microsoft.graph.smsAuthenticationMethodConfiguration" state = "disabled" } Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration ` -AuthenticationMethodConfigurationId "sms" ` -BodyParameter $smsPolicy $voicePolicy = @{ "@odata.type" = "#microsoft.graph.voiceAuthenticationMethodConfiguration" state = "disabled" } Update-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration ` -AuthenticationMethodConfigurationId "voice" ` -BodyParameter $voicePolicy # Block legacy authentication protocols via Conditional Access $blockLegacyPolicy = @{ displayName = "Block Legacy Authentication" state = "enabled" conditions = @{ users = @{ includeUsers = @("All") } applications = @{ includeApplications = @("All") } clientAppTypes = @( "exchangeActiveSync", "other" ) } grantControls = @{ operator = "OR" builtInControls = @("block") } } New-MgIdentityConditionalAccessPolicy -BodyParameter $blockLegacyPolicy # Audit users still using legacy authentication $legacyAuthReport = Get-MgAuditLogSignIn -Filter "clientAppUsed ne 'Browser' and clientAppUsed ne 'Mobile Apps and Desktop clients'" ` -Top 1000 | Group-Object userPrincipalName | Select-Object Count, Name | Sort-Object Count -Descending Write-Host "Users with Legacy Auth (last 30 days):" $legacyAuthReport | Format-Table -AutoSize
Step 6: Monitor Passwordless Adoption Metrics
Track deployment progress and authentication method usage:
# Generate passwordless adoption dashboard data function Get-PasswordlessAdoptionMetrics { # Authentication method registration statistics $registrationReport = Get-MgReportAuthenticationMethodUserRegistrationDetail -All $metrics = @{ TotalUsers = $registrationReport.Count PasswordlessCapable = ($registrationReport | Where-Object { $_.IsPasswordlessCapable }).Count MfaRegistered = ($registrationReport | Where-Object { $_.IsMfaRegistered }).Count Fido2Registered = ($registrationReport | Where-Object { "fido2" -in $_.MethodsRegistered }).Count WhfbRegistered = ($registrationReport | Where-Object { "windowsHelloForBusiness" -in $_.MethodsRegistered }).Count AuthenticatorRegistered = ($registrationReport | Where-Object { "microsoftAuthenticator" -in $_.MethodsRegistered }).Count SmsOnly = ($registrationReport | Where-Object { "sms" -in $_.MethodsRegistered -and "fido2" -notin $_.MethodsRegistered -and "windowsHelloForBusiness" -notin $_.MethodsRegistered }).Count } # Authentication method usage from sign-in logs $signInLogs = Get-MgAuditLogSignIn -Top 10000 -Filter "createdDateTime ge $((Get-Date).AddDays(-30).ToString('yyyy-MM-ddTHH:mm:ssZ'))" $authMethodUsage = $signInLogs | Group-Object { $_.AuthenticationMethodsUsed -join "," } | Select-Object Count, Name | Sort-Object Count -Descending return @{ Registration = $metrics Usage = $authMethodUsage } } $adoption = Get-PasswordlessAdoptionMetrics $reg = $adoption.Registration Write-Host "PASSWORDLESS ADOPTION REPORT" Write-Host "============================" Write-Host "Total Users: $($reg.TotalUsers)" Write-Host "Passwordless Capable: $($reg.PasswordlessCapable) ($([math]::Round($reg.PasswordlessCapable/$reg.TotalUsers*100,1))%)" Write-Host " FIDO2 Keys: $($reg.Fido2Registered)" Write-Host " Windows Hello: $($reg.WhfbRegistered)" Write-Host " Authenticator: $($reg.AuthenticatorRegistered)" Write-Host "MFA Registered: $($reg.MfaRegistered) ($([math]::Round($reg.MfaRegistered/$reg.TotalUsers*100,1))%)" Write-Host "SMS Only (needs upgrade): $($reg.SmsOnly)"
Key Concepts
| Term | Definition |
|---|---|
| FIDO2 | Fast Identity Online 2 standard enabling passwordless authentication using public-key cryptography bound to hardware authenticators or platform credentials |
| Passkey | FIDO2 credential that can be device-bound (security key) or synced across devices, providing phishing-resistant authentication without passwords |
| Windows Hello for Business | Windows platform authenticator using PIN, fingerprint, or facial recognition backed by TPM-protected asymmetric keys for passwordless sign-in |
| Cloud Kerberos Trust | Deployment model for hybrid WHfB that uses Azure AD Kerberos to authenticate to on-premises resources without requiring PKI certificate infrastructure |
| Temporary Access Pass | Time-limited passcode issued by admins enabling users to register passwordless methods or recover access when their primary method is unavailable |
| Authentication Strength | Conditional Access capability in Microsoft Entra that specifies which authentication method combinations satisfy MFA requirements for a given policy |
Tools & Systems
- Microsoft Entra Admin Center: Portal for configuring authentication methods, Conditional Access policies, and monitoring sign-in analytics
- Microsoft Intune: MDM/MAM platform for deploying Windows Hello for Business configuration profiles to managed devices
- Microsoft Graph API: Programmatic interface for managing authentication methods, policies, and generating adoption reports
- FIDO2 Security Keys: Hardware authenticators (YubiKey, Feitian, Google Titan) storing cryptographic credentials for phishing-resistant authentication
Common Scenarios
Scenario: Enterprise-Wide Passwordless Migration
Context: Organization with 5,000 users plans to eliminate passwords within 12 months after experiencing a phishing attack that compromised 47 accounts. Current state: 60% use SMS MFA, 30% use Authenticator app, 10% have no MFA.
Approach:
- Phase 1 (Month 1-2): Enable FIDO2 and WHfB authentication methods in report-only Conditional Access
- Phase 2 (Month 2-3): Deploy WHfB to all managed Windows devices via Intune with Cloud Kerberos Trust
- Phase 3 (Month 3-5): Distribute FIDO2 security keys to executives, IT admins, and finance (highest-risk users first)
- Phase 4 (Month 5-8): Enable Authenticator passkeys for mobile-primary users and field workers
- Phase 5 (Month 8-10): Switch Conditional Access from report-only to enforced for phishing-resistant auth
- Phase 6 (Month 10-12): Disable SMS and voice call methods, block legacy authentication protocols
- Ongoing: Monitor adoption metrics, issue TAPs for stragglers, maintain break-glass accounts
Pitfalls:
- Not deploying Cloud Kerberos Trust causes WHfB to fail for on-premises resource access in hybrid environments
- Enforcing passwordless without ensuring all applications support modern authentication breaks access
- Issuing only one security key per user without a backup creates lockout risk if the key is lost
- Not configuring Temporary Access Pass as a recovery method before disabling password-based sign-in
Output Format
PASSWORDLESS AUTHENTICATION DEPLOYMENT REPORT ================================================ Tenant: corp.onmicrosoft.com Users: 5,247 Deployment Phase: Phase 4 (Authenticator Passkeys) AUTHENTICATION METHOD REGISTRATION Passwordless Capable: 4,103 / 5,247 (78.2%) FIDO2 Security Keys: 892 (17.0%) Windows Hello: 2,847 (54.3%) Authenticator Passkey: 1,234 (23.5%) Certificate-Based: 312 (5.9%) LEGACY METHOD STATUS SMS-Only Users: 387 (7.4%) -- migration in progress Voice-Only Users: 0 (disabled) No MFA Users: 42 (0.8%) -- TAPs issued CONDITIONAL ACCESS Phishing-Resistant Policy: ENFORCED (all users except exclusion group) Legacy Auth Block: ENABLED Admin Portal Policy: SECURITY KEY REQUIRED SIGN-IN ANALYTICS (Last 30 Days) Total Sign-Ins: 847,293 Passwordless: 623,891 (73.6%) Password + MFA: 198,402 (23.4%) Password Only: 0 (blocked) Legacy Protocol: 0 (blocked) SECURITY IMPACT Phishing Incidents: 0 (down from 47 pre-deployment) Password Reset Tickets: -82% reduction Avg Sign-In Time: 8.2s (passwordless) vs 24.1s (password)