Security made easy! Pre-provision FIDO2 keys for your users with PowerShell!

Multi-factor authentication (MFA) in Entra ID is crucial for safeguarding sensitive information and maintaining robust identity security. Traditional MFA methods, such as SMS or one-time codes, can be vulnerable to phishing attacks where attackers trick users into revealing their authentication codes using fake websites.

Phishing-resistant MFA, on the other hand, uses more secure methods that are harder for attackers to intercept or manipulate. Among these methods FIDO2 keys (AKA “Security keys“) are one of the best options for this purpose because they leverage public-key cryptography on a dedicated hardware chip. This gives us a strong, password-free, hardware-based layer of security for an account. These keys are resistant to phishing, replay attacks and other common attack-methods associated with passwords, combined with traditional MFA.

Why FIDO2 keys? What about Passkeys?

As just mentioned: FIDO2 keys are a great option because of their strong security, and while they are very similar to passkeys (the technical credential stored is exactly the same), the main difference is that passkeys are stored in various apps/browsers/OSes while FIDO2 are dedicated hardware devices.

It’s important to be aware that the credentials stored on FIDO2 keys are often called Passkeys in various contexts. This terminology can be confusing, as it seems to blur the distinction between the two. However, understanding that both terms refer to the same concept is key to navigating the topic without confusion.

To better distinguish between the two, I employ a simple classification:

  • A “FIDO2 Key” refers to a tangible device that connects via USB or NFC, similar to the one shown in the image below.
  • A “Passkey” denotes a software iteration of the FIDO2 Key, which is integrated within the operating system, web browser, password manager, mobile application etc.

🥳Now we can pre-provision FIDO2 keys in Entra ID!

Oh yes, you read that correctly! 😍 FIDO2 keys just got another, VERY exciting advantage: You can now, using PowerShell, provision new FIDO2 keys on behalf of a user in Entra ID! This eliminates the need to instruct the end-users to do this themselves and makes it a real plug-and-play experience to onboard new users. A password-less workday just came a step closer!

❗Beware: A FIDO2 key can let anyone log in as a user

It is CRITICAL that you and your users understand that a configured FIDO2 key will allow anyone with the key, and its PIN code, to log in with any credential stored on said security key. It’s crucial to inform your IT personnel and users about the importance of diligently safeguarding their security keys. Treat the FIDO2 key and its corresponding PIN with the same level of caution as you would a debit card. Just as you wouldn’t casually leave your debit card and its PIN exposed, apply the same vigilance to protect your FIDO2 key and ensure its confidentiality.

Prerequisites

  • Entra ID P1 licenses
  • FIDO2 enabled in “authentication method” for the user in question
  • AAGUID not blocked
  • Global admin account
  • PowerShell
    • Module: DSInternals.Passkeys
    • Module: Microsoft.Graph.Beta

FIDO2 availability

As mentioned, FIDO2 needs to be enabled for the user, and the AAGUID must not be blocked. Head over to “Protection” and “Authentication Methods” and make sure “Passkey (FIDO2)” is enabled and the user is in scope.

AAGUID

So you may wonder what an “AAGUID” is. Is it short for “Authenticator Attestation Global Unique Identifier” and is a 128-bit identifier that indicates the model of a FIDO key. It lets you verify the vendor and security characteristics of your security keys in case you want to block certain models or vendors.

Click on “Passkey (FIDO2)” and select “Configure“. If you have enforced key restrictions, make sure you don’t block the AAGUID for the FIDO2 key you try to configure.

Setting up the FIDO2 key

Next you need a physical FIDO2 key ready, and the following script. You are free to use and modify this script as you wish as long as you respect the Disclaimer in the script synopsis. Make sure you add the correct values to line 26-28.

<#
.SYNOPSIS
    Register FIDO2 on behalf of another user
.DESCRIPTION
    This script registers a FIDO2 key on behalf of another user. The script requires the admin to have a FIDO2 key and the user's UPN.
    The script will connect to Microsoft Graph and register the FIDO2 key on behalf of the user.
    The script will also register the FIDO2 key in Entra ID.
.NOTES
    Author: Per-Torben Sørensen
    Date: 2024-10-31
    Version: 2410.31
    Requires: Microsoft.Graph.Beta and DSInternals.Passkeys module.
    Tested with: PowerShell 7.4.6
.DISCLAIMER
    THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND. ALL USEAGE IS AT YOUR OWN RISK.
    
    The entire risk arising out of the use or performance of this script remains with you. 
    
    In no event shall the author be held liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, 
    or other pecuniary loss) arising out of the use of or inability to use this script, even if the author has been advised of the possibility of such damages.
    
    The use of this script carries no support from the author, unless otherwise specified. By using this script, you agree to these terms.
#>

param (
    [string]$TenantId = "xyz.onmicrosoft.com", # Your tenant ID
    [string]$DisplayName = "YubiKey PowerShell", # Display name of the FIDO2 key
    [string]$UPN = "user@zyx.domain.com"  # UPN of the user you want to register the FIDO2 key for
)

# Function to ensure a module is installed
function Ensure-Module {
    param (
        [string]$ModuleName
    )
    if (-not (Get-Module -Name $ModuleName -ListAvailable)) {
        Install-Module -Name $ModuleName -Scope CurrentUser -Force -ErrorAction Stop
    }
}

# Function to connect to Microsoft Graph
function Connect-ToMicrosoftGraph {
    param (
        [string]$TenantId
    )
    try {
        Connect-MgGraph -Scopes "UserAuthenticationMethod.ReadWrite.All" -TenantId $TenantId -ErrorAction Stop
    } catch {
        Write-Error "Failed to connect to Microsoft Graph: $_"
        exit 1
    }
}

# Function to register the passkey on the FIDO2 key
function Register-Passkey {
    param (
        [string]$UPN,
        [string]$DisplayName
    )
    try {
        $FIDO2Options = Get-PasskeyRegistrationOptions -UserId $UPN -ErrorAction Stop
        $FIDO2 = New-Passkey -Options $FIDO2Options -DisplayName $DisplayName -ErrorAction Stop
        return $FIDO2
    } catch {
        Write-Error "Failed to register the passkey: $_"
        exit 1
    }
}

# Function to register the FIDO2 key in Entra ID
function Register-FIDO2KeyInEntraID {
    param (
        [string]$UPN,
        [string]$DisplayName,
        [PSCustomObject]$FIDO2
    )
    try {
        $URI = "https://graph.microsoft.com/beta/users/$UPN/authentication/fido2Methods"
        $FIDO2JSON = $FIDO2 | ConvertFrom-Json 
        $AttestationObject = $FIDO2JSON.publicKeyCredential.response.attestationObject
        $ClientDataJson = $FIDO2JSON.publicKeyCredential.response.clientDataJSON
        $Id = $FIDO2JSON.publicKeyCredential.id
        $Body = @{
            displayName = $DisplayName
            publicKeyCredential = @{
                id = $Id
                response = @{
                    clientDataJSON = $ClientDataJson
                    attestationObject = $AttestationObject
                }
            }
        }

        Invoke-MgGraphRequest -Method 'POST' `
            -Body $Body `
            -OutputType 'Json' `
            -ContentType 'application/json' `
            -Uri $URI
    } catch {
        Write-Error "Failed to register the FIDO2 key in Entra ID: $_"
        exit 1
    }
}

# Function to verify the registration
function Verify-Registration {
    param (
        [string]$UPN,
        [string]$DisplayName
    )
    try {
        $RegisteredKey = Get-MgBetaUserAuthenticationFido2Method -UserId $UPN | Where-Object { $_.DisplayName -eq $DisplayName }
        if ($RegisteredKey) {
            Write-Host "Passkey registered successfully for user $UPN."
        } else {
            Write-Error "Failed to verify the registration of the passkey."
        }
    } catch {
        Write-Error "Failed to verify the registration: $_"
        exit 1
    }
}

# Main script execution
Ensure-Module -ModuleName "DSInternals.Passkeys"
Ensure-Module -ModuleName "Microsoft.Graph.Beta"
Connect-ToMicrosoftGraph -TenantId $TenantId
$FIDO2 = Register-Passkey -UPN $UPN -DisplayName $DisplayName
Register-FIDO2KeyInEntraID -UPN $UPN -DisplayName $DisplayName -FIDO2 $FIDO2
Verify-Registration -UPN $UPN -DisplayName $DisplayName

Running the script

Let’s dive into the process of initializing a new FIDO2 key. In our scenario, Grady Archie currently lacks any Multi-Factor Authentication (MFA) methods associated with his account, as shown in the image below. Our next step is to pre-assign a FIDO2 key to his account, ensuring enhanced security and a streamlined authentication experience.

Also checking the FIDO2 key, that it does not have any credentials for Mr. Grady Archie

Now we just need to update the parameters in the script and then run it. When it runs the “Register-Passkey” function (3rd to last line in the script), you will be prompted on where to save the credential. Make sure you select “Security key“.

Depending on your exact model you may or may not have to set or confirm a PIN code and touch it to verify it is physically close to you.

And finally you should have a success message

Checking the content of the FIDO2 key again, I now see a credential for Grady Archie stored on the key, but it is still missing from Entra ID.

Running “Register-FIDO2KeyInEntraID” (second to last line in the script) registers the credential in Entra ID and this is what makes it usable for the account in question.

And the last line in the script will do a simple lookup in Entra ID and see if the credential is registered on the account.

Testing the provisioned key

Now we take the key and try to log in to M365 as Mr. Grady Archie. We go to portal.office.com and provide his username, but select to use security key instead of a password.

We then select the security key and provide its PIN code and local verification by touching it. Again, this is dependent on your exact vendor and model.

🥳 And then we have successfully logged in without a password, ready to work! So easy and yet so secure!

Conclusion

So to summarize, the integration of FIDO2 keys with Entra ID marks a significant leap towards a more secure and user-friendly digital environment. Now, the simplicity of pre-provisioning these keys for both existing and new users not only streamlines the process of setting up a passwordless workday, but it also fortifies the security posture without compromising convenience.

Another great advantage is that this procedure can be be run as soon as your new user accounts are created (or synced) into Entra ID, and this will protect the accounts from being hijacked as MFA is configured on it as soon as it is available. Not only MFA, but we’re talking about phishing-resistant MFA! 😍

So thank you everyone for reading this post, I hope you found it useful. Keep an eye out for upcoming blog entries from us here at agderinthe.cloud, and remember you can subscribe so you don’t miss any new posts from us. Take care everyone.

Author

  • Per-Torben Sørensen

    Per-Torben Sørensen has 25 years experience in IT and Microsoft infrastructure. He is currently an MCT and works as a Technical Architect within M365 at Crayon. His passion is Entra ID and Identity and access management and helps customers become "copilot-ready". He's also a engaged speaker and is always eager to share his knowledge and learn from others.

    View all posts

Discover more from Agder in the cloud

Subscribe to get the latest posts sent to your email.

By Per-Torben Sørensen

Per-Torben Sørensen has 25 years experience in IT and Microsoft infrastructure. He is currently an MCT and works as a Technical Architect within M365 at Crayon. His passion is Entra ID and Identity and access management and helps customers become "copilot-ready". He's also a engaged speaker and is always eager to share his knowledge and learn from others.

Related Post

Leave a Reply