Register FIDO2 Passkey in Entra ID on behalf of users with PowerShell
Hi All,
Recently i was working together with one of my fellows (shout out to Raul Ruta) to figure out how Register Passkeys with the new Microsoft Graph Beta API’s.
The only thing we found was the Article of Jan Bakker, that uses the Yubico Sample scripts based on Phyton.
I was looking if there is a better way that does not require Phyton and found this PowerShell Module
- PowerShell Gallery DSInternals.Passkeys
I am using the new PSResourceGet here instead of the PowerShellGet commands.
Install the PowerShell Module and list the Commands
Install-PSResource -Name DSInternals.Passkeys -Scope CurrentUser
List the commands from the Module
Get-Command -Module DSInternals.Passkeys
For the new Graph Beta API’s around FIDO2/Passkeys you need to have the following Permission
- UserAuthenticationMethod.ReadWrite.All
Let’s connect with Microsoft.Graph
Connect-MgGraph -Scopes UserAuthenticationMethod.ReadWrite.All -TenantId icewolfch.onmicrosoft.com -NoWelcome
Query Passkeys
Let’s have a look at the SecurityInfo of my Account. As you can see i have registered two FIDO2 Security Keys
Let’s figure out if there is any PowerShell Command that could help us here
get-command *beta*fido*
Query the Security Keys of my user
Get-MgBetaUserAuthenticationFido2Method -UserId a.bohren@icewolf.ch | fl
You can also use Microsoft Graph Explorer GET https://graph.microsoft.com/beta/users/a.bohren@icewolf.ch/authentication/fido2Methods
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#users('a.bohren%40icewolfch.onmicrosoft.com')/authentication/fido2Methods",
"@microsoft.graph.tips": "Use $select to choose only the properties your app needs, as this can lead to performance improvements. For example: GET users('<key>')/authentication/fido2Methods?$select=aaGuid,attestationCertificates",
"value": [
{
"id": "Oy0dkr_kI6lp0loPvgEWqA2",
"displayName": "Yubikey",
"createdDateTime": "2020-03-10T21:57:14Z",
"aaGuid": "2fc0579f-8113-47ea-b116-bb5a8db9202a",
"model": "YubiKey 5 Series with NFC",
"attestationCertificates": [
"dbe793efdf194fe2df25d93653a1e8a3268a9075"
],
"attestationLevel": "attested"
},
{
"id": "SwUHoZSJLI5d8zGDR_NcIO26L6pNmzITTDSQ5R9znNl1s-MC8_D1iOpMSwz5geCB0",
"displayName": "Yubikey Bio",
"createdDateTime": "2021-10-15T06:56:45Z",
"aaGuid": "d8522d9f-575b-4866-88a9-ba99fa02f35b",
"model": "YubiKey Bio Series",
"attestationCertificates": [
"cd988ed081f44b4cbce2462b99efdebf3d54f467"
],
"attestationLevel": "attested"
}
]
}
FIDO2 Key
I have two FIDO2 Keys
It depends on the Firmware Version how many Passkeys you can store on a Security Key.
Sadly you can’t update the Firmware
Yubico periodically updates its firmware to take advantage of features and capabilities introduced into the ecosystem. YubiKeys are programmed in Yubico’s facilities with the latest available firmware. Once programmed, YubiKeys cannot be updated to another version. The firmware cannot be altered or removed from a YubiKey.
On my YubiKey 5 NFC i have Firmware that allows only 25 Passkeys.
From Version 5.7 you can store up to 100 Passkeys.
Client To Authenticator Protocol (CTAP)
Client To Authenticator Protocol (CTAP)
FIDO2 consists of two standardized components, a web API (WebAuthn) and a protocol for clients to communicate with authenticators: the Client To Authenticator Protocol (CTAP). The client can be a platform (an Operating System such as Microsoft Windows), a browser (such as Google Chrome), or an application (such as an SSH client).
CTAP clients can use different transports to communicate with an authenticator. When the authenticator is a FIDO security key, USB or NFC is typically used.
The difference between WebAuthn and CTAP is illustrated in the figure below.
Details about the CTAP Protocoll can be found here
Register Passkey
Here is the Flow of the Registration Process
Initial situation
The Account m.muster@icewolf.ch does not have a registered Security Key
Let’s check with PowerShell
Get-MgBetaUserAuthenticationFido2Method -UserId m.muster@icewolf.ch | fl
Let’s check with Graph Explorer
GET https://graph.microsoft.com/beta/users/m.muster@icewolf.ch/authentication/fido2Methods
{
"@odata.context": "https://graph.microsoft.com/beta/$metadata#users('m.muster%40icewolf.ch')/authentication/fido2Methods",
"@microsoft.graph.tips": "Use $select to choose only the properties your app needs, as this can lead to performance improvements. For example: GET users('<key>')/authentication/fido2Methods?$select=aaGuid,attestationCertificates",
"value": []
}
On the Yubikey there is no Passkey for m.muster
Get FIDO2 Creation Options
Let’s check with Graph Explorer
This will return a Challenge with a Timeout and some other Parameters
There is a command in the Microsoft.Graph.Beta Module, but i was not able to make it work
Invoke-MgBetaCreationUserAuthenticationFido2MethodOption -UserId m.muster@icewolf.ch -ChallengeTimeoutInMinutes 5
The Module DSInternals.Passkeys has a similar Function that get’s the Challenge from Entra ID
$RegistrationOptions = Get-PasskeyRegistrationOptions -UserId m.muster@icewolf.ch
$RegistrationOptions
$RegistrationOptions.PublicKeyOptions
Now the Passkey needs to be stored on the Yubico Security Key (New-Passkey)
$UPN = "m.muster@icewolf.ch"
$Passkey = Get-PasskeyRegistrationOptions -UserId $UPN | New-Passkey -DisplayName 'YubiKey 5 NFC'
At this point, the Passkey is saved on the Yubikey.
Let’s have a look at the CTAP Response from the Passkey
$JSON = $Passkey | ConvertFrom-Json
$Attestationobject = $JSON.publicKeyCredential.response.attestationObject
$ClientDataJson = $JSON.publicKeyCredential.response.clientDataJSON
$id = $JSON.publicKeyCredential.id
$id
$Attestationobject
$ClientDataJson
Register Passkey in EntraID
Now we have all the Information and can Register the Passkey in Entra ID
$UPN = "m.muster@icewolf.ch"
$Passkey = Get-PasskeyRegistrationOptions -UserId $UPN | New-Passkey -DisplayName 'YubiKey 5 NFC'
$JSON = $Passkey | ConvertFrom-Json
$Attestationobject = $JSON.publicKeyCredential.response.attestationObject
$ClientDataJson = $JSON.publicKeyCredential.response.clientDataJSON
$id = $JSON.publicKeyCredential.id
#Prepare the Body
$Body = @"
{
"displayName": "YubiKey 5 NFC",
"publicKeyCredential": {
"id": "$ID",
"response": {
"clientDataJSON": "$clientDataJSON",
"attestationObject": "$Attestationobject"
}
}
}
"@
#Register in EntraID
$URI = "https://graph.microsoft.com/beta/users/$UPN/authentication/fido2Methods"
[string]$response = Invoke-MgGraphRequest -Method "POST" -Uri $URI -OutputType "Json" -ContentType 'application/json' -Body $Body
As you can see the Security Key is registered in Entra ID
This is the Body Object i’ve used to Register the Passkey in Entra ID
{
"displayName": "YubiKey 5 NFC",
"publicKeyCredential": {
"id": "RXR6RPQ-IF9OuRumbHzIBQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWlhsS2FHSkhZMmxQYVVwVFZUQkZkRlF3UmtaVlF6QjVUbFJaYVV4RFNteGliVTFwVDJsS1FrMXFWVEpTTUU1T1NXbDNhV1ZFVm1wSmFuQmlTV3N4U2xOVlVtaFNSVTVFVVZkNFJGb3dSak5UVlVwQ1dqQnNVbUo2YkhCaU1WWndWVmhhZGs0d2NGRlBWbWhDVVd4Q1VGSnNRVEZoYTBaUFVXMWtjbU5YYUhKaFZXTTFaSHBDUTFGV1JucFNhMFpGVVdwU1RsZEdiRE5TVmtaYVV6Qk9ZVk5YTVhCWGJFSTFWRVZrVWxGclpGTlhWVkpwWWxaWmQxUlZTbFpTTUU1dVlsWk9TMkl5TVZWUFIydzBZVEJHVTJFeFpFTk5NbEozV1cweFUyUnRVWHBVV0dSSlZWWnNSVlpzUmxKU1JWWTBWMnMxVm1WVVJsRlpNakZyWVVkS2RHSkVXbHBYUmtwM1dXcEpNR1JHUmxoVWJYQmhWMFUxTmxSVlRucFNNRVY0VmxWV1JHVkZNWEpVTUZKTFlURnNkRkp0Y0ZwV1JrWXdWRlJLVms1Rk1WUk5SRUpQWWxVMWIxUkdVbk5oYXpVMlZGaFNUbEpIYzNoVVZXUk9aVVp3V0ZKdGNGcFdSM042VkZWSk1GZEZVbFZTVkVKT1VrWkdORlF3VWtKTlJURlZVVlJHVG1KSE9WbFNSbEpLVFVVeFJWVllhRTlXUlVWM1ZGWlNWazFWTVhOaU0yUk5aV3RXTUZSVlRucFNNRVY0VmxWV1FtVkZNWEpYVnpGTFlVVTFjVmRVVms1V01WWXdWRzF3Y2sxck9WUk5SRUpRVmtkNGNsUkdaRXRoTURsSVVsaFNUbUZzUlhsWGJGSkNaREF4ZEZadGNFNWxiV04zVkZWc1NsRnJiSEZSVlRWRFdqSjBlR0ZIZEhCU2Vtd3pUVVZLUWxWVlZrZFJWVVpRVVRCR1VrOUZSazVUVld4RFVUSmtURkV3UmxKU1ZVWnlVVlJrV21WRlNrZFBWMXB3VjBWV2FXTjZVa1ZhTTAweVRsVjBXV0ZUZEhCamJFVXhUMFZzTVZKcmF6SldiVFI1VldzMGVHTXpaSFJrTUdScFRqSndXazF1VFhoalZrMTNUa1YwYmxVeVRtcFdTRlpEVmtkb1dWVXdhRVpPUkZKcFl6SkdRbUl3VWxOUFJGVjVVMVpPZEUxc2JGaGpWa0l5VVZad1VtRlRkRmhUYkVwVFlXeHNXVTF0YXpGU1dGWlVXbTFTUkdSSFRYWlpNVmswVkd4U1YyTnVhRmhOTVZwUldWWlNjbUV6Um05V1IxWmFVV3hHVkZKSFdqUmpiR2Q0WkVaR00wOVZXVEpSYlVaSVUzcHJlV0l3UlhaV1UzUlBUbXhHTW1SVk5YTmxSbVJoVldwT1NWVXlPVzlaTTJ3MVdtdFdTazFGV2xoaWVtZ3hWa1ZzYVdScWJIcFViV3N6WVZWR1QwOUZiRU5UTWxveFZuazRORkp0Y0U1WmJtaGFXakZhV0ZKRlozZGFTRlpOVWpCS01rNXJjelZoUmxsMldXMU5NbGx1UWpOV01scEtaREphUjJGRlpGVk9NV3hOWVVaYU1rNXJWakprYmtFMFltcEdXbFZJVVRWVFIyZ3pZbFJuTTJGWVVscFNSekZIVGxaR1ZrNHlTakZhVjBwaFZGaEthVlV5YkU5TlIyUlVWa2hDU0ZJeVdreE5SR3g0VlVWNFRXVkZPRFZWYlVaMlltNUJlR014UWs5UlYzaDJUa2hvZDFsdFNrNWpWMXB1V1RKR1RrMTZSbEZUUmtaS1VrVkdVbEZWU25abGJVNHpWR3hTUWxSVlNtNVViRnBKVldzeFExRlhXVFJTVlVaeFVWVkdUbEZ0WkVoUlZFWldXa1Z3VWxKVlNYWmtNVVpRVkZWR00xSXdUbkJqTUdSQ1ZWWkdRMW95Y0dwV1ZVWnVVbGhrUkdReGJFVldiRWwzVlVWS1FsVlZVa0phTURBd1ZGVkZkMUl3VGxSalZXUlVVMWRKZWxKR1JrWlJhMDR6VmxWR1FrNUZiRU5SVmtaRFRYcGpjbGxXY0dsV01VSkdVbXhLTm1GWVFsQlNWVTB5Wkd0c2RWVlhhM2xTYm1neVdURkdiMWR1YUZOTldFWmhVbXR3U0ZOdFJsZFNNMnd4VTFkc2VHVkZOWEpqYm13elUwUlJlVlF3WkhkbFIzUkZUVE5LZEVzemNETmFSVlkxWlZVNVVGWXdhekpYU0VKelZsVTFOV0ZHVGpaV00wWlhTekJTTTJSSWNHbGtXRUpWVkRKYVExTkVSbWhMTW5SU1ZraG5NV013WXpKYWJYQldZVEJKZVdGdGJFbFpWRUl4V1Zadk1GbDZTWGROVjBwelVtdHNkV1Z1VG5sVk1rcG9XbGRzZW1Gc2NETk9NMXB4VDBkU2ExSkdjRzVqYXpCNFRtNUdXV0ZyVGtKT2VsWnVXVEpTUkdOV2NFNVhWVGswWTIwNVlWcHVRa1ZVUlRsNVRtNXNjMWt3VmtKV2JWSkdVbFY0UlZveFJrVlVNMWw0WVZWT1EyVkVhekZWTW1ScldYcE9VVTU1T1doWFdIQTJWRlp2ZDFaV1FYSmpNRVpIWTBadmQwOUdTa1JaV0ZaSVZWaEthRTVzVGpSTmJWSlJWMGRuTTFaV1VUSk9iR2hZVlZaS05WSlVWa1ZXVjJSdVpXNUtjRlJJUlRKaVJHaE9WRmRvVW1JeFp6QmtNa3B1VjFST1RGSlhXbnBUV0hCTVZETmtkRXN3WjNoV2FUaDZWMVJrZEZKNlNrOVZVM1JRVjFad2Jrd3hRazVQVmtwSlZVVlNTbE5XUmxWVmVtaDRUakI0ZWsxSE9VWlhWV05wV0ZOM2FXVkVWakJKYW05cFRUQk5NRkV3U1RWT1ZWVjRUV3BKTVU1VlVrVlJWR3Q2VG1wTk1rMXFRVFJPYTBwRlRWUk5OVTE2V1RKUmFrNUdUMVJhUWxGVFNqa3VaVXBQVmpCMFJUSjNWR2t6TnpCUk9USm1hSGxNVWtWS1pWcEdZM2x5V2t4cmJpMUxZWGRZTWt0TlMwWXhZbXByWVhSbGJXMVVNM2xIVlRJek5VOHhSREUyZGtKQmFHVlpWbEV4Y20xTGRISm9Ra294UWxGQmRuaFBTMVJsVFhaUFltVnFZMHhTWVc5cVpWWktPV0l4TjFOM2NreE5UalJxZVZSTlptbHRUMkZNY0d0RU5FcG9kelYyYldneE9WbDFiM3BvU1ZjNU5ESkNiRXBtWkU5VU5tdHNjekJ5VldwQlRESnZOMUY2UzNWSWFGazFTa05OWkV0SVRERlhNVTVITkhSc1pFVTJhbXhTTm5jelFqWTNSemhqTUhoSGRrTlhNVlIyVWxaT1gxbGFOV1JuYWt4dlVtMHhVbGMzU0d0UE1GWkJkRU5PYVdaak9VcFNZMnRNUzFNMk9EZFFaa1JsWW1KbU1URjRlbE54VkZKQlgyZFJlSGhETVU1S1dtb3lRa1JxVUVNNFRHRmpkRmRKTjNGb00yTlFPREZTVmxsSmNWaEVNWGh3VURCWlFsSnFWVmxMVm1WV1JVbEtPVTFCV1dacE9WUm5MbUUxUTE5cWRYcGlkMmxzT1hGa2FGRnhOV3RGZGxFdU9VRlBZVFJUYldkd2NsQkNjRE5STlVocU5YVTJYelZxUVRWTmFTMUJhR2hMY2s5dk5IZDJiR1kzZEVwd2JrRTVSa3gzTkVsd1RIQkZXbFZPU0RSNVJUWXRiME5DTWxWVFZteFZiVk0yYWxkbk1WZFZhVEI2WlhCclp6RTVXbmRQT1VzeWRtOVJTV2xMWTNGUmFEbFlOREJ6TjJ0ZmMwMVNabWMzVjBFeE1tVTVSbWRMTFhweGNsZHJiV1k0TW5Cd1VXcDVlbXd6ZDFkclNuaHpiSGQwYm1OcmJDMXJSMUl4ZGtaR1lubE1iakZ6Wm1OZllrVjZaRk5LY1dOTlIxOUlUMVUxTWtKeVVITTFlSE5QTmprNFZtRnNjWGgzZVhGVFZWWmtPV2MwWTAxTlYzbFRaVVJQUzJreGNGRmphRTB0TFhoSk4wdHVhelJGZEU5b1NGWkxTM1ZUU1RsT1FqQnlOM1JqVTJsVGEwcG9hM0F5UkVOTldtUlZiMk0zZUd0cE0xVmhNbmQ1VUd0NmNGOXlUMUpPZURGdk1GOXJSbHBSV1hwTU1WaHhiR3hETVZWd1JXNXNNbFJ2WmxsNlpWQktibkJPVDA5Wk1FdGZkMlF6T0ZSRmVrOXlTVTVmU0ZSWlVFTm9URjlSWVRaT2JscDZZbFl6WjFoR1RuVlFVWEp6Y2kxQmVsOTRhRGRuTWtaYVpXeG1RVXh4VUhKWmQwZDVjMEZWVm1saFpIaHJSV3RQYldSV01tMUlUM0pqZUc1dFRXYzFUblppVFRKeFkyZHpOVnBPYUV4c2RHVnNZblZRVEdkMGMzQm9kMk52VEhwdVpGZGpkSFpKZG5kVFZXOWxjMWd4VTI5bFVEQnFTek01WkhjMVdWVjBlbXRzTmxjNU1rNVBaRmR4VTA5NUxYSmtWbmx2Y2tsTU9Hb3hXVk5QVW1SSFN6Rndaek5KTUhKamJsVkJSazh4TjJOeVExOHhVMUZQV1hJMVdHVnpWR2gwU2tGV2FFOHhTMHR2WlhaVU9XaDJaSHBrVUZWd01saHVXSGhCVjBWNWEyVXlkVFl4WDNWT2NEWnJZMmgyYVhBMVZGbFNXV3Q1WkhkUmF6RkRMV0pLWDFGWVZsVmtUbGhsWW1sTGNsVlliVGN4VldaVVgwVXlielJUY25CSWNXUjRiMHMyVFdaUFluZHZjWEZqUlhRNVNYRmZSRzR3UzE5MlprSXRSR2R4Y25CUWQzbDBiMmxvZEhGUmVHTXdhVWxwTnpsU1RWTkVPSFpzY0dKb2NHRmFTMHQ1WkRBdFh6TmtTM0pSZEVGWE5saENVbFJKT0VwNmRWQjBkbVY1UjJnMVpWOUpPWE5GVkZCck1XZHZTVkJxYVVRM1pVODBhVmx6UzJVMlMwMUpTMkk1UlZKNk5WcGxObEYyT1dKUldFbFpXbmhwT1ZsbVdISjBRbEJ1ZW1GRFRIZFlaVVZuWXpZMmJGSm9ZV05sYzJkdFNtOHdSVmg1VldabUxYaFdVR0ZwYTFKcFFYbDJYMXBpY0VscFZuZzRlbkp4VmxabGFuUnJibGREVFRkQk9HVlZPRzFzUkhSU2EwSlVTa05hWW0xNFRGaHFURWx5TTBkWlpsVTFUWFEzWW5kaGVUUk1kbEZhVEhCM2FXc3daMFpwVld0SWExODNhWFUyWVZGVGRqTllhbEl6WlVwTFJrMXdMV1JNVVRSa04wNTRRekZDYW1aTVFsUk9hRE5LVXpac1YxcE9ibXR2V25oTmJITkpaMEpIY1VWNVdYUTNTMnhFU21aTFdVdzJZbmhNUWxKMldVdHhaRkZxU0RoU1F5MVNOVkpGWTFaRmNFb3pVVVZ2VUhJMVJtNHhNRUZFV0ZkWVpsYzBkVEJVWHpJelNGUmlVVGhxY0RGeU0yZEJObU56UjB0RlFVVlZRV0p6V1ZZMFYxRXRSR2hMU1VsNVEyaG5jMHhuUzJOcGFGTTRXbEJHZEdSMVpXOHpha3hET0c5cVFubGhTMng0TWpSbU9XcGhWbTkxZFZjek1YRnRTQzFRYUVONk56ZFZjVkZTY1V4T2FuQnpUMnd3ZVhJeE5UZzBSVkJVWVU1dWFtMVpWbXBOVkY5RFlsazViMFJWYm5Od1kzSTJUSFl6WVdaRE9UWXpSRll0VVRaeVdXZ3haMEpQT0hkNGFXaFZiVUp6VUROTFpucEVkMUI2Um5oemFrcGFPVTl1TTBnd04zWXljbkppWjNGS1VYVndRMnN6VmxSQmJ6TnRjVGxGTm5NMVZubzJOV1ZUVGtaNllYbFllVlZVU1ZkV1JWUnVhVzV5UTJkWGRqbGhPR1kxY0dGWVZXWnVVVWhIWlVKVFJYUXhjSFpFZWkxTWJHTjJjQzFhY0dwaVpIQTBhR0ZRVkdoSU5tTk5RMFpVTFU0NWNsbFdhbmxzV2tGWmNUSlNjR1pZVnpOSU5IRkNaMFF4ZGxjdFVWbHlkRE5aY1RkQ1dteGpNR1pLWldkeE1UQlVObVpOUjJaelYzQmxjRWxTYldRMVdWcENkVWRvZVcxUWRVVmhMV1pFVkc5blEyRmlaRGxJVW0xTlpUQkhhRmRzWW0xWU9XODNSSEp4ZG1VellYcHNhV0ZhV0VsWVNHcFNZbk15V21KM05GSnFlbWN3TTFkUkxVbEVWRTVwVTJnek1VUTVZMjlQVWsxRE5sbzVMV3hTT0ZsamRXRnZORmQzTUdneVkybG1VRmhYYkY5bGExUnlWMGxLYzJ4aFowNDFhMDFKUTBkR1UxaGxVbkkwWVhRMFJVVk9YeTExZWtsVE5XZDVMVUZ6YjB0cE9YTlliRlUxVjJ4Vk0ycDJUVkJzVUVKNmRWUkZhRUp1VmpGRU9FOXFhRk00VVdOeVJsTm1XRU4xWDFoek1YaGZiMWhTV25kSVEwOTVZWGx2U0c1bWNWWmtNbmg1YURCNlFqTlJTSEJ4WmpaNVJUVXRjbXBoWHpNMVEwUllZbUZmYlVvMk1UVlpabGxyVm01MVFWQkZaRUZtUkhrM2MwcHViRWw1V1MxSGJrTnNZV3B2VGtwSVpIYzBXRWxLUVVsU1IwdERNVTlPVG5wMGJrMXhjMnB1TVZJM2RXRlNjVXh5UTNKaFUyNTFUak52WmxwWE9ETldaVXN0ZG5vMVNISTFTSGRhWW5GUGRrUjBjVlZLVEVwSWJEbEVlRnAzVFZsblZWaEZXWE13Vm1kbFIwdGZOblptWDBkdmFGcHFPV3hUT0U0elNFUnFSV1IyUlVoRmVrNXJabFZCWkdGRmVFaDNNR2RmWTJReWRXOXhTbEJGUjA5eFZteHRTVWRET0ZSQ1F6STFVWE5RYnpGdFZ6aDJiMmxQWjI5VVZuVllOM2h2YmpkdFFqWnlRMEpLVW1sMVZYTmpkVEZYVDI4ME0waFlTalJHVlRKblZHVmFSRVl5TkRsMFRtUllWa3h0TlhsSFREVnBRV2R5YmpkNldrdEtORlZKVWxWb1kzTkVYelpzTm1SQlVFRktkbEl3ZDNSaGFsWkJjVTVmY0V4S1EyTnhaR2R0Y2pkR01EZzVjR1Z1VUhaMFUycDJjbEpuUlhoQ1ozcEJSbk5ZY210aVpIYzBUbmhuWHkxQ2EyOUxkbGMwU2xGeE5IcEtkR1kyWkdSaFVVVm1Sa3c1V0VGUE5HWm1kRmx2UnpKNVJuUXlWblEwYjNKRFYwSnlTMnhmZGxrM2NqaFFjRVJHZEVOWWREbHRTazFGYjFwUldsbEZZVFZwTW5Gc1owY3pOM1pTV0hSUFdYVkRia2x2ZGsxRVptMXBkbVpVWDBKUE0yTnNMVzFXV1dNNFNWUjJlRFYwZW5Wak9GQlhWWEZOVTNkNVIzcFhUWFJGTm1WRk1IVkZTV1pQV0VSQ01IbGthbFZuWkVFNFJEQjFiVlJWVm5aeGVqRkNSV3Q2Tm1abVluaERTSEppZUZWTGNrY3RaMUF3UmtaUlozQmhZbmhMTUdaV1kwRlZiVEZZTjNJMVdVcHNhMDVFVTFJMWVsUnpTbEZoUkUwdFEzZHdVMVZ1UnpWbGJuVTNha2RtUzJsclRrNVZjMjV2TTJaS2QwWjZkelYyU25ab2FuRjJaVkpZU2pkTFZGQmxXVkZxY1VJNGJHUk1WSFZyU0hseFZqRklPR296YzJSalVXaGZPRVJxZFMwM1ZHUlpURlpITFdOdGJUbFVOMHBRVUVGSVN6QjRSMVo0VkRjMlVsbFBlbDh5U1VoMFVXWTFSa2RTVjFWT1RVNU9aSFphVG5aU1V6VkVSa0pxTkdwUFdHZGtMV1JYY21OWU5HOURXVFZZVDNSeFZYaDNibVpXVVRFM2VEUXpWRTgxU2t4VlRIcFNaak0xWlZJM05IZEhSVGRuWTFSWFlYSlRXWEJWYUdNdFRXMVVia1JEYVZoaU1teE1hbmRDYzJjMExYWm5NRkpUVDNBMlUyRllXVFp3WjNoT1FUWnVZbTB3ZFdSUGJqYzVlbEJXZWxSVk5uZFFVbXByVmtWbWNtZDRXVFJIVnpkYVRITkhXbGxNV1ZWeldHeFdhV3htVDNOcVFsWktjaTF6VDJjNE9XeE5jbmh4WnpoeFoyTkVTVzVpV2tNemVTMVhlbU16UVhrMlVFNVZWVXR1ZGpjd05GVjFOREp4TjNOVlJsTmtYMGQ0Y3pCZldGVmZjalUwUzNOMGFWVTJaelZ4VTNaWFpGUlVTRlExVkROT2QzRnVUbVY2Y3poRGEzbFBZMFJRVHkxeGNqSkxRbTFpTmxSbFJXSnpVRWRRU2pCeWRFOU1UWFl0VDBaRFREQTFURmx3TlRacE9UUjNjR2w2Vmt0VmVYcDBSM2QzYVhNemEzQkhVbEJ2WDNaUFRIZENPSE5mVjBOYU5tMHhkSHAxVlhkRVRHUm1ZbnBqVlRSZmNuaE5hMUJJT1RGVmVpMVFZelJHZGxWTFIyNVZkMHB4UVRadU0xaGthWHBmZWtSQlpHeGFTSGhDY0haVGFVdFRhVTk1YjI5YWJWa3haMmRMV1Vob2JucDJTMEpLYTFNMlNVaENjMUU1VjBFNGFETjJkRzVOTm5Cd01WTTBiV1ZRY21NMVgyeE5jQzF4YlZoMFExcGljalpZUVRocGJUVTFVMlIwWkhwYWFYVXlRbWcyYkRWNFZVMTFVbFZqVTJ3eFYxWkpRMGt6TnpoNk5ISXdhMUl6VTJGcVVsOVJiR000VnpCM1puWTVSV1ExVFhwaFdFMTVaa2RUT1hBd1kxOXRjMk13WmtaNWNHbFlWWGh0ZURKWlowaEhXa3hNUjJwaFRrSmZSMEV5VmpWc1NsSmlibmczTW14MlRFaHhOeTE0WkZSSGMwVTJlakYyZFVsNVZuZHFaMlYzY1RsUWVFUkhRVlF4VnpGNWJIVXpNek5HVGxZMU5XbEdhMDFzTFhwaFMwUnBaa05TTkRSa056QkZURWhTWkZaeU9YUTVkRFpvVWtSSldITmpaVE0zYW5rdFpVMHplRzFaUTNsZlpXUmxYelpwYlVvNGRWTjNlbmN1YVZKMk9HeDRjR2xPUm05aGJXWmhTVkJGVG5sblFRIiwib3JpZ2luIjoiaHR0cHM6Ly9sb2dpbi5taWNyb3NvZnQuY29tIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ",
"attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEYwRAIgSGoNTfOV200W9rJQRftMtAuauM5s6ZTKrEeZ762fFC8CIGPO-f31L0ACPwteJe9AItu6wDo69sOtUYNsXdRAYGxdY3g1Y4FZAsIwggK-MIIBpqADAgECAgRd0E7hMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9vdCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0MDAwMDAwWjBvMQswCQYDVQQGEwJTRTESMBAGA1UECgwJWXViaWNvIEFCMSIwIAYDVQQLDBlBdXRoZW50aWNhdG9yIEF0dGVzdGF0aW9uMSgwJgYDVQQDDB9ZdWJpY28gVTJGIEVFIFNlcmlhbCAxNTczOTMyNzY5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI2ngCz5RGdaptNGHvrRBgM2scepSmIHj530c9YGhO2cxK6kyhjWdw0gNgRPWXhQU7ObRDrstHsjdmj8C_VyA1KNsMGowIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjcwEwYLKwYBBAGC5RwCAQEEBAMCBDAwIQYLKwYBBAGC5RwBAQQEEgQQL8BXn4ETR-qxFrtajbkgKjAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCHyh0lTij0UhRhKbeHVSGtMZ-e6RzMUl3eiMQmfHNkGcMHJ7tV_NUUnTlVDO7zn0cjeZHErkUfgcujoudWsqsqHtw8SEQKtCASBu2MjAP4ABdCQABlPV7KeDnvajzaj_pRr17T834GS1dXdPDM8aAxBpYaCqXjWDKJFz7xgEFtYX6jOjO9FPaH6p7Gl3tjA9WToVGIuE3KKqGX2qmetoe2txDISX77k8ycJRLKA3IpPNL0Mft3wb6pMVlXbdt15wDKNLYRx_O0UtKzQC7r6Msu-31HSmMZbbyQ5sTwX0EGQm0spm09rDdb8vJlc7Gzc8r6IQ0zkrdS1tso8-LZr8BhaGF1dGhEYXRhWK81bJ7UoJMhuWlfHq-RggPxtV9onaYfvJYYTBV92mgMgcUAAAADL8BXn4ETR-qxFrtajbkgKgAQRXR6RPQ-IF9OuRumbHzIBaUBAgMmIAEhWCCYwv1D0HA1BMplBBTXI6zJm3i01rofVOUdeiswK6YsvSJYINBBuEU0NLN94B26pnay1ZcbfOe511iq_QNPTsq3ZqvmomtjcmVkUHJvdGVjdAFraG1hYy1zZWNyZXT1"
}
}
}
Using the PowerShell Module DSInternals.Passkeys
This is the easy way with the PowerShell Module “DSInternals.Passkeys”
Connect to Microsoft Graph with the Scope “UserAuthenticationMethod.ReadWrite.All”
Pipe all three Commands and this will get the Challenge from Entra ID, Save the Passkey on the Security Key, Register the Passkey in Entra ID.
Bevore that, i deleted the Passkey on the Yubikey and removed the Security Key in Security Information to register the Security Key again.
Connect-MgGraph -Scopes UserAuthenticationMethod.ReadWrite.All -TenantId icewolfch.onmicrosoft.com -NoWelcome
Get-PasskeyRegistrationOptions -UserId 'm.muster@icewolf.ch' | New-Passkey -DisplayName 'YubiKey 5 NFC' | Register-Passkey -UserId 'm.muster@icewolf.ch'
The Passkey has been saved on the Yubikey
The Security Key has been added to Security Info (Entra ID)
Troubleshooting
During my Tests i got the following Error Message: User or Group restriction policy failed for user error_user_group_restriction_policy_failure
Get-PasskeyRegistrationOptions -UserId 'm.mouse@icewolf.li' | New-Passkey -DisplayName 'YubiKey 5 NFC' | Register-Passkey -UserId 'm.mouse@icewolf.li'
It was, because i had limited who can register a Security Key to a Group and the User was not a Member of that Group
It might also happen, that you have applied a Key Restriction Policies and only selected AAGUID’s are allowed
Summary
I’ve learned a lot during this Article. The PowerShell Module made it very easy and i don’t had to learn Python. This can be used to Preprovision Passkeys on Security Keys for Endusers or Admin Users without having to Log In Interactively with the Browser to the Security Info Page.
Regards
Andres Bohren