Add support for Okta (#18)

This commit is contained in:
Alex Seigler
2025-01-16 15:29:49 +01:00
committed by GitHub
parent 92fbd8078e
commit 7fee5c4322
30 changed files with 3305 additions and 347 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ The `DSInternals.Win32.WebAuthn.Adapter` library additionally uses classes defin
## PowerShell Module
The [DSInternals.Passkeys](https://www.powershellgallery.com/packages/DSInternals.Passkeys) PowerShell module uses the `DSInternals.Win32.WebAuthn` library together with the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/api/resources/fido2authenticationmethod?view=graph-rest-beta) to provide Microsoft Entra ID administrators the capability of registering Passkeys on behalf of other users:
The [DSInternals.Passkeys](https://www.powershellgallery.com/packages/DSInternals.Passkeys) PowerShell module uses the `DSInternals.Win32.WebAuthn` library together with the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/api/resources/fido2authenticationmethod?view=graph-rest-beta) and the [Okta API](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/UserFactor/#tag/UserFactor/operation/enrollFactor) to provide Microsoft Entra ID and Okta administrators respectively the capability of registering passkeys on behalf of other users:
![PowerShell Passkey Registration Screenshot](../Documentation/Screenshots/powershell.png)
+14
View File
@@ -52,6 +52,20 @@ jobs:
shell: powershell
run: .\Build-PowerShellModule.ps1 -Configuration Release
- name: Test PowerShell Module
env:
EntraIdTenantId: ${{ secrets.ENTRAIDTENANTID }}
EntraIdClientId: ${{ secrets.ENTRAIDCLIENTID }}
EntraIdClientSecret: ${{ secrets.ENTRAIDCLIENTSECRET }}
EntraIdUserId: ${{ secrets.ENTRAIDUSERID }}
OktaTenantId: ${{ secrets.OKTATENANTID }}
OktaClientId: ${{ secrets.OKTACLIENTID }}
OktaJsonWebKey: ${{ secrets.OKTAJSONWEBKEY }}
OktaUserId: ${{ secrets.OKTAUSERID }}
shell: pwsh
run: |
Invoke-Pester .\Build\bin\PSModule\Release\DSInternals.Passkeys\DSInternals.Passkeys.Tests.ps1 -Passthru
- name: Upload PowerShell Module as Artifact
uses: actions/upload-artifact@v4
with:
+1
View File
@@ -354,3 +354,4 @@ MigrationBackup/
# Private Keys and API Keys
*.Private.snk
*.key
.vscode/launch.json
+8 -1
View File
@@ -1,6 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "PowerShell Launch Current File",
"type": "PowerShell",
"request": "launch",
"script": "${file}",
"cwd": "${cwd}"
},
{
"name": "Debug FIDO2 UI",
"type": "clr",
@@ -13,4 +20,4 @@
"preLaunchTask": "build"
}
]
}
}
+202
View File
@@ -0,0 +1,202 @@
---
external help file: DSInternals.Passkeys-help.xml
Module Name: DSInternals.Passkeys
online version: https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/Connect-Okta.md
schema: 2.0.0
---
# Connect-Okta
## SYNOPSIS
Retrieves an access token to interact with Okta APIs.
## SYNTAX
### If you are using the authorization code flow
```
Connect-Okta -Tenant <String> -ClientId <String> [-Scopes <String[]>] [-ProgressAction <ActionPreference>]
[<CommonParameters>]
```
### If you are using the client credentials flow
```
Connect-Okta -Tenant <String> -ClientId <String> [-Scopes <String[]>]
-JsonWebKey <ValidateNotNullOrEmptyAttribute> [-ProgressAction <ActionPreference>] [<CommonParameters>]
```
## DESCRIPTION
In order to use this module with Okta, you will need to create an application within your Okta tenant. You may use the authorization code flow with PKCE (highly recommended for interactive login), or the client credentials flow (for headless use). If you are unsure which to choose, select the authorization code flow, as it is the most secure option.
### Setup to use the authorization code flow with PKCE
To use the authorization code flow, you will need to create a Single Page App (SPA). In the application area in the Okta admin console, click `Create App Integration`, then select `OIDC - OpenID Connect` for the sign-in method, then `Single-Page Application` for the application type, and click next. Note: This process is nearly identical to [User-based API access setup](https://developer.okta.com/docs/reference/rest/#user-based-api-access-setup) in the Okta developer documentation.
<br><br>
<img width="559" alt="Create SPA" src="https://github.com/user-attachments/assets/9a85d754-8e3c-4756-80e5-1dca4635f8f4">
<br><br>
On the next page, give the app integration a meaningful name. Under assignments, select either `Limit access to selected groups` if you already have a group you want to assign this application to, or select `Skip group assignment for now`. You can change this later, but you are required to select an option. Everything else can be left at the defaults. Click save.
<br><br>
<img width="559" alt="SPA options" src="https://github.com/user-attachments/assets/2c83f1b7-8f50-49c3-bfe2-fb77aec50aa6">
<br><br>
WARNING: Do NOT, under ANY circumstances, select `Allow everyone in your organization to access`. Use one of the other options. This is your warning. Seriously. Do not do this.
<br><br>
<img width="559" alt="Do not select allow everyone" src="https://github.com/user-attachments/assets/bb6cebbd-a9a6-4e12-b198-1040163bf962">
<br><br>
On the next page, ensure that Proof Key for Code Exchange (PKCE) is selected, and note the client ID, you will need this to connect.
<br><br>
<img width="517" alt="Take note of the Client ID and ensure require PKCE is selected" src="https://github.com/user-attachments/assets/35455c7e-07e8-4112-aefc-e55ba1f56e91">
<br><br>
On the Assignments tab, assing the application to the appropriate user(s). On the Okta API Scopes tab, scroll down to the okta.users.manage scope and click the `Grant` button to the right of the scope name. This is the minimal permission required to use this library, since the whole point of the library is to register a passkey on behalf of someone else. This scope allows the app to create new users and to manage all users' profile and credentials information. Be very, very careful assigning this app.
Okta API scopes should look approximately like this:
<br><br>
<img width="506" alt="Okta API scopes: okta.users.manage" src="https://github.com/user-attachments/assets/434bdeb4-fe12-4578-b1c6-dc5f313936f6">
<br><br>
Assignments tab should look something like this:
<br><br>
<img width="506" alt="Okta application assignments" src="https://github.com/user-attachments/assets/c977cb67-05b1-4ccc-98d1-875fcd86a3ca">
<br><br>
### Setup to use the client credentials flow
If you intend to use the client credentials flow, you must set up an API service application as described below. Note: This process is nearly identical to the [Service-based API access setup](https://developer.okta.com/docs/reference/rest/#service-based-api-access-setup) in the Okta developer documentation.
<img width="559" alt="" src="https://github.com/user-attachments/assets/2a62bb79-7c93-4af8-9527-c7073ae450d5">
<br><br>
On the next page, give the app integration a meaningful name. Click save.
<br><br>
<img width="559" alt="" src="https://github.com/user-attachments/assets/101c98ad-6077-4182-be5d-630c54cfa38b">
<br><br>
On the next page, note the client ID, you will need this to connect. Switch the client authentication configuration from the default client secret to public key/private key by clicking edit, then selecting the radio button.
<br><br>
<img width="572" alt="image" src="https://github.com/user-attachments/assets/96aa533d-bf16-4920-8690-be4ebf703d90">
<br><br>
The public keys configuration section will open. For a simple test configuration, leave the radio button for save keys to Okta selected. You will need to add at least one key here, by clicking the add key button.
<br><br>
<img width="508" alt="image" src="https://github.com/user-attachments/assets/e5539794-1e79-43e9-ac83-a47dc0bef4e6">
<br><br>
The add a public key dialog will appear. You have the option to paste in your own public key, or let Okta generate one based on RS256 for you by clicking generate new key.
<br><br>
<img width="508" alt="image" src="https://github.com/user-attachments/assets/e9c0a690-c40b-418d-b7bb-3f83dbf418be">
<br><br>
Leave the private key format at the default JSON, and click the copy to clipboard button. Save the contents in a safe place, as this key is a sensitive credential, then click save.
<br><br>
<img width="539" alt="image" src="https://github.com/user-attachments/assets/10ec207b-859d-483a-9c6b-2d821d2d4f60">
<br><br>
I typically compress the output to avoid issues later. In the compressed format the key is ready to use as a parameter value to the [-JsonWebKey parameter](https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/Connect-Okta.md#-jsonwebkey)
<br><br>
```powershell
PS C:\> $jwk = @'
{
"kty": "RSA",
"e": "AQAB",
"kid": "EE3QB0WvhuOwR9DuR6717OERKbDrBemrDKOK4Xvbf8c",
"d": "TmljZSB0cnkhICBCdXQgdGhpcyBpc24ndCBhIHJlYWwga2V5Lg",
"p": "wqFCdWVuIGludGVudG8hIFBlcm8gZXN0YSBubyBlcyB1bmEgY2xhdmUgcmVhbC4",
"q": "TmV0dGVyIFZlcnN1Y2ghIEFiZXIgZGFzIGlzdCBrZWluIGVjaHRlciBTY2hsw7xzc2VsLg",
"dp": "QmVsbGUgdGVudGF0aXZlICEgTWFpcyBjZSBuJ2VzdCBwYXMgdW5lIHZyYWllIGNsw6ku",
"dq": "5LiN6ZSZ55qE5bCd6K-V77yB5L2G6L-Z5LiN5piv55yf5q2j55qE6ZKl5YyZ44CC",
"qi": "7KKL7J2AIOyLnOuPhOuEpOyalCEg7ZWY7KeA66eMIOydtOqxtCDsp4Tsp5wg7Je07Ieg6rCAIOyVhOuLiOyXkOyalC4",
"n": "uT64MHF-qH5F-jLAoWxuwI8_YJ2cdkMIjvlyazJnB0xjvtddS14O8cfDHW2lxHWiOor-jarCK282WC1BukjnizCeUl7YoWDWnVNOcQd1hi__nC5cjhgnjVPNgBiRQhctj8e6HWgKfMHTxZ_vaMsqQZJ9QwYy1NzkxchqcI9pZN1zlA2LHdXsXzvJeRxzUg8EjGxaDKuLPHPUfZxf0Wr4Jv-lNVYzwg84XtzOoZ0v9Hb91ZISmVKKtMegXX2ahGRgMeI1pgX8oCG8TCgx7x-SmViJfi9iNfznJWyA_lZiUKu3qJMsFJdgfRp9egCZtUl6P_nnpqe-_-MNmy55WXhc0Q"
}
'@
| ConvertFrom-Json | ConvertTo-Json -Compress
```
On the Okta API Scopes tab, scroll down to the okta.users.manage scope and click the `Grant` button to the right of the scope name. This is the minimal permission required to use this library, since the whole point of the library is to register a passkey on behalf of someone else. This scope allows the app to create new users and to manage all users' profile and credentials information. Okta API scopes should look approximately like this:
<br><br>
Okta API scopes: okta.users.manage" src="https://github.com/user-attachments/assets/434bdeb4-fe12-4578-b1c6-dc5f313936f6">
<br><br>
On the Admin roles tab, you will need to assign an admin role to this app. The `Help Desk Administrator` role seems to work well for this module.
<br><br>
<img width="506" alt="Admin assignment help desk administrator role" src="https://github.com/user-attachments/assets/8ffb2acb-67cb-4cb4-9aef-6747540a0fdd">
<br>
## EXAMPLES
### Example 1
```powershell
PS C:\> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
```
Connects to `example.okta.com` tenant using application with client id `0oakmj8hvxvtvCy3P5d7` with the authentication code flow with PKCE
### Example 2
```powershell
PS C:\> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7 -Scopes @('okta.users.manage','okta.something.else')
```
Connects to `example.okta.com` tenant using application with client id `0oakmj8hvxvtvCy3P5d7` with the authentication code flow with PKCE, requesting scopes `'okta.users.manage'` and `'okta.something.else'`
### Example 3
```powershell
PS C:\> $jwk = '{"kty":"RSA","kid":"EE3QB0WvhuOwR9DuR6717OERKbDrBemrDKOK4Xvbf8c","d":"TmljZSB0cnkhICBCdXQgdGhpcyBpc...'
PS C:\> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7 -Scopes @('okta.users.manage','okta.something.else') -JsonWebKey $jwk
```
Connects to `example.okta.com` tenant using application with client id `0oakmj8hvxvtvCy3P5d7` with the client credentials flow using `$jwk` as the the client credential, requesting scopes `'okta.users.manage'` and `'okta.something.else'`
## PARAMETERS
### -Tenant
The unique identifier of Okta tenant, like `'example.okta.com'`.
```yaml
Type: String
Parameter Sets: (All)
Aliases:
Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -ClientId
The client id of the Okta application used to obtain an access token.
```yaml
Type: String
Parameter Sets: (All)
Aliases:
Required: True
Position: Named
Default value: None
Accept pipeline input: True (ByValue)
Accept wildcard characters: False
```
### -Scopes
Scopes to request for the access token. Defaults to `'okta.users.manage'`.
```yaml
Type: String[]
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -JsonWebKey
The JSON Web Key used to authenticate to the Okta application, in order to obtain access token using the client credentials OAuth flow.
```yaml
Parameter Sets: ClientCredentials
Aliases: jwk
Required: True
Position: Named
Default value: None
Accept pipeline input: True (ByValue)
Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
## OUTPUTS
## NOTES
## RELATED LINKS
https://developer.okta.com/docs/reference/rest/
@@ -0,0 +1,40 @@
---
external help file: DSInternals.Passkeys-help.xml
Module Name: DSInternals.Passkeys
online version:
schema: 2.0.0
---
# Disconnect-Okta
## SYNOPSIS
Revokes Okta access token.
## SYNTAX
```
Disconnect-Okta [<CommonParameters>]
```
## DESCRIPTION
Revokes the Okta access token cached from the call to `Connect-Okta`.
## EXAMPLES
### EXAMPLE 1
```
Disconnect-Okta
```
## PARAMETERS
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
## OUTPUTS
## NOTES
## RELATED LINKS
@@ -8,56 +8,56 @@ schema: 2.0.0
# Get-PasskeyRegistrationOptions
## SYNOPSIS
Retrieves creation options required to generate and register a Microsoft Entra ID-compatible passkey.
Retrieves creation options required to generate and register a Microsoft Entra ID or Okta compatible passkey.
## SYNTAX
```
Get-PasskeyRegistrationOptions [-UserId] <String> [[-ChallengeTimeout] <TimeSpan>] [<CommonParameters>]
Get-PasskeyRegistrationOptions [-UserId] <String> [[-ChallengeTimeout] <TimeSpan>]
[-ProgressAction <ActionPreference>] [<CommonParameters>]
```
## DESCRIPTION
{{ Fill in the Description }}
Retrieves the [credential creation options](https://w3c.github.io/webauthn/#dictionary-makecredentialoptions) required to generate a Microsoft Entra ID or Okta-compatible passkey.
## EXAMPLES
### EXAMPLE 1
```powershell
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com'
### EXAMPLE 1 (Entra ID)
```
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com'
```
### EXAMPLE 2
```powershell
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (New-TimeSpan -Minutes 10)
### EXAMPLE 2 (Entra ID)
```
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (New-TimeSpan -Minutes 10)
```
### EXAMPLE 3 (Okta)
```
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7
```
### EXAMPLE 4 (Okta)
```
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 -ChallengeTimeout (New-TimeSpan -Seconds 60)
```
## PARAMETERS
### -ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request.
The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.
```yaml
Type: TimeSpan
Parameter Sets: (All)
Aliases:
Required: False
Position: 2
Default value: (New-TimeSpan -Minutes 5)
Accept pipeline input: False
Accept wildcard characters: False
```
### -UserId
The unique identifier of user.
The unique identifier of user.
For Entra ID, this is the object id (GUID) or UPN.
For Okta, this is the unique identifier of Okta user.
```yaml
Type: String
Parameter Sets: (All)
Aliases:
Aliases: User
Required: True
Position: 1
@@ -66,6 +66,23 @@ Accept pipeline input: False
Accept wildcard characters: False
```
### -ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request.
For Entra ID, the default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.
For Okta, the default value is 300 seconds, with the accepted range being between 1 second and 1 day.
```yaml
Type: TimeSpan
Parameter Sets: (All)
Aliases: Timeout
Required: False
Position: 2
Default value: (New-TimeSpan -Minutes 5)
Accept pipeline input: False
Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
@@ -73,9 +90,13 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
## OUTPUTS
### DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnCredentialCreationOptions
### DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOption
## NOTES
Self-service operations aren't supported.
More info at https://learn.microsoft.com/en-us/graph/api/fido2authenticationmethod-creationoptions
Self-service operations aren't supported with Entra ID.
## RELATED LINKS
[More info about Entra ID](https://learn.microsoft.com/en-us/graph/api/fido2authenticationmethod-creationoptions)
<br>
[More info about Okta](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/UserFactor/#tag/UserFactor/operation/enrollFactor)
+27 -17
View File
@@ -8,41 +8,49 @@ schema: 2.0.0
# New-Passkey
## SYNOPSIS
Creates a new Microsoft Entra ID-compatible passkey.
Creates a new Microsoft Entra ID or Okta-compatible passkey.
## SYNTAX
```
New-Passkey [-Options] <MicrosoftGraphWebauthnCredentialCreationOptions> [-DisplayName] <String>
[<CommonParameters>]
New-Passkey [-Options] <WebauthnCredentialCreationOptions> [[-DisplayName] <String>]
[-ProgressAction <ActionPreference>] [<CommonParameters>]
```
## DESCRIPTION
{{ Fill in the Description }}
Takes the `MicrosoftGraphWebauthnCredentialCreationOptions` or `OktaWebauthnCredentialCreationOptions` object from `Get-PasskeyRegistrationOptions` and uses them to create a credential using the system dialogs.
## EXAMPLES
### EXAMPLE 1
### EXAMPLE 1 (Entra ID)
```
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
```
```powershell
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
### EXAMPLE 2 (Okta)
```
PS C:\> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> New-Passkey -Options $options
```
### EXAMPLE 3 (Okta)
```
PS C:\> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 | New-Passkey
```
## PARAMETERS
### -DisplayName
Custom name given to the registered passkey.
Custom name given to the Entra ID registered passkey.
```yaml
Type: String
Parameter Sets: (All)
Aliases:
Required: True
Required: False
Position: 2
Default value: None
Accept pipeline input: False
@@ -50,11 +58,10 @@ Accept wildcard characters: False
```
### -Options
Options required to generate a Microsoft Entra ID-compatible passkey.
Options required to generate a Microsoft Entra ID or Okta compatible passkey.
```yaml
Type: MicrosoftGraphWebauthnCredentialCreationOptions
Type: WebauthnCredentialCreationOptions
Parameter Sets: (All)
Aliases:
@@ -69,10 +76,13 @@ Accept wildcard characters: False
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
### DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOptions
## OUTPUTS
### DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnAttestationResponse
### DSInternals.Win32.WebAuthn.WebauthnAttestationResponse
## NOTES
## RELATED LINKS
[Microsoft WebAuthn portal](https://learn.microsoft.com/en-us/windows/win32/webauthn/-webauthn-portal)
+10 -4
View File
@@ -9,13 +9,13 @@ Locale: en-US
## Description
This PowerShell module allows administrative registration of passkeys (i.e. FIDO2 security keys and Microsoft Authenticator mobile app) in Microsoft Entra ID (formerly Azure Active Directory).
This PowerShell module allows administrative registration of passkeys (i.e. FIDO2 security keys and Microsoft Authenticator mobile app) in Microsoft Entra ID (formerly Azure Active Directory) as well as Okta.
## DSInternals.Passkeys Cmdlets
### [Register-Passkey](Register-Passkey.md)
Registers a new passkey in Microsoft Entra ID.
Registers a new passkey in Microsoft Entra ID or Okta.
```powershell
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
@@ -23,10 +23,16 @@ Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano'
Disconnect-MgGraph
```
```powershell
Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
Register-Passkey -UserId 00ub61wm1aqmawzRC5d7'
Disconnect-Okta
```
### [Get-PasskeyRegistrationOptions](Get-PasskeyRegistrationOptions.md)
Retrieves creation options required to generate and register a Microsoft Entra ID-compatible passkey.
Retrieves creation options required to generate and register a Microsoft Entra ID or Okta-compatible passkey.
### [New-Passkey](New-Passkey.md)
Creates a new Microsoft Entra ID-compatible passkey.
Creates a new Microsoft Entra ID or Okta-compatible passkey.
+83 -69
View File
@@ -1,15 +1,14 @@
---
external help file: DSInternals.Passkeys-help.xml
Module Name: DSInternals.Passkeys
online version: https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/Register-Passkey.md
online version: https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/New-Passkey.md
schema: 2.0.0
---
# Register-Passkey
## SYNOPSIS
Registers a new passkey in Microsoft Entra ID.
Registers a newly created passkey with Microsoft Entra ID or Okta.
## SYNTAX
@@ -20,95 +19,104 @@ Register-Passkey -UserId <String> -DisplayName <String> [-ChallengeTimeout <Time
### Existing
```
Register-Passkey -UserId <String> -Passkey <MicrosoftGraphWebauthnAttestationResponse> [<CommonParameters>]
Register-Passkey -UserId <String> -Passkey <WebauthnAttestationResponse> [<CommonParameters>]
```
## DESCRIPTION
{{ Fill in the Description }}
## EXAMPLES
### EXAMPLE 1
```powershell
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano'
### EXAMPLE 1 (Entra ID)
```
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano'
```
### EXAMPLE 2
```powershell
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano' -ChallengeTimeout (New-TimeSpan -Minutes 10)
### EXAMPLE 2 (Entra ID)
```
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano' -ChallengeTimeout (New-TimeSpan -Minutes 10)
```
### EXAMPLE 3
### EXAMPLE 3 (Entra ID)
```
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
```
```powershell
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
### EXAMPLE 4 (Okta)
```
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Register-Passkey -UserId 00eDuihq64pgP1gVD0x7
```
### EXAMPLE 5 (Okta)
```
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 | New-Passkey | Register-Passkey
```
## PARAMETERS
### -ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request.
The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.
```yaml
Type: TimeSpan
Parameter Sets: New
Aliases:
Required: False
Position: Named
Default value: (New-TimeSpan -Minutes 5)
Accept pipeline input: False
Accept wildcard characters: False
```
### -DisplayName
Custom name given to the registered passkey.
```yaml
Type: String
Parameter Sets: New
Aliases:
Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -Passkey
The passkey to be registered.
```yaml
Type: MicrosoftGraphWebauthnAttestationResponse
Parameter Sets: Existing
Aliases:
Required: True
Position: Named
Default value: None
Accept pipeline input: True (ByValue)
Accept wildcard characters: False
```
### -UserId
The unique identifier of user.
The unique identifier of user.
For Entra ID, this is the object id (GUID) or UPN.
For Okta, this is the unique identifier of Okta user.
```yaml
Type: String
Parameter Sets: (All)
Aliases: User
Required: True
Position: 1
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -Passkey
The passkey to be registered.
```yaml
Type: DSInternals.Win32.WebAuthn.WebauthnAttestationResponse
Parameter Sets: (Existing)
Aliases:
Required: True
Position: Named
Position: 2
Default value: None
Accept pipeline input: True
Accept wildcard characters: False
```
### -DisplayName
Custom name given to the Entra ID registered passkey.
```yaml
Type: String
Parameter Sets: (EntraIDNew)
Aliases:
Required: False
Position: 2
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request.
For Entra ID, the default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.
For Okta, the default value is 300 seconds, with the accepted range being between 1 second and 1 day.
```yaml
Type: TimeSpan
Parameter Sets: (EntraIDNew, OktaNew)
Aliases: Timeout
Required: False
Position: 3
Default value: (New-TimeSpan -Minutes 5)
Accept pipeline input: False
Accept wildcard characters: False
```
@@ -117,11 +125,17 @@ Accept wildcard characters: False
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
### DSInternals.Win32.WebAuthn.WebauthnAttestationResponse
## OUTPUTS
### Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod
### DSInternals.Win32.WebAuthn.Okta.OktaFido2AuthenticationMethod
## NOTES
More info at https://learn.microsoft.com/en-us/graph/api/authentication-post-fido2methods
## RELATED LINKS
[More info for Entra ID](https://learn.microsoft.com/en-us/graph/api/authentication-post-fido2methods)
<br>
[More info for Okta](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/UserFactor/#tag/UserFactor/operation/activateFactor)
@@ -0,0 +1,138 @@
BeforeAll {
Add-Type -Path "./build/bin/DSInternals.Win32.WebAuthn.Tests/release/DSInternals.Win32.WebAuthn.Tests.dll" -ErrorAction Stop
Import-Module .\Build\bin\PSModule\Release\DSInternals.Passkeys\DSInternals.Passkeys.psm1 -Force
}
Describe 'EntraID Tests' {
BeforeAll {
$SecureClientSecret = ConvertTo-SecureString -String $env:EntraIdClientSecret -AsPlainText -Force
$ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:EntraIdClientId, $SecureClientSecret
Connect-MgGraph -TenantId $env:EntraIdTenantId -ClientSecretCredential $ClientSecretCredential
}
It "Registers passkeys to Entra ID for each credential parameter option" {
$options = Get-PasskeyRegistrationOptions -UserId $env:EntraIdUserId
$options | Should -BeOfType [DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnCredentialCreationOptions]
$options.ChallengeTimeout | Should -BeGreaterThan (Get-Date)
$options.PublicKeyOptions.RelyingParty.Id | Should -Be "login.microsoft.com"
$options.PublicKeyOptions.PublicKeyCredentialParameters.Count | Should -Be 2
$factory0 = [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]::new()
$factory0 | Should -BeOfType [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]
$passkey0 = $factory0.MakePasskey($options, 0)
$passkey0 | Should -BeOfType [DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnAttestationResponse]
$passkey0.DisplayName | Should -BeLike 'DSInternals.Passkeys*'
$factory1 = [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]::new()
$factory1 | Should -BeOfType [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]
$passkey1 = $factory1.MakePasskey($options, 1)
$passkey1 | Should -BeOfType [DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnAttestationResponse]
$passkey1.DisplayName | Should -BeLike 'DSInternals.Passkeys*'
$result0 = Register-Passkey -Passkey $passkey0 -UserId $env:EntraIdUserId
$result0 | Should -Not -BeNullOrEmpty
$result0 | Should -BeOfType [Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod]
$result0.AaGuid | Should -Be "4453496e-7465-726e-616c-730000000000"
$result0.CreatedDateTime | Should -BeLessThan $options.ChallengeTimeout
$result0.AttestationCertificates.Count | Should -Be 1
$credentialId0 = $result0.Id
$result1 = Register-Passkey -Passkey $passkey1 -UserId $env:EntraIdUserId
$result1 | Should -Not -BeNullOrEmpty
$result1 | Should -BeOfType [Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod]
$result1.AaGuid | Should -Be "4453496e-7465-726e-616c-730000000000"
$result1.CreatedDateTime | Should -BeLessThan $options.ChallengeTimeout
$result1.AttestationCertificates.Count | Should -Be 1
$credentialId1 = $result1.Id
$result0.AttestationCertificates[0] | Should -Not -Be $result1.AttestationCertificates[0]
$result0.Id | Should -Not -Be $result1.Id
[string] $credentialDeletionUrl0 = '/beta/users/{0}/authentication/fido2Methods/{1}' -f [uri]::EscapeDataString($env:EntraIdUserId), $credentialId0
$response0 = Invoke-MgGraphRequest -Method Delete -Uri $credentialDeletionUrl0
$response0 | Should -BeNullOrEmpty
[string] $credentialDeletionUrl1 = '/beta/users/{0}/authentication/fido2Methods/{1}' -f [uri]::EscapeDataString($env:EntraIdUserId), $credentialId1
$response1 = Invoke-MgGraphRequest -Method Delete -Uri $credentialDeletionUrl1
$response1 | Should -BeNullOrEmpty
}
AfterAll {
Disconnect-MgGraph
}
}
Describe "Okta Tests" {
BeforeAll {
Connect-Okta -Tenant $env:OktaTenantId -ClientId $env:OKtaClientId -Scopes @('okta.users.manage') -JsonWebKey $env:OktaJsonWebKey
}
It "Registers passkeys to Okta for each credential parameter option" {
$options0 = Get-PasskeyRegistrationOptions -UserId $env:OktaUserId
$options0 | Should -BeOfType [DSInternals.Win32.WebAuthn.Okta.OktaWebauthnCredentialCreationOptions]
$options0.PublicKeyOptions.RelyingParty.Id | Should -Be $env:OktaTenantId
$options0.PublicKeyOptions.PublicKeyCredentialParameters.Count | Should -Be 2
$factory0 = [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]::new()
$factory0 | Should -BeOfType [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]
$passkey0 = $factory0.MakePasskey($options0, 0)
$passkey0 | Should -BeOfType [DSInternals.Win32.WebAuthn.Okta.OktaWebauthnAttestationResponse]
$passkey0 | Should -Not -BeNullOrEmpty
$factorId0 = $passkey0.FactorId
$result0 = Register-Passkey -Passkey $passkey0 -UserId $env:OktaUserId
$result0 | Should -Not -BeNullOrEmpty
$result0 | Should -BeOfType [DSInternals.Win32.WebAuthn.Okta.OktaFido2AuthenticationMethod]
$result0.Id | Should -Be $factorId0
$result0.FactorType | Should -Be "webauthn"
$result0.Provider | Should -Be "FIDO"
$result0.VendorName | Should -Be "FIDO"
$result0.Status | Should -Be "ACTIVE"
$result0.Created | Should -BeLessThan (Get-Date -AsUTC)
$result0.Profile.CredentialId | Should -Not -BeNullOrEmpty
$result0.Profile.CredentialId | Should -Be ([Convert]::ToBase64String($passkey0.PublicKeyCred.Id)).Replace('+', '-').Replace('/', '_').Replace('=', '')
$OktaUserId = $env:OktaUserId
[string] $credentialDeletionPath0 = "/api/v1/users/${OktaUserId}/factors/${factorId0}"
$response0 = Invoke-OktaWebRequest -Method ([Microsoft.PowerShell.Commands.WebRequestMethod]::Delete) -Path $credentialDeletionPath0
$response0.StatusCode | Should -Be 204
$response0.RawContentLength | Should -Be 0
$options1 = Get-PasskeyRegistrationOptions -UserId $env:OktaUserId
$options1 | Should -BeOfType [DSInternals.Win32.WebAuthn.Okta.OktaWebauthnCredentialCreationOptions]
$options1.PublicKeyOptions.RelyingParty.Id | Should -Be $env:OktaTenantId
$options1.PublicKeyOptions.PublicKeyCredentialParameters.Count | Should -Be 2
$factory1 = [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]::new()
$factory1 | Should -BeOfType [DSInternals.Win32.WebAuthn.Tests.PasskeyFactory]
$passkey1 = $factory1.MakePasskey($options1, 1)
$passkey1 | Should -BeOfType [DSInternals.Win32.WebAuthn.Okta.OktaWebauthnAttestationResponse]
$passkey1 | Should -Not -BeNullOrEmpty
$factorId1 = $passkey1.FactorId
$result1 = Register-Passkey -Passkey $passkey1 -UserId $env:OktaUserId
$result1 | Should -Not -BeNullOrEmpty
$result1 | Should -BeOfType [DSInternals.Win32.WebAuthn.Okta.OktaFido2AuthenticationMethod]
$result1.Id | Should -Be $factorId1
$result1.FactorType | Should -Be "webauthn"
$result1.Provider | Should -Be "FIDO"
$result1.VendorName | Should -Be "FIDO"
$result1.Status | Should -Be "ACTIVE"
$result1.Created | Should -BeLessThan (Get-Date -AsUTC)
$result1.Profile.CredentialId | Should -Not -BeNullOrEmpty
$result1.Profile.CredentialId | Should -Be ([Convert]::ToBase64String($passkey1.PublicKeyCred.Id)).Replace('+', '-').Replace('/', '_').Replace('=', '')
[string] $credentialDeletionPath1 = "/api/v1/users/${OktaUserId}/factors/${factorId1}"
$response1 = Invoke-OktaWebRequest -Method ([Microsoft.PowerShell.Commands.WebRequestMethod]::Delete) -Path $credentialDeletionPath1
$response1.StatusCode | Should -Be 204
$response1.RawContentLength | Should -Be 0
$result0.Profile.CredentialId | Should -Not -Be $result1.Profile.CredentialId
}
AfterAll {
Disconnect-Okta
}
}
@@ -65,7 +65,7 @@ RequiredModules = @('Microsoft.Graph.Authentication','Microsoft.Graph.Identity.S
# NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @('Get-PasskeyRegistrationOptions', 'New-Passkey', 'Register-Passkey')
FunctionsToExport = @('Get-PasskeyRegistrationOptions', 'New-Passkey', 'Register-Passkey','Connect-Okta','Disconnect-Okta')
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
@@ -96,7 +96,7 @@ PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('Windows', 'PSEdition_Desktop', 'PSEdition_Core', 'PSModule', 'FIDO2', 'Passkeys', 'EntraID', 'AzureAD', 'Azure', 'WebAuthn', 'CTAP', 'Security','2FA','MFA','Authentication','Identity','Passwordless')
Tags = @('Windows', 'PSEdition_Desktop', 'PSEdition_Core', 'PSModule', 'FIDO2', 'Passkeys', 'EntraID', 'AzureAD', 'Azure', 'WebAuthn', 'CTAP', 'Security','2FA','MFA','Authentication','Identity','Passwordless','Okta')
# A URL to the license for this module.
LicenseUri = 'https://github.com/MichaelGrafnetter/webauthn-interop/blob/main/LICENSE'
@@ -2,25 +2,196 @@
if ($PSVersionTable.PSVersion.Major -ge 6) {
# PowerShell Core
Add-Type -Path "$PSScriptRoot/net6.0/DSInternals.Win32.WebAuthn.dll" -ErrorAction Stop
Add-Type -Path "$PSScriptRoot/net6.0/Microsoft.Identity.Client.dll" -ErrorAction Stop
Add-Type -Path "$PSScriptRoot/net6.0/System.IdentityModel.Tokens.Jwt.dll" -ErrorAction Stop
}
else {
# PowerShell Desktop
Add-Type -Path "$PSScriptRoot/net48/DSInternals.Win32.WebAuthn.dll" -ErrorAction Stop
Add-Type -Path "$PSScriptRoot/net48/Microsoft.Identity.Client.dll" -ErrorAction Stop
Add-Type -Path "$PSScriptRoot/net48/System.IdentityModel.Tokens.Jwt.dll" -ErrorAction Stop
}
# Needed for [Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod] type
Import-Module -Name Microsoft.Graph.Identity.SignIns -ErrorAction Stop
New-Variable -Name OktaToken -Value $null -Scope Script
New-Variable -Name OktaRevocationInfo -Value $null -Scope Script
function Get-EntraIDPasskeyRegistrationOptions
{
[OutputType([DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnCredentialCreationOptions])]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({
return $_ -match "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" -or $true -eq [guid]::TryParse($_, $([ref][guid]::Empty))
})]
[Alias('User')]
[string] $UserId,
[Parameter(Mandatory = $false)]
[ValidateScript({
if ($_ -is [TimeSpan]) {
$min = New-TimeSpan -Minutes 5
$max = New-TimeSpan -Minutes 43200
return $_ -ge $min -and $_ -le $max
}
else {
throw "Parameter must be a TimeSpan object."
}
})]
[Alias('Timeout')]
[TimeSpan] $ChallengeTimeout = (New-TimeSpan -Minutes 5)
)
try {
Write-Debug "UserId ${UserId} TokenLifetimeSeconds ${ChallengeTimeout}"
# Generate the user-specific URL, e.g., https://graph.microsoft.com/beta/users/af4cf208-16e0-429d-b574-2a09c5f30dea/authentication/fido2Methods/creationOptions
[string] $credentialOptionsUrl = '/beta/users/{0}/authentication/fido2Methods/creationOptions' -f [uri]::EscapeDataString($UserId)
Write-Debug ('Credential options url: ' + $credentialOptionsUrl)
[string] $response = Invoke-MgGraphRequest -Method GET `
-Uri $credentialOptionsUrl `
-Body @{ challengeTimeoutInMinutes = $ChallengeTimeout.TotalMinutes } `
-OutputType Json
Write-Debug ('Credential options response: ' + $response)
# Parse JSON response
return [DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnCredentialCreationOptions]::Create($response)
}
catch {
throw $_
}
}
function Invoke-OktaWebRequest
{
param(
$Path,
$Query,
$Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Post,
$Body,
$ContentType = "application/json"
)
Write-Debug "Path ${Path}"
Write-Debug "Query ${Query}"
Write-Debug "Method ${Method}"
Write-Debug ('Body ' + ($Body | ConvertTo-Json))
Write-Debug "Content type ${ContentType}"
$tokenType = $Script:OktaToken.TokenType
$token = $Script:OktaToken.AccessToken
$headers = @{
"Accept" = "application/json"
"Authorization" = "${tokenType} ${token}"
}
$tenant = ([System.UriBuilder]($Script:OktaToken.AuthenticationResultMetadata.TokenEndpoint)).Host
Write-Debug "Tenant ${Tenant}"
$uriBuilder = New-Object System.UriBuilder
$uriBuilder.Scheme = "https"
$uriBuilder.Host = $tenant
$uriBuilder.Path = $Path
$uriBuilder.Query = $Query
$uri = $uriBuilder.ToString()
Write-Debug "Uri ${uri}"
return Invoke-WebRequest -Uri $uri `
-Method $Method `
-Headers $headers `
-ContentType $ContentType `
-Body $Body
}
function Get-OktaPasskeyRegistrationOptions
{
[OutputType([DSInternals.Win32.WebAuthn.Okta.OktaWebauthnCredentialCreationOptions])]
param(
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9_-]{20}$")]
[Alias('User')]
[string] $UserId,
[Parameter(Mandatory = $false)]
[ValidateScript({
if ($_ -is [TimeSpan]) {
$min = New-TimeSpan -Seconds 1
$max = New-TimeSpan -Seconds 86400
return $_ -ge $min -and $_ -le $max
}
else {
throw "Parameter must be a TimeSpan object."
}
})]
[Alias('Timeout')]
[TimeSpan] $ChallengeTimeout = (New-TimeSpan -Minutes 5)
)
begin {
if ($null -eq $Script:OktaToken)
{
throw 'Not connected to Okta, call Connnect-Okta to get started.'
}
}
process {
try
{
Write-Debug "In Get-OktaPasskeyRegistrationOptions with ${UserId} and ${ChallengeTimeout}"
$TokenLifetimeSeconds = $ChallengeTimeout.TotalSeconds
Write-Debug "TokenLifetimeSeconds ${TokenLifetimeSeconds}"
[string] $credentialOptionsPath = "/api/v1/users/${UserId}/factors"
[string] $credentialOptionsQuery = "tokenLifetimeSeconds=${TokenLifetimeSeconds}&activate=true"
Write-Debug ('Credential options path: ' + $credentialOptionsPath)
Write-Debug ('Credential options query: ' + $credentialOptionsQuery)
$body = @{
factorType = "webauthn"
provider = "FIDO"
} | ConvertTo-Json -Compress
Write-Debug ('Credential options payload: ' + $body)
[string] $response = Invoke-OktaWebRequest -Path $credentialOptionsPath `
-Query $credentialOptionsQuery `
-Body $body
Write-Debug ('Credential options response: ' + $response)
# Parse JSON response
$options = [DSInternals.Win32.WebAuthn.Okta.OktaWebauthnCredentialCreationOptions]::Create($response)
# Okta appears to omit relying party id in the options, but it is required for credential creation
# So set default to the tenant we are talking to, which is probably what the user wants anyway
if ($null -eq $options.Embedded.PublicKeyOptions.RelyingParty.Id)
{
Write-Debug ('Setting relying party id to ' + ([System.UriBuilder]($Script:OktaToken.AuthenticationResultMetadata.TokenEndpoint)).Host)
$options.Embedded.PublicKeyOptions.RelyingParty.Id = ([System.UriBuilder]($Script:OktaToken.AuthenticationResultMetadata.TokenEndpoint)).Host
}
Write-Debug ('Credential options: ' + ($options | Out-String))
return $options
}
catch
{
throw $_
}
}
}
<#
.SYNOPSIS
Retrieves creation options required to generate and register a Microsoft Entra ID-compatible passkey.
Retrieves creation options required to generate and register a Microsoft Entra ID or Okta compatible passkey.
.PARAMETER UserId
The unique identifier of user.
The unique identifier of user. For Entra ID, this is the object id (guid) or UPN. For Okta, this is the unique identifier of Okta user.
.PARAMETER ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request.
The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.
Overrides the timeout of the server-generated challenge returned in the request. For Entra ID, the default value is 5 minutes, with the accepted range being between 5 minutes and 30 days. For Okta, the default value is 300 second, with the accepted range being between 1 second and 1 day.
.EXAMPLE
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
@@ -30,58 +201,205 @@ PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com'
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (New-TimeSpan -Minutes 10)
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 -ChallengeTimeout (New-TimeSpan -Seconds 60)
.NOTES
Self-service operations aren't supported.
More info at https://learn.microsoft.com/en-us/graph/api/fido2authenticationmethod-creationoptions
Self-service operations aren't supported for Entra ID.
More info about Entra ID at https://learn.microsoft.com/en-us/graph/api/fido2authenticationmethod-creationoptions
More info about Okta at https://developer.okta.com/docs/api/openapi/okta-management/management/tag/UserFactor/#tag/UserFactor/operation/enrollFactor
#>
function Get-PasskeyRegistrationOptions
{
[CmdletBinding()]
[OutputType([DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnCredentialCreationOptions])]
[OutputType([DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOptions])]
param(
[Parameter(Mandatory = $true)]
[Alias('User')]
[ValidateScript({
if ($_ -match "^[A-Za-z0-9_-]{20}$" -or $true -eq [guid]::TryParse($_, $([ref][guid]::Empty)) -or $_ -match "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
{return $true}
return $false
})]
[string] $UserId,
[Parameter(Mandatory = $false)]
[Alias('Timeout')]
[timespan] $ChallengeTimeout = (New-TimeSpan -Minutes 5)
)
try {
# Generate the user-specific URL, e.g., https://graph.microsoft.com/beta/users/af4cf208-16e0-429d-b574-2a09c5f30dea/authentication/fido2Methods/creationOptions
[string] $credentialOptionsUrl = '/beta/users/{0}/authentication/fido2Methods/creationOptions' -f [uri]::EscapeDataString($UserId)
[string] $response = Invoke-MgGraphRequest -Method GET `
-Uri $credentialOptionsUrl `
-Body @{ challengeTimeoutInMinutes = $ChallengeTimeout.TotalMinutes } `
-OutputType Json
# Parse JSON response
return [DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnCredentialCreationOptions]::Create($response)
begin {
Write-Debug "In Get-PasskeyRegistrationOptions with ${UserId} and ${ChallengeTimeout}"
$IsEntraID = ($UserId -match "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" -or $true -eq [guid]::TryParse($UserId, $([ref][guid]::Empty)))
Write-Debug "IsEntraID: ${IsEntraId}"
if ($IsEntraID)
{
$min = New-TimeSpan -Minutes 5
$max = New-TimeSpan -Minutes 43200
if ($ChallengeTimeout -gt $max -or $ChallengeTimeout -lt $min) {
Write-Error "Cannot validate argument on parameter 'ChallengeTimeout' which must be a valid TimeSpan between 5 and 43200 minutes for $_." -ErrorAction Stop
}
}
else
{
$min = New-TimeSpan -Seconds 1
$max = New-TimeSpan -Seconds 86400
if ($ChallengeTimeout -gt $max -or $ChallengeTimeout -lt $min) {
Write-Error "Cannot validate argument on parameter 'ChallengeTimeout' which must be a valid TimeSpan between 1 and 86400 seconds for $_." -ErrorAction Stop
}
if ($UserId -notmatch "^[A-Za-z0-9_-]{20}$") {
Write-Error "Cannot validate argument on parameter 'UserID' which must the unique idenitier for the user for Okta." -ErrorAction Stop
}
if ($null -eq $Script:OktaToken) {
throw 'Not connected to Okta, call Connnect-Okta to get started.'
}
}
}
catch {
# TODO: PS Error Record ($PSCmdlet.ThrowTerminatingError())
throw
process {
$Options = $null
try {
if ($IsEntraID) {
Write-Debug "Calling Get-EntraIDPasskeyRegistrationOptions with ${UserId} and ${ChallengeTimeout}"
$Options = Get-EntraIDPasskeyRegistrationOptions -UserId $UserId -ChallengeTimeout $ChallengeTimeout
}
else {
Write-Debug "Calling Get-OktaPasskeyRegistrationOptions with ${UserId} and ${ChallengeTimeout}"
$Options = Get-OktaPasskeyRegistrationOptions -UserId $UserId -ChallengeTimeout $ChallengeTimeout
}
return $Options
}
catch {
$errorRecord = New-Object Management.Automation.ErrorRecord(
$_.Exception,
$_.Exception.Message,
[Management.Automation.ErrorCategory]::InvalidArgument,
$Options
)
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
}
}
function Register-EntraIDPasskey
{
[OutputType([Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod])]
param(
[Parameter(Mandatory = $true)]
[ValidateScript({
return $_ -match "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" -or $true -eq [guid]::TryParse($_, $([ref][guid]::Empty))
})]
[Alias('User')]
[string] $UserId,
[ValidateScript({
if ([string]::IsNullOrEmpty($_.DisplayName))
{
throw "Passkey 'DisplayName' field may not be null or empty."
}
return $true
})]
[DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnAttestationResponse]
$Passkey
)
try
{
# Generate the user-specific URL, e.g., https://graph.microsoft.com/beta/users/af4cf208-16e0-429d-b574-2a09c5f30dea/authentication/fido2Methods
[string] $registrationUrl = '/beta/users/{0}/authentication/fido2Methods' -f [uri]::EscapeDataString($UserId)
Write-Debug ('Registration URL: ' + $registrationUrl)
[string] $response = Invoke-MgGraphRequest `
-Method POST `
-Uri $registrationUrl `
-OutputType Json `
-ContentType 'application/json' `
-Body $Passkey.ToString()
Write-Debug ('Registration response: ' + $response)
return [Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod]::FromJsonString($response)
}
catch
{
throw $_
}
}
function Register-OktaPasskey
{
[CmdletBinding(DefaultParameterSetName = 'New')]
[OutputType([DSInternals.Win32.WebAuthn.Okta.OktaFido2AuthenticationMethod])]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'Existing')]
[Parameter(Mandatory = $true, ParameterSetName = 'New')]
[Alias('User')]
[string] $UserId,
[Parameter(Mandatory = $true, ParameterSetName = 'Existing', ValueFromPipeline = $true)]
[DSInternals.Win32.WebAuthn.Okta.OktaWebauthnAttestationResponse]
$Passkey,
[Parameter(Mandatory = $false, ParameterSetName = 'New')]
[ValidateScript({
if ($_ -is [TimeSpan]) {
$min = New-TimeSpan -Seconds 1
$max = New-TimeSpan -Seconds 86400
return $_ -ge $min -and $_ -le $max
}
else {
throw "Parameter must be a TimeSpan object."
}
})]
[Alias('Timeout')]
[timespan] $ChallengeTimeout = (New-TimeSpan -Minutes 5)
)
try
{
if ($null -eq $Script:OktaToken)
{
throw 'Not connected to Okta, call Connnect-Okta to get started.'
}
$userId = $Passkey.UserId
$factorId = $Passkey.FactorId
[string] $registrationPath = "/api/v1/users/${userId}/factors/${factorId}/lifecycle/activate"
Write-Debug ('Registration path: ' + $registrationPath)
[string] $response = Invoke-OktaWebRequest -Path $registrationPath `
-Body $Passkey.ToString()
Write-Debug ('Registration response: ' + $response)
return [DSInternals.Win32.WebAuthn.Okta.OktaFido2AuthenticationMethod]::FromJsonString($response)
}
catch
{
throw $_
}
}
<#
.SYNOPSIS
Registers a new passkey in Microsoft Entra ID.
Registers a new passkey in Microsoft Entra ID, or Okta.
.PARAMETER UserId
The unique identifier of user.
The unique identifier of user. For Entra ID, this is the object id (guid) or UPN. For Okta, this is the unique identifier of Okta user.
.PARAMETER ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request. For Entra ID, the default value is 5 minutes, with the accepted range being between 5 minutes and 30 days. For Okta, the default value is 300 second, with the accepted range being between 1 second and 1 day.
.PARAMETER Passkey
The passkey to be registered.
.PARAMETER DisplayName
Custom name given to the registered passkey.
.PARAMETER ChallengeTimeout
Overrides the timeout of the server-generated challenge returned in the request.
The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.
Custom name given to the Entra ID registered passkey.
.EXAMPLE
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
@@ -95,54 +413,74 @@ PS \> Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Register-Passkey -UserId 00eDuihq64pgP1gVD0x7
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 | New-Passkey | Register-Passkey
.NOTES
More info at https://learn.microsoft.com/en-us/graph/api/authentication-post-fido2methods
More info for Entra ID at https://learn.microsoft.com/en-us/graph/api/authentication-post-fido2methods
More info for Okta at https://developer.okta.com/docs/api/openapi/okta-management/management/tag/UserFactor/#tag/UserFactor/operation/activateFactor
#>
function Register-Passkey
{
[CmdletBinding(DefaultParameterSetName = 'New')]
[OutputType([Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod])]
[OutputType([DSInternals.Win32.WebAuthn.Okta.OktaFido2AuthenticationMethod], ParameterSetName = 'OktaNew')]
[OutputType([Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod], ParameterSetName = 'EntraIDNew')]
param(
[Parameter(Mandatory = $true, ParameterSetName = 'EntraIDNew')]
[Parameter(Mandatory = $true, ParameterSetName = 'Existing')]
[Parameter(Mandatory = $true, ParameterSetName = 'New')]
[Parameter(Mandatory = $true, ParameterSetName = 'OktaNew')]
[Alias('User')]
[string] $UserId,
[Parameter(Mandatory = $true, ParameterSetName = 'Existing', ValueFromPipeline = $true)]
[DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnAttestationResponse]
[DSInternals.Win32.WebAuthn.WebauthnAttestationResponse]
$Passkey,
[Parameter(Mandatory = $true, ParameterSetName = 'New')]
[Parameter(Mandatory = $true, ParameterSetName = 'EntraIDNew')]
[string] $DisplayName,
[Parameter(Mandatory = $false, ParameterSetName = 'New')]
[Parameter(Mandatory = $false, ParameterSetName = 'EntraIDNew')]
[Parameter(Mandatory = $false, ParameterSetName = 'OktaNew')]
[Alias('Timeout')]
[timespan] $ChallengeTimeout = (New-TimeSpan -Minutes 5)
)
process
{
# TODO: Write-Error
begin {
if ($null -ne $Passkey -and $Passkey.GetType() -eq ([DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnAttestationResponse]) -and [string]::IsNullOrEmpty($Passkey.displayName)) {
throw "Parameter 'DisplayName' may not be null or empty."
}
}
process {
switch ($PSCmdlet.ParameterSetName) {
'Existing' {
# Generate the user-specific URL, e.g., https://graph.microsoft.com/beta/users/af4cf208-16e0-429d-b574-2a09c5f30dea/authentication/fido2Methods
[string] $registrationUrl = '/beta/users/{0}/authentication/fido2Methods' -f [uri]::EscapeDataString($UserId)
switch ($Passkey.GetType())
{
([DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnAttestationResponse])
{
return Register-EntraIDPasskey -UserId $UserId -Passkey $Passkey
}
[string] $response = Invoke-MgGraphRequest `
-Method POST `
-Uri $registrationUrl `
-OutputType Json `
-ContentType 'application/json' `
-Body $Passkey.ToString()
return [Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod]::FromJsonString($response)
([DSInternals.Win32.WebAuthn.Okta.OktaWebauthnAttestationResponse])
{
if ($null -eq $Script:OktaToken) {
throw 'Not connected to Okta, call Connnect-Okta to get started.'
}
return Register-OktaPasskey -UserId $UserId -Passkey $Passkey
}
}
}
'New' {
[DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnCredentialCreationOptions] $registrationOptions =
Get-PasskeyRegistrationOptions -UserId $UserId -ChallengeTimeout $ChallengeTimeout
default {
[DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOptions] $registrationOptions =
Get-PasskeyRegistrationOptions -UserId $UserId -ChallengeTimeout $ChallengeTimeout
[DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnAttestationResponse] $passkey =
New-Passkey -Options $registrationOptions -DisplayName $DisplayName
[DSInternals.Win32.WebAuthn.WebauthnAttestationResponse] $passkey =
New-Passkey -Options $registrationOptions -DisplayName $DisplayName
# Recursive call with the 'Existing' parameter set
return Register-Passkey -UserId $UserId -Passkey $passkey
@@ -153,47 +491,267 @@ function Register-Passkey
<#
.SYNOPSIS
Creates a new Microsoft Entra ID-compatible passkey.
Creates a new Microsoft Entra ID or Okta compatible passkey.
.PARAMETER Options
Options required to generate a Microsoft Entra ID-compatible passkey.
Options required to generate a Microsoft Entra ID or Okta compatible passkey.
.PARAMETER DisplayName
Custom name given to the registered passkey.
Custom name given to the Entra ID registered passkey.
.EXAMPLE
PS \> Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \> Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
.EXAMPLE
PS \> New-Passkey -Options $options
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
PS \> Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 | New-Passkey
.INPUTS
DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOptions
#>
function New-Passkey
{
[OutputType([DSInternals.Win32.WebAuthn.WebauthnAttestationResponse])]
[CmdletBinding()]
[OutputType([DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnAttestationResponse])]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnCredentialCreationOptions]
[DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOptions]
$Options,
[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $false)]
[ValidateLength(1, 30)]
[string] $DisplayName
)
process
{
try {
[DSInternals.Win32.WebAuthn.WebAuthnApi] $api = [DSInternals.Win32.WebAuthn.WebAuthnApi]::new()
[DSInternals.Win32.WebAuthn.PublicKeyCredential] $credential = $api.AuthenticatorMakeCredential($Options.PublicKeyOptions)
return [DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnAttestationResponse]::new($credential, $DisplayName)
try {
[DSInternals.Win32.WebAuthn.WebAuthnApi] $api = [DSInternals.Win32.WebAuthn.WebAuthnApi]::new()
[DSInternals.Win32.WebAuthn.PublicKeyCredential] $credential = $api.AuthenticatorMakeCredential($Options.PublicKeyOptions)
switch ($Options.GetType())
{
([DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnCredentialCreationOptions])
{
if ([string]::IsNullOrEmpty($DisplayName)) {
throw "Parameter 'DisplayName' may not be null or empty."
}
return [DSInternals.Win32.WebAuthn.EntraID.MicrosoftGraphWebauthnAttestationResponse]::new($credential, $DisplayName)
}
([DSInternals.Win32.WebAuthn.Okta.OktaWebauthnCredentialCreationOptions])
{
return [DSInternals.Win32.WebAuthn.Okta.OktaWebauthnAttestationResponse]::new($credential, $Options.PublicKeyOptions.User.Id, $Options.Id)
}
}
catch {
# TODO: PS Error Record (Write-Error)
throw
}
catch {
$errorRecord = New-Object Management.Automation.ErrorRecord(
$_,
$_.Message,
[Management.Automation.ErrorCategory]::InvalidArgument,
$Options
)
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
}
<#
.SYNOPSIS
Retrieves the Microsoft Graph endpoint URL.
.NOTES
Dynamic URL retrieval is used to support Azure environments, like Azure Public, Azure Government, or Azure China.
#>
function Get-MgGraphEndpoint
{
[CmdletBinding()]
[OutputType([string])]
param()
try {
[Microsoft.Graph.PowerShell.Authentication.AuthContext] $context = Get-MgContext -ErrorAction Stop
if($null -ne $context) {
return (Get-MgEnvironment -Name $context.Environment -ErrorAction Stop).GraphEndpoint
}
}
catch {
$errorRecord = New-Object Management.Automation.ErrorRecord(
(New-Object Exception('Not connected to Microsoft Graph.')),
'Not connected to Microsoft Graph.',
[Management.Automation.ErrorCategory]::ConnectionError,
$context
)
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
}
<#
.SYNOPSIS
Retrieves an access token to interact with Okta APIs.
.PARAMETER Tenant
The unique identifier of Okta tenant, like 'example.okta.com'.
.PARAMETER ClientId
The client id of the Okta application used to obtain an access token.
.PARAMETER Scopes
Scopes to request for the access token. Defaults to 'okta.users.manage'.
.PARAMETER JsonWebKey
The JSON Web Key used to authenticate to the Okta application, in order to obtain access token using the client credentials OAuth flow.
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7
.EXAMPLE
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7 -Scopes @('okta.users.manage','okta.something.else')
.EXAMPLE
PS \> $jwk = '{"kty":"RSA","kid":"EE3QB0WvhuOwR9DuR6717OERKbDrBemrDKOK4Xvbf8c","d":"TmljZSB0cnkhICBCdXQgdGhpcyBpc...'
PS \> Connect-Okta -Tenant example.okta.com -ClientId 0oakmj8hvxvtvCy3P5d7 -Scopes @('okta.users.manage','okta.something.else') -JsonWebKey $jwk
#>
function Connect-Okta
{
param(
[Parameter(Mandatory = $true, ParameterSetName = 'ClientCredentials')]
[Parameter(Mandatory = $true, ParameterSetName = 'AuthorizationCode')]
[ValidatePattern('^[a-zA-Z0-9-]+\.okta(?:-emea|preview|\.mil)?\.com$')]
[string] $Tenant,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ClientCredentials')]
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'AuthorizationCode')]
[ValidatePattern('^[A-Za-z0-9_-]{20}$')]
[string]
$ClientId,
[Parameter(Mandatory = $false, ParameterSetName = 'ClientCredentials')]
[Parameter(Mandatory = $false, ParameterSetName = 'AuthorizationCode')]
[string[]] $Scopes = @('okta.users.manage'),
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ClientCredentials')]
[Alias('jwk')]
[string]
$JsonWebKey
)
try {
$Script:OktaRevocationInfo = [PSCustomObject] @{
ClientId = $ClientId
RevocationToken = $null
}
switch ($PSCmdlet.ParameterSetName){
'AuthorizationCode'
{
Write-Debug "No JWK found, assuming public client intended"
$publicClientApp = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($ClientId).
WithExperimentalFeatures().
WithOidcAuthority("https://${tenant}/").
WithRedirectUri("http://localhost:8080/login/callback").
Build()
$Script:OktaToken = $publicClientApp.AcquireTokenInteractive($Scopes).ExecuteAsync().GetAwaiter().GetResult()
if ($null -ne $Script:OktaToken)
{
Write-Host 'Okta access token successfully retrieved.'
}
}
'ClientCredentials'
{
Write-Debug "JWK found, assuming confidential client intended"
$jwk = [Microsoft.IdentityModel.Tokens.JsonWebKey]::new($JsonWebKey)
$signingCredentials = [Microsoft.IdentityModel.Tokens.SigningCredentials]::new($jwk,'RS256')
$issuer = $ClientId
$audience = "https://${tenant}/oauth2/v1/token"
$subject = [System.Security.Claims.ClaimsIdentity]::new()
$subject.Claims.Add([System.Security.Claims.Claim]::new('sub',$ClientId))
$notBefore = (Get-Date)
$expires = (Get-Date).AddMinutes(60)
$issuedAt = $notBefore
$tokenHandler = [System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler]::new()
$securityToken = $tokenHandler.CreateJwtSecurityToken($issuer, $audience, $subject, $notBefore, $expires, $issuedAt, $signingCredentials)
$revocationToken = $tokenHandler.CreateJwtSecurityToken($issuer, "https://${tenant}/oauth2/v1/revoke", $subject, $notBefore, $expires, $issuedAt, $signingCredentials)
$assertion = $tokenHandler.WriteToken($securityToken)
$Script:OktaRevocationInfo.RevocationToken = $tokenHandler.WriteToken($revocationToken)
$confidentialClientApp = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($ClientId).
WithClientAssertion($assertion).
WithOidcAuthority("https://${tenant}").
Build()
$Script:OktaToken = $confidentialClientApp.AcquireTokenForClient($Scopes).ExecuteAsync().GetAwaiter().GetResult()
if ($null -ne $Script:OktaToken -and $null -ne $Script:OktaRevocationInfo.RevocationToken)
{
Write-Host 'Okta access and revocation tokens successfully retrieved.'
}
}
}
}
catch {
throw
}
}
<#
.SYNOPSIS
Revokes Okta access token.
.EXAMPLE
PS \> Disconnect-Okta
.DESCRIPTION
Revokes the Okta access token cached from the call to `Connect-Okta`.
.LINK
https://developer.okta.com/docs/guides/revoke-tokens/main/
#>
function Disconnect-Okta
{
if ($null -ne $Script:OktaToken)
{
[string] $revocationPath = "/oauth2/v1/revoke"
Write-Debug ('Revocation path: ' + $revocationPath)
$body = @{
client_id = $Script:OktaRevocationInfo.ClientId
token = $Script:OktaToken.AccessToken
token_type_hint = "access_token"
}
if ($null -ne $Script:OktaRevocationInfo.RevocationToken)
{
$body.Add('client_assertion_type',"urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
$body.Add('client_assertion',$Script:OktaRevocationInfo.RevocationToken)
}
Write-Debug ('Revocation payload: ' + ($body | ConvertTo-Json))
[string] $response = Invoke-OktaWebRequest -Uri $revocationPath `
-ContentType "application/x-www-form-urlencoded" `
-Body $body
$Script:OktaToken = $null
$Script:OktaRevocationInfo.RevocationToken = $null
if ($response.Length -eq 0 -and $null -eq $Script:OktaToken)
{
Write-Host 'Okta access token successfully revoked.'
}
}
}
New-Alias -Name Register-MgUserAuthenticationFido2Method -Value Register-Passkey
Export-ModuleMember -Function 'Get-PasskeyRegistrationOptions','New-Passkey','Register-Passkey' `
Export-ModuleMember -Function 'Get-PasskeyRegistrationOptions','New-Passkey','Register-Passkey','Connect-Okta','Disconnect-Okta','Invoke-OktaWebRequest' `
-Alias 'Register-MgUserAuthenticationFido2Method'
@@ -2,11 +2,11 @@
<helpItems schema="maml" xmlns="http://msh">
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
<command:details>
<command:name>Get-PasskeyRegistrationOptions</command:name>
<command:verb>Get</command:verb>
<command:noun>PasskeyRegistrationOptions</command:noun>
<command:name>Connect-Okta</command:name>
<command:verb>Connect</command:verb>
<command:noun>Okta</command:noun>
<maml:description>
<maml:para>Retrieves creation options required to generate and register a Microsoft Entra ID-compatible passkey.</maml:para>
<maml:para>{{ Fill in the Synopsis }}</maml:para>
</maml:description>
</command:details>
<maml:description>
@@ -14,11 +14,11 @@
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Get-PasskeyRegistrationOptions</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
<maml:name>UserId</maml:name>
<maml:name>Connect-Okta</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="named" aliases="none">
<maml:name>ClientId</maml:name>
<maml:description>
<maml:para>The unique identifier of user.</maml:para>
<maml:para>{{ Fill ClientId Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
@@ -27,10 +27,167 @@
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="named" aliases="jwk">
<maml:name>JsonWebKey</maml:name>
<maml:description>
<maml:para>{{ Fill JsonWebKey Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ValidateNotNullOrEmptyAttribute</command:parameterValue>
<dev:type>
<maml:name>ValidateNotNullOrEmptyAttribute</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Scopes</maml:name>
<maml:description>
<maml:para>{{ Fill Scopes Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Tenant</maml:name>
<maml:description>
<maml:para>{{ Fill Tenant Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="named" aliases="none">
<maml:name>ClientId</maml:name>
<maml:description>
<maml:para>{{ Fill ClientId Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="named" aliases="jwk">
<maml:name>JsonWebKey</maml:name>
<maml:description>
<maml:para>{{ Fill JsonWebKey Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ValidateNotNullOrEmptyAttribute</command:parameterValue>
<dev:type>
<maml:name>ValidateNotNullOrEmptyAttribute</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Scopes</maml:name>
<maml:description>
<maml:para>{{ Fill Scopes Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>Tenant</maml:name>
<maml:description>
<maml:para>{{ Fill Tenant Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:parameters>
<command:inputTypes />
<command:returnValues />
<maml:alertSet>
<maml:alert>
<maml:para></maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
<command:example>
<maml:title>-------------------------- Example 1 --------------------------</maml:title>
<dev:code>PS C:\&gt; {{ Add example code here }}</dev:code>
<dev:remarks>
<maml:para>{{ Add example description here }}</maml:para>
</dev:remarks>
</command:example>
</command:examples>
<command:relatedLinks />
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
<command:details>
<command:name>Get-PasskeyRegistrationOptions</command:name>
<command:verb>Get</command:verb>
<command:noun>PasskeyRegistrationOptions</command:noun>
<maml:description>
<maml:para>Retrieves creation options required to generate and register a Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
</command:details>
<maml:description>
<maml:para></maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Get-PasskeyRegistrationOptions</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="User">
<maml:name>UserId</maml:name>
<maml:description>
<maml:para>The unique identifier of user. For Entra ID, this is the object id (GUID) or UPN. For Okta, this is the unique identifier of Okta user.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="Timeout">
<maml:name>ChallengeTimeout</maml:name>
<maml:description>
<maml:para>Overrides the timeout of the server-generated challenge returned in the request. The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.</maml:para>
<maml:para>Overrides the timeout of the server-generated challenge returned in the request. For Entra ID, the default value is 5 minutes, with the accepted range being between 5 minutes and 30 days. For Okta, the default value is 300 seconds, with the accepted range being between 1 second and 1 day.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">TimeSpan</command:parameterValue>
<dev:type>
@@ -39,13 +196,25 @@
</dev:type>
<dev:defaultValue>(New-TimeSpan -Minutes 5)</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="Timeout">
<maml:name>ChallengeTimeout</maml:name>
<maml:description>
<maml:para>Overrides the timeout of the server-generated challenge returned in the request. The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.</maml:para>
<maml:para>Overrides the timeout of the server-generated challenge returned in the request. For Entra ID, the default value is 5 minutes, with the accepted range being between 5 minutes and 30 days. For Okta, the default value is 300 seconds, with the accepted range being between 1 second and 1 day.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">TimeSpan</command:parameterValue>
<dev:type>
@@ -54,10 +223,22 @@
</dev:type>
<dev:defaultValue>(New-TimeSpan -Minutes 5)</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="User">
<maml:name>UserId</maml:name>
<maml:description>
<maml:para>The unique identifier of user.</maml:para>
<maml:para>The unique identifier of user. For Entra ID, this is the object id (GUID) or UPN. For Okta, this is the unique identifier of Okta user.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
@@ -71,7 +252,7 @@
<command:returnValues>
<command:returnValue>
<dev:type>
<maml:name>DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnCredentialCreationOptions</maml:name>
<maml:name>DSInternals.Win32.WebAuthn.WebauthnCredentialCreationOptions</maml:name>
</dev:type>
<maml:description>
<maml:para></maml:para>
@@ -80,22 +261,36 @@
</command:returnValues>
<maml:alertSet>
<maml:alert>
<maml:para>Self-service operations aren't supported. More info at https://learn.microsoft.com/en-us/graph/api/fido2authenticationmethod-creationoptions</maml:para>
<maml:para>Self-service operations aren't supported. For the Okta token, you should not use SSWS but instead use a bearer token.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
<command:example>
<maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
<dev:code>Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com'</dev:code>
<maml:title>--------------------- EXAMPLE 1 (Entra ID) ---------------------</maml:title>
<dev:code>PS \&gt; Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \&gt; Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com'</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
<dev:code>Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (New-TimeSpan -Minutes 10)</dev:code>
<maml:title>--------------------- EXAMPLE 2 (Entra ID) ---------------------</maml:title>
<dev:code>PS \&gt; Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \&gt; Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (New-TimeSpan -Minutes 10)</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>----------------------- EXAMPLE 3 (Okta) -----------------------</maml:title>
<dev:code>PS \&gt; Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 -Tenant example.okta.com -Token your_okta_token</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>----------------------- EXAMPLE 4 (Okta) -----------------------</maml:title>
<dev:code>PS \&gt; Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 -ChallengeTimeout (New-TimeSpan -Seconds 60) -Tenant example.okta.com -Token your_okta_token</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
@@ -106,6 +301,14 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
<maml:linkText>Online Version:</maml:linkText>
<maml:uri>https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/Get-PasskeyRegistrationOptions.md</maml:uri>
</maml:navigationLink>
<maml:navigationLink>
<maml:linkText>More info about Entra ID</maml:linkText>
<maml:uri>https://learn.microsoft.com/en-us/graph/api/fido2authenticationmethod-creationoptions</maml:uri>
</maml:navigationLink>
<maml:navigationLink>
<maml:linkText>More info about Okta</maml:linkText>
<maml:uri>https://developer.okta.com/docs/api/openapi/okta-management/management/tag/UserFactor/#tag/UserFactor/operation/enrollFactor</maml:uri>
</maml:navigationLink>
</command:relatedLinks>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
@@ -114,11 +317,11 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
<command:verb>New</command:verb>
<command:noun>Passkey</command:noun>
<maml:description>
<maml:para>Creates a new Microsoft Entra ID-compatible passkey.</maml:para>
<maml:para>Creates a new Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
</command:details>
<maml:description>
<maml:para>{{ Fill in the Description }}</maml:para>
<maml:para>Takes the MicrosoftGraphWebauthnCredentialCreationOptions or OktaWebauthnCredentialCreationOptions object from Get-PasskeyRegistrationOptions and uses them to create a credential using the system dialogs.</maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
@@ -126,19 +329,19 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="1" aliases="none">
<maml:name>Options</maml:name>
<maml:description>
<maml:para>Options required to generate a Microsoft Entra ID-compatible passkey.</maml:para>
<maml:para>Options required to generate a Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">MicrosoftGraphWebauthnCredentialCreationOptions</command:parameterValue>
<command:parameterValue required="true" variableLength="false">WebauthnCredentialCreationOptions</command:parameterValue>
<dev:type>
<maml:name>MicrosoftGraphWebauthnCredentialCreationOptions</maml:name>
<maml:name>WebauthnCredentialCreationOptions</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<maml:name>DisplayName</maml:name>
<maml:description>
<maml:para>Custom name given to the registered passkey.</maml:para>
<maml:para>Custom name given to the Entra ID registered passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
@@ -147,13 +350,25 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<maml:name>DisplayName</maml:name>
<maml:description>
<maml:para>Custom name given to the registered passkey.</maml:para>
<maml:para>Custom name given to the Entra ID registered passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
@@ -165,11 +380,23 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="1" aliases="none">
<maml:name>Options</maml:name>
<maml:description>
<maml:para>Options required to generate a Microsoft Entra ID-compatible passkey.</maml:para>
<maml:para>Options required to generate a Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">MicrosoftGraphWebauthnCredentialCreationOptions</command:parameterValue>
<command:parameterValue required="true" variableLength="false">WebauthnCredentialCreationOptions</command:parameterValue>
<dev:type>
<maml:name>MicrosoftGraphWebauthnCredentialCreationOptions</maml:name>
<maml:name>WebauthnCredentialCreationOptions</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
@@ -179,7 +406,7 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
<command:returnValues>
<command:returnValue>
<dev:type>
<maml:name>DSInternals.Win32.WebAuthn.MicrosoftGraphWebauthnAttestationResponse</maml:name>
<maml:name>DSInternals.Win32.WebAuthn.WebauthnAttestationResponse</maml:name>
</dev:type>
<maml:description>
<maml:para></maml:para>
@@ -193,9 +420,23 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' -ChallengeTimeout (N
</maml:alertSet>
<command:examples>
<command:example>
<maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
<dev:code>Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'
Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
<maml:title>--------------------- EXAMPLE 1 (Entra ID) ---------------------</maml:title>
<dev:code>PS \&gt; Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \&gt; Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>----------------------- EXAMPLE 2 (Okta) -----------------------</maml:title>
<dev:code>PS \&gt; New-Passkey -Options $options</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>----------------------- EXAMPLE 3 (Okta) -----------------------</maml:title>
<dev:code>PS \&gt; Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 -Tenant example.okta.com -Token your_okta_token | New-Passkey</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
@@ -206,39 +447,43 @@ Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
<maml:linkText>Online Version:</maml:linkText>
<maml:uri>https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/New-Passkey.md</maml:uri>
</maml:navigationLink>
<maml:navigationLink>
<maml:linkText>Microsoft WebAuthn portal</maml:linkText>
<maml:uri>https://learn.microsoft.com/en-us/windows/win32/webauthn/-webauthn-portal</maml:uri>
</maml:navigationLink>
</command:relatedLinks>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
<command:details>
<command:name>Register-Passkey</command:name>
<command:verb>Register</command:verb>
<command:name>New-Passkey</command:name>
<command:verb>New</command:verb>
<command:noun>Passkey</command:noun>
<maml:description>
<maml:para>Registers a new passkey in Microsoft Entra ID.</maml:para>
<maml:para>Creates a new Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
</command:details>
<maml:description>
<maml:para>{{ Fill in the Description }}</maml:para>
<maml:para>Takes the MicrosoftGraphWebauthnCredentialCreationOptions or OktaWebauthnCredentialCreationOptions object from Get-PasskeyRegistrationOptions and uses them to create a credential using the system dialogs.</maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Register-Passkey</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ChallengeTimeout</maml:name>
<maml:name>New-Passkey</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="1" aliases="none">
<maml:name>Options</maml:name>
<maml:description>
<maml:para>Overrides the timeout of the server-generated challenge returned in the request. The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.</maml:para>
<maml:para>Options required to generate a Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">TimeSpan</command:parameterValue>
<command:parameterValue required="true" variableLength="false">WebauthnCredentialCreationOptions</command:parameterValue>
<dev:type>
<maml:name>TimeSpan</maml:name>
<maml:name>WebauthnCredentialCreationOptions</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>(New-TimeSpan -Minutes 5)</dev:defaultValue>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<maml:name>DisplayName</maml:name>
<maml:description>
<maml:para>Custom name given to the registered passkey.</maml:para>
<maml:para>Custom name given to the Entra ID registered passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
@@ -247,41 +492,14 @@ Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>UserId</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>The unique identifier of user.</maml:para>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
</command:syntaxItem>
<command:syntaxItem>
<maml:name>Register-Passkey</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="named" aliases="none">
<maml:name>Passkey</maml:name>
<maml:description>
<maml:para>The passkey to be registered.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">MicrosoftGraphWebauthnAttestationResponse</command:parameterValue>
<dev:type>
<maml:name>MicrosoftGraphWebauthnAttestationResponse</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>UserId</maml:name>
<maml:description>
<maml:para>The unique identifier of user.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
@@ -289,22 +507,10 @@ Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ChallengeTimeout</maml:name>
<maml:description>
<maml:para>Overrides the timeout of the server-generated challenge returned in the request. The default value is 5 minutes, with the accepted range being between 5 minutes and 30 days.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">TimeSpan</command:parameterValue>
<dev:type>
<maml:name>TimeSpan</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>(New-TimeSpan -Minutes 5)</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<maml:name>DisplayName</maml:name>
<maml:description>
<maml:para>Custom name given to the registered passkey.</maml:para>
<maml:para>Custom name given to the Entra ID registered passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type>
@@ -313,26 +519,26 @@ Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="named" aliases="none">
<maml:name>Passkey</maml:name>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="1" aliases="none">
<maml:name>Options</maml:name>
<maml:description>
<maml:para>The passkey to be registered.</maml:para>
<maml:para>Options required to generate a Microsoft Entra ID or Okta compatible passkey.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">MicrosoftGraphWebauthnAttestationResponse</command:parameterValue>
<command:parameterValue required="true" variableLength="false">WebauthnCredentialCreationOptions</command:parameterValue>
<dev:type>
<maml:name>MicrosoftGraphWebauthnAttestationResponse</maml:name>
<maml:name>WebauthnCredentialCreationOptions</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>UserId</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="proga">
<maml:name>ProgressAction</maml:name>
<maml:description>
<maml:para>The unique identifier of user.</maml:para>
<maml:para>{{ Fill ProgressAction Description }}</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<command:parameterValue required="true" variableLength="false">ActionPreference</command:parameterValue>
<dev:type>
<maml:name>String</maml:name>
<maml:name>ActionPreference</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
@@ -342,7 +548,7 @@ Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
<command:returnValues>
<command:returnValue>
<dev:type>
<maml:name>Microsoft.Graph.PowerShell.Models.MicrosoftGraphFido2AuthenticationMethod</maml:name>
<maml:name>DSInternals.Win32.WebAuthn.WebauthnAttestationResponse</maml:name>
</dev:type>
<maml:description>
<maml:para></maml:para>
@@ -351,30 +557,28 @@ Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'</dev:code>
</command:returnValues>
<maml:alertSet>
<maml:alert>
<maml:para>More info at https://learn.microsoft.com/en-us/graph/api/authentication-post-fido2methods</maml:para>
<maml:para></maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
<command:example>
<maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
<dev:code>Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano'</dev:code>
<maml:title>--------------------- EXAMPLE 1 (Entra ID) ---------------------</maml:title>
<dev:code>PS \&gt; Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
PS \&gt; Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
<dev:code>Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Register-Passkey -UserId 'AdeleV@contoso.com' -DisplayName 'YubiKey 5 Nano' -ChallengeTimeout (New-TimeSpan -Minutes 10)</dev:code>
<maml:title>----------------------- EXAMPLE 2 (Okta) -----------------------</maml:title>
<dev:code>PS \&gt; New-Passkey -Options $options</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>-------------------------- EXAMPLE 3 --------------------------</maml:title>
<dev:code>Connect-MgGraph -Scopes 'UserAuthenticationMethod.ReadWrite.All'
Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -DisplayName 'YubiKey 5 Nano' | Register-Passkey -UserId 'AdeleV@contoso.com'</dev:code>
<maml:title>----------------------- EXAMPLE 3 (Okta) -----------------------</maml:title>
<dev:code>PS \&gt; Get-PasskeyRegistrationOptions -UserId 00eDuihq64pgP1gVD0x7 -Tenant example.okta.com -Token your_okta_token | New-Passkey</dev:code>
<dev:remarks>
<maml:para></maml:para>
</dev:remarks>
@@ -383,7 +587,11 @@ Get-PasskeyRegistrationOptions -UserId 'AdeleV@contoso.com' | New-Passkey -Displ
<command:relatedLinks>
<maml:navigationLink>
<maml:linkText>Online Version:</maml:linkText>
<maml:uri>https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/Register-Passkey.md</maml:uri>
<maml:uri>https://github.com/MichaelGrafnetter/webauthn-interop/tree/main/Documentation/PowerShell/New-Passkey.md</maml:uri>
</maml:navigationLink>
<maml:navigationLink>
<maml:linkText>Microsoft WebAuthn portal</maml:linkText>
<maml:uri>https://learn.microsoft.com/en-us/windows/win32/webauthn/-webauthn-portal</maml:uri>
</maml:navigationLink>
</command:relatedLinks>
</command:command>
@@ -66,16 +66,24 @@
"System.Diagnostics.DiagnosticSource": "5.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "17.10.0",
"contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "6.0.0",
@@ -135,27 +143,44 @@
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "6.17.0",
"contentHash": "I3cSVE185qF3a222/iQIdmBFhrhZBtz7wZ1RUUbMuHC1un79XCI7vggbWdmbqIttFcUoeziemadO6t+3FLjcSA==",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.IdentityModel.Tokens": "6.17.0"
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "6.17.0",
"contentHash": "Ix6/CMLDoo939NDf1ARDuGK6YERY7pAX9WYbfwb4gZqx7r52unMFIykJk+zlEBX7jjtbDz/0uzikQFvheV9KsQ=="
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "6.17.0",
"contentHash": "mhOe+d9BQg5U45TkTCyXAFOjl7RvwaFj6v9qo8b+WFolkuGsfjSFfQ+WI9D3ho9sD/fK75gvL4JptmjLzyUPkw==",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.CSharp": "4.5.0",
"Microsoft.IdentityModel.Logging": "6.17.0",
"System.Security.Cryptography.Cng": "4.5.0"
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"Microsoft.Testing.Extensions.Telemetry": {
@@ -251,8 +276,8 @@
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
@@ -269,11 +294,11 @@
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "6.17.0",
"contentHash": "G3rY4WLr54Mo+97+AEq0ANpiKvW7E8Qu5bKWfVMa7rkyJtvrOxUqp/OLqrGw/6JDbD5GlxnAtFKukGteUuB0rQ==",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "6.17.0",
"Microsoft.IdentityModel.Tokens": "6.17.0"
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Reflection.Metadata": {
@@ -310,7 +335,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Security.Cryptography.Cng": "[5.0.0, )",
"System.Text.Json": "[8.0.5, )"
}
@@ -318,7 +345,7 @@
"dsinternals.win32.webauthn.adapter": {
"type": "Project",
"dependencies": {
"DSInternals.Win32.WebAuthn": "[1.0.3, )",
"DSInternals.Win32.WebAuthn": "[1.0.4, )",
"Fido2.Models": "[3.0.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.Security.Cryptography.Cng": "[5.0.0, )"
@@ -35,6 +35,62 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ=="
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.CSharp": "4.5.0",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Security.Cryptography.Cng": "4.5.0",
"System.Text.Json": "8.0.5"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "12.0.3",
@@ -55,11 +111,29 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -101,7 +175,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Security.Cryptography.Cng": "[5.0.0, )",
"System.Text.Json": "[8.0.5, )"
}
@@ -135,6 +211,57 @@
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Memory": "4.5.5",
"System.Text.Json": "8.0.5"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "12.0.3",
@@ -155,6 +282,24 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -215,7 +360,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Text.Json": "[8.0.5, )"
}
}
@@ -248,6 +395,57 @@
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Memory": "4.5.5",
"System.Text.Json": "8.0.5"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "12.0.3",
@@ -268,6 +466,24 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -328,7 +544,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Text.Json": "[8.0.5, )"
}
}
@@ -367,6 +585,62 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ=="
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.CSharp": "4.5.0",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Security.Cryptography.Cng": "4.5.0",
"System.Text.Json": "8.0.5"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "12.0.3",
@@ -387,11 +661,28 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -433,7 +724,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Security.Cryptography.Cng": "[5.0.0, )",
"System.Text.Json": "[8.0.5, )"
}
@@ -465,6 +758,59 @@
"System.Formats.Asn1": "5.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"PeterO.Numbers": {
"type": "Transitive",
"resolved": "1.8.2",
@@ -475,11 +821,28 @@
"resolved": "1.0.0",
"contentHash": "fpRTBsYACMp7NvTECauYRomubWTC3vUNw4hMXdIedP8ctBGK6tea9HOJwE+qVzis6MZYkL3LIs8qeY3rc6Jdlw=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
@@ -505,7 +868,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Security.Cryptography.Cng": "[5.0.0, )",
"System.Text.Json": "[8.0.5, )"
}
@@ -537,6 +902,59 @@
"System.Formats.Asn1": "5.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"PeterO.Numbers": {
"type": "Transitive",
"resolved": "1.8.2",
@@ -547,11 +965,33 @@
"resolved": "1.0.0",
"contentHash": "fpRTBsYACMp7NvTECauYRomubWTC3vUNw4hMXdIedP8ctBGK6tea9HOJwE+qVzis6MZYkL3LIs8qeY3rc6Jdlw=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -568,7 +1008,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Security.Cryptography.Cng": "[5.0.0, )",
"System.Text.Json": "[8.0.5, )"
}
@@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fido2.Models" Version="3.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.5.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.5.1" />
@@ -1,4 +1,5 @@
using System.Text.Json;
using DSInternals.Win32.WebAuthn.EntraID;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DSInternals.Win32.WebAuthn.Tests
@@ -1,13 +1,304 @@
using System.Text.Json;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Formats.Asn1;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using DSInternals.Win32.WebAuthn.EntraID;
using DSInternals.Win32.WebAuthn.FIDO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PeterO.Cbor;
using DSInternals.Win32.WebAuthn.Okta;
namespace DSInternals.Win32.WebAuthn.Tests
{
[TestClass]
public class PasskeyFactory
{
internal WebauthnCredentialCreationOptions _options;
internal CBORObject _attestationObject => CBORObject.NewMap().
Add("fmt", "packed").
Add("authData", _authData).
Add("attStmt", CBORObject.NewMap().
Add("alg", _alg).
Add("sig", _sig).
Add("x5c", _x5c)
);
internal COSE.Algorithm _alg;
internal COSE.KeyType _kty
{
get
{
return _alg switch
{
COSE.Algorithm.RS1 or COSE.Algorithm.RS256 or COSE.Algorithm.RS384 or COSE.Algorithm.RS512 or COSE.Algorithm.PS256 or COSE.Algorithm.PS384 or COSE.Algorithm.PS512 => COSE.KeyType.RSA,
COSE.Algorithm.ES256 or COSE.Algorithm.ES384 or COSE.Algorithm.ES512 => COSE.KeyType.EC2,
COSE.Algorithm.EdDSA => COSE.KeyType.OKP,
_ => throw new ArgumentOutOfRangeException(nameof(_alg)),
};
}
}
internal COSE.EllipticCurve _crv
{
get
{
return _alg switch
{
COSE.Algorithm.ES256 => COSE.EllipticCurve.P256,
COSE.Algorithm.ES384 => COSE.EllipticCurve.P384,
COSE.Algorithm.ES512 => COSE.EllipticCurve.P521,
_ => throw new ArgumentOutOfRangeException(nameof(_alg)),
};
}
}
internal RSASignaturePadding _padding
{
get
{
return _alg switch
{
COSE.Algorithm.RS1 or COSE.Algorithm.RS256 or COSE.Algorithm.RS384 or COSE.Algorithm.RS512 => RSASignaturePadding.Pkcs1,
COSE.Algorithm.PS256 or COSE.Algorithm.PS384 or COSE.Algorithm.PS512 => RSASignaturePadding.Pss,
_ => throw new ArgumentOutOfRangeException(nameof(_alg)),
};
}
}
internal CredentialPublicKey _credentialPublicKey;
internal string _rp => _options.PublicKeyOptions.RelyingParty.Id;
internal string _origin => new UriBuilder("https", _options.PublicKeyOptions.RelyingParty.Id).ToString();
internal byte[] _challenge => _options.PublicKeyOptions.Challenge;
internal CertificateRequest _certReq;
internal static X500DistinguishedName _rootDN = new X500DistinguishedName("CN=Testing, O=DSInternals, OU=Passkeys, C=US");
internal static byte[] _asnEncodedAaguid = [0x04, 0x10, 0x44, 0x53, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,];
internal byte[] _sig;
internal CBORObject _x5c
{
get
{
_certReq.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, true, 2, false));
_certReq.CertificateExtensions.Add(new X509Extension(new Oid("1.3.6.1.4.1.45724.1.1.4"), _asnEncodedAaguid, true));
using X509Certificate2 root = _certReq.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(2));
return CBORObject.NewArray().Add(root.RawData);
}
}
internal byte[] _rpIdHash => SHA256.HashData(Encoding.UTF8.GetBytes(_rp));
internal byte[] _clientDataJson
{
get
{
return JsonSerializer.SerializeToUtf8Bytes(new CollectedClientData()
{
Type = "webauthn.create",
Challenge = _challenge,
Origin = _origin
});
}
}
internal byte[] _clientDataHash => SHA256.HashData(_clientDataJson);
internal byte[] _attToBeSigned {
get
{
byte[] toBeSigned = new byte[_authData.Length + _clientDataHash.Length];
_authData.CopyTo(toBeSigned, 0);
_clientDataHash.CopyTo(toBeSigned, _authData.Length);
return toBeSigned;
}
}
internal static byte[] HashData(HashAlgorithmName hashName, ReadOnlySpan<byte> data)
{
return hashName.Name switch
{
"SHA1" => SHA1.HashData(data),
"SHA256" or "HS256" or "RS256" or "ES256" or "PS256" => SHA256.HashData(data),
"SHA384" or "HS384" or "RS384" or "ES384" or "PS384" => SHA384.HashData(data),
"SHA512" or "HS512" or "RS512" or "ES512" or "PS512" => SHA512.HashData(data),
_ => throw new ArgumentOutOfRangeException(nameof(hashName)),
};
}
internal byte[] _attToBeSignedHash(HashAlgorithmName alg)
{
return HashData(alg, _attToBeSigned);
}
internal byte[] _credentialID;
internal const AuthenticatorFlags _flags = AuthenticatorFlags.AttestationData | AuthenticatorFlags.ExtensionData | AuthenticatorFlags.UserPresent | AuthenticatorFlags.UserVerified;
internal ushort _signCount;
internal static byte[] _aaguid = [0x44, 0x53, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,];
internal byte[] _authData
{
get
{
var writer = new ArrayBufferWriter<byte>(512);
writer.Write(_rpIdHash);
writer.Write(stackalloc byte[1] { (byte)_flags });
var buffer = writer.GetSpan(4);
BinaryPrimitives.WriteUInt32BigEndian(buffer, _signCount);
writer.Advance(4);
writer.Write(_acd);
CBORObject exts = CBORObject.NewMap().Add("testing", true);
writer.Write(exts.EncodeToBytes());
return writer.WrittenSpan.ToArray();
}
}
internal byte[] _acd
{
get
{
var writer = new ArrayBufferWriter<byte>(16 + 2 + _credentialID.Length + _credentialPublicKey.GetBytes().Length);
writer.Write(_aaguid);
// Write the length of credential ID, as big endian bytes of a 16-bit unsigned integer
var credentialIDLen = (ushort)_credentialID.Length;
var credentialIDLenBytes = BitConverter.GetBytes(credentialIDLen);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(credentialIDLenBytes);
}
writer.Write(credentialIDLenBytes);
// Write CredentialID bytes
writer.Write(_credentialID);
// Write credential public key bytes
writer.Write(_credentialPublicKey.GetBytes());
return writer.WrittenSpan.ToArray();
}
}
internal static HashAlgorithmName HashAlgFromCOSEAlg(COSE.Algorithm alg)
{
return alg switch
{
COSE.Algorithm.RS1 => HashAlgorithmName.SHA1,
COSE.Algorithm.ES256 => HashAlgorithmName.SHA256,
COSE.Algorithm.ES384 => HashAlgorithmName.SHA384,
COSE.Algorithm.ES512 => HashAlgorithmName.SHA512,
COSE.Algorithm.PS256 => HashAlgorithmName.SHA256,
COSE.Algorithm.PS384 => HashAlgorithmName.SHA384,
COSE.Algorithm.PS512 => HashAlgorithmName.SHA512,
COSE.Algorithm.RS256 => HashAlgorithmName.SHA256,
COSE.Algorithm.RS384 => HashAlgorithmName.SHA384,
COSE.Algorithm.RS512 => HashAlgorithmName.SHA512,
(COSE.Algorithm)4 => HashAlgorithmName.SHA1,
(COSE.Algorithm)11 => HashAlgorithmName.SHA256,
(COSE.Algorithm)12 => HashAlgorithmName.SHA384,
(COSE.Algorithm)13 => HashAlgorithmName.SHA512,
COSE.Algorithm.EdDSA => HashAlgorithmName.SHA512,
_ => throw new ArgumentOutOfRangeException(nameof(alg)),
};
}
internal void MakeCredentialPublicKey()
{
var cpk = CBORObject.NewMap().
Add(COSE.KeyCommonParameter.KeyType, _kty).
Add(COSE.KeyCommonParameter.Alg, _alg);
switch (_kty)
{
case COSE.KeyType.EC2:
ECCurve curve = _crv switch
{
COSE.EllipticCurve.P256 => ECCurve.NamedCurves.nistP256,
COSE.EllipticCurve.P384 => ECCurve.NamedCurves.nistP384,
COSE.EllipticCurve.P521 => ECCurve.NamedCurves.nistP521,
_ => throw new ArgumentOutOfRangeException(nameof(_crv)),
};
var ecdsa = ECDsa.Create(curve);
_certReq = new CertificateRequest(_rootDN, ecdsa, HashAlgorithmName.SHA256);
var ecparams = ecdsa.ExportParameters(true);
cpk.Add(COSE.KeyTypeParameter.X, ecparams.Q.X);
cpk.Add(COSE.KeyTypeParameter.Y, ecparams.Q.Y);
cpk.Add((int)COSE.KeyTypeParameter.Crv, (int)_crv);
_credentialPublicKey = new CredentialPublicKey(cpk);
var sig = ecdsa.SignData(_attToBeSigned, HashAlgFromCOSEAlg(_alg));
var coefficientSize = (int)Math.Ceiling((decimal)ecdsa.KeySize / 8);
var r = sig[0..coefficientSize];
var s = sig[(sig.Length - coefficientSize)..sig.Length];
var asnwriter = new AsnWriter(AsnEncodingRules.BER);
ReadOnlySpan<byte> zero = new byte[1] { 0 };
using (asnwriter.PushSequence())
{
asnwriter.WriteIntegerUnsigned(r);
asnwriter.WriteIntegerUnsigned(s);
}
_sig = asnwriter.Encode();
break;
case COSE.KeyType.RSA:
var rsa = RSA.Create();
var padding = _alg switch // https://www.iana.org/assignments/cose/cose.xhtml#algorithms
{
COSE.Algorithm.RS1 or COSE.Algorithm.RS256 or COSE.Algorithm.RS384 or COSE.Algorithm.RS512 => RSASignaturePadding.Pkcs1,
COSE.Algorithm.PS256 or COSE.Algorithm.PS384 or COSE.Algorithm.PS512 => RSASignaturePadding.Pss,
_ => throw new ArgumentOutOfRangeException(nameof(_alg)),
};
_certReq = new CertificateRequest(_rootDN, rsa, HashAlgorithmName.SHA256, padding);
var rsaparams = rsa.ExportParameters(true);
cpk.Add(COSE.KeyTypeParameter.N, rsaparams.Modulus);
cpk.Add(COSE.KeyTypeParameter.E, rsaparams.Exponent);
_credentialPublicKey = new CredentialPublicKey(cpk);
_sig = rsa.SignData(_attToBeSigned, HashAlgFromCOSEAlg(_alg), _padding);
break;
default:
throw new ArgumentOutOfRangeException(nameof(_kty), _kty, "Invalid COSE key type");
}
}
public WebauthnAttestationResponse MakePasskey(WebauthnCredentialCreationOptions options, int algIndex = 0)
{
WebauthnAttestationResponse response;
_credentialID = RandomNumberGenerator.GetBytes(32);
_options = options;
_alg = _options.PublicKeyOptions.PublicKeyCredentialParameters[algIndex].Algorithm;
MakeCredentialPublicKey();
PublicKeyCredential pkc = new()
{
AuthenticatorResponse = new AuthenticatorAttestationResponse()
{
AttestationObject = _attestationObject.EncodeToBytes(),
ClientDataJson = _clientDataJson
},
ClientExtensionResults = new()
{
HmacSecret = true
},
Id = _credentialID
};
response = options.GetType().Name switch
{
nameof(MicrosoftGraphWebauthnCredentialCreationOptions) => new MicrosoftGraphWebauthnAttestationResponse(pkc, $"DSInternals.Passkeys {_alg}"),
nameof(OktaWebauthnCredentialCreationOptions) => new OktaWebauthnAttestationResponse(pkc, options.PublicKeyOptions.User.Id, (options as OktaWebauthnCredentialCreationOptions).Id),
_ => throw new ArgumentOutOfRangeException(nameof(options)),
};
return response;
}
}
[TestClass]
public class PublicKeyCredentialCreationOptionsTester
{
[TestMethod]
public void PublicKeyCredentialCreationOptions_Deserialize()
public void EntraIdPublicKeyCredentialCreationOptions_Deserialize()
{
var options = JsonSerializer.Deserialize<PublicKeyCredentialCreationOptions>(@"{
""rp"": {
@@ -97,5 +388,61 @@ namespace DSInternals.Win32.WebAuthn.Tests
Assert.AreEqual(COSE.Algorithm.RS256, options.PublicKeyCredentialParameters[1].Algorithm);
Assert.AreEqual(COSE.Algorithm.EdDSA, options.PublicKeyCredentialParameters[2].Algorithm);
}
[TestMethod]
public void OktaPublicKeyCredentialCreationOptions_Deserialize()
{
var options = JsonSerializer.Deserialize<PublicKeyCredentialCreationOptions>(@"{
""rp"": {
""name"": ""Okta Tenant Name -- Environment"",
""id"": ""example.okta.com""
},
""user"": {
""displayName"": ""Okta Doe"",
""name"": ""okta@contoso.com"",
""id"": ""00eDuihq64pgP1gVD0x7""
},
""pubKeyCredParams"": [
{
""type"": ""public-key"",
""alg"": -7
},
{
""type"": ""public-key"",
""alg"": -257
}
],
""challenge"": ""AVun-poGmJKZOAT0r-KBSs-94BPqMf3j"",
""attestation"": ""direct"",
""authenticatorSelection"": {
""userVerification"": ""required"",
""requireResidentKey"": false
},
""u2fParams"": {
""appid"": ""https://example.okta.com""
},
""excludeCredentials"": [
{
""type"": ""public-key"",
""id"": ""VX_AlCL9qUx2ox_Ekth4NYngvwpUswaBqcfb4XsHglI""
},
{
""type"": ""public-key"",
""id"": ""kFPT5CL3-I30e22QQ0WYo4C9EFCTcbWM0-G-wTBslVNzKzf-FsJ1CBVrgN2k5RJH2dTFJxyzgI06XxIbrcbpAA""
}
]
}");
Assert.AreEqual("example.okta.com", options.RelyingParty.Id);
Assert.AreEqual("okta@contoso.com", options.User.Name);
Assert.AreEqual(AttestationConveyancePreference.Direct, options.Attestation);
Assert.IsNotNull(options.ExcludeCredentials);
Assert.IsFalse(options.AuthenticatorSelection.RequireResidentKey);
Assert.AreEqual(AuthenticatorAttachment.Any, options.AuthenticatorSelection.AuthenticatorAttachment);
Assert.AreEqual(UserVerificationRequirement.Required, options.AuthenticatorSelection.UserVerificationRequirement);
Assert.AreEqual(2, options.PublicKeyCredentialParameters.Count);
Assert.AreEqual(COSE.Algorithm.ES256, options.PublicKeyCredentialParameters[0].Algorithm);
Assert.AreEqual(COSE.Algorithm.RS256, options.PublicKeyCredentialParameters[1].Algorithm);
}
}
}
@@ -8,6 +8,12 @@
"resolved": "6.0.2",
"contentHash": "bJShQ6uWRTQ100ZeyiMqcFlhP7WJ+bCuabUs885dJiBEzMsJMSFr7BOyeCw4rgvQokteGi5rKQTlkhfQPUXg2A=="
},
"Fido2.Models": {
"type": "Direct",
"requested": "[3.0.1, )",
"resolved": "3.0.1",
"contentHash": "mgjcuGETuYSCUEaZG+jQeeuuEMkDLc4GDJHBvKDdOz6oSOWp5adPdWP4btZx7Pi+9fu4szN3JIjJmby67MaILw=="
},
"Microsoft.NET.Test.Sdk": {
"type": "Direct",
"requested": "[17.10.0, )",
@@ -51,11 +57,64 @@
"System.Diagnostics.DiagnosticSource": "5.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "17.10.0",
"contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA=="
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"Microsoft.Testing.Extensions.Telemetry": {
"type": "Transitive",
"resolved": "1.3.2",
@@ -141,19 +200,36 @@
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA=="
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Reflection.Metadata": {
"type": "Transitive",
"resolved": "1.6.0",
"contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Security.Cryptography.Cng": {
"type": "Transitive",
"resolved": "5.0.0",
@@ -170,7 +246,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Security.Cryptography.Cng": "[5.0.0, )",
"System.Text.Json": "[8.0.5, )"
}
@@ -43,12 +43,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.1" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!-- TODO: Replace with System.Formats.Cbor -->
<PackageReference Include="PeterO.Cbor" Version="4.5.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.0" />
<!-- Note: Classes from this namespace are part of .NET Framework -->
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" Condition="'$(TargetFramework)' != 'net472' AND '$(TargetFramework)' != 'net48'" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
@@ -1,9 +1,9 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DSInternals.Win32.WebAuthn
namespace DSInternals.Win32.WebAuthn.EntraID
{
public class MicrosoftGraphWebauthnAttestationResponse
public class MicrosoftGraphWebauthnAttestationResponse : WebauthnAttestationResponse
{
/// <summary>
/// The display name of the key as given by the user.
@@ -15,11 +15,11 @@ namespace DSInternals.Win32.WebAuthn
/// Contains the WebAuthn public key credential information being registered.
/// </summary>
[JsonPropertyName("publicKeyCredential")]
public PublicKeyCredential PublicKeyCredential { get; set; }
public override PublicKeyCredential PublicKeyCred { get; set; }
public MicrosoftGraphWebauthnAttestationResponse(PublicKeyCredential publicKeyCredential, string displayName)
{
PublicKeyCredential = publicKeyCredential;
PublicKeyCred = publicKeyCredential;
DisplayName = displayName;
}
@@ -2,13 +2,13 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DSInternals.Win32.WebAuthn
namespace DSInternals.Win32.WebAuthn.EntraID
{
/// <summary>
///
/// </summary>
/// <remarks>OData Object: https://graph.microsoft.com/beta/$metadata#microsoft.graph.webauthnCredentialCreationOptions</remarks>
public class MicrosoftGraphWebauthnCredentialCreationOptions
public class MicrosoftGraphWebauthnCredentialCreationOptions : WebauthnCredentialCreationOptions
{
/// <summary>
/// Defines when the challenge in the creation options is no longer valid.
@@ -20,7 +20,7 @@ namespace DSInternals.Win32.WebAuthn
/// Defines public key options for the creation of a new WebAuthn public key credential.
/// </summary>
[JsonPropertyName("publicKey")]
public PublicKeyCredentialCreationOptions PublicKeyOptions { get; set; }
public override PublicKeyCredentialCreationOptions PublicKeyOptions { get; set; }
public static MicrosoftGraphWebauthnCredentialCreationOptions Create(string json)
{
@@ -0,0 +1,146 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DSInternals.Win32.WebAuthn.Okta
{
public class OktaProfile
{
[JsonConstructor]
public OktaProfile(
string credentialId,
object appId,
object version,
string authenticatorName,
object presetPinAvailable,
object fulfillmentProvider
)
{
this.CredentialId = credentialId;
this.AppId = appId;
this.Version = version;
this.AuthenticatorName = authenticatorName;
this.PresetPinAvailable = presetPinAvailable;
this.FulfillmentProvider = fulfillmentProvider;
}
/// <summary>
/// ID for the Factor credential
/// </summary>
[JsonPropertyName("credentialId")]
public string CredentialId { get; }
/// <summary>
/// U2F appId string
/// </summary>
[JsonPropertyName("appId")]
public object AppId { get; }
/// <summary>
/// Undocumented
/// </summary>
[JsonPropertyName("version")]
public object Version { get; }
/// <summary>
/// Human-readable name of the authenticator
/// </summary>
[JsonPropertyName("authenticatorName")]
public string AuthenticatorName { get; }
/// <summary>
/// Undocumented
/// </summary>
[JsonPropertyName("presetPinAvailable")]
public object PresetPinAvailable { get; }
/// <summary>
/// Undocumented
/// </summary>
[JsonPropertyName("fulfillmentProvider")]
public object FulfillmentProvider { get; }
}
public class OktaFido2AuthenticationMethod
{
[JsonConstructor]
public OktaFido2AuthenticationMethod(
string id,
string factorType,
string provider,
string vendorName,
string status,
DateTime? created,
DateTime? lastUpdated,
OktaProfile profile
)
{
this.Id = id;
this.FactorType = factorType;
this.Provider = provider;
this.VendorName = vendorName;
this.Status = status;
this.Created = created;
this.LastUpdated = lastUpdated;
this.Profile = profile;
}
public static OktaFido2AuthenticationMethod FromJsonString(string json)
{
if (string.IsNullOrEmpty(json))
{
throw new ArgumentNullException(nameof(json));
}
return JsonSerializer.Deserialize<OktaFido2AuthenticationMethod>(json);
}
/// <summary>
/// ID of the Factor
/// </summary>
[JsonPropertyName("id")]
public string Id { get; }
/// <summary>
/// Type of Factor
/// </summary>
[JsonPropertyName("factorType")]
public string FactorType { get; }
/// <summary>
/// Provider for the Factor
/// </summary>
[JsonPropertyName("provider")]
public string Provider { get; }
/// <summary>
/// Name of the Factor vendor. This is usually the same as the provider except for On-Prem MFA where it depends on administrator settings.
/// </summary>
[JsonPropertyName("vendorName")]
public string VendorName { get; }
/// <summary>
/// Status of the Factor
/// </summary>
[JsonPropertyName("status")]
public string Status { get; }
/// <summary>
/// Timestamp when the Factor was enrolled
/// </summary>
[JsonPropertyName("created")]
public DateTime? Created { get; }
/// <summary>
/// Timestamp when the Factor was last updated
/// </summary>
[JsonPropertyName("lastUpdated")]
public DateTime? LastUpdated { get; }
/// <summary>
/// Specific attributes related to the Factor
/// </summary>
[JsonPropertyName("profile")]
public OktaProfile Profile { get; }
}
}
@@ -0,0 +1,52 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DSInternals.Win32.WebAuthn.Okta
{
public class OktaWebauthnAttestationResponse : WebauthnAttestationResponse
{
/// <summary>
/// Contains the WebAuthn public key credential information being registered.
/// </summary>
[JsonIgnore]
public override PublicKeyCredential PublicKeyCred { get; set; }
/// <summary>
/// ID of an existing Okta user.
/// </summary>
[JsonIgnore]
public string UserId { get; set; }
/// <summary>
/// ID of an existing user Factor.
/// </summary>
[JsonIgnore]
public string FactorId { get; set; }
/// <summary>
/// ID of an existing user Factor.
/// </summary>
[JsonPropertyName("attestation")]
[JsonConverter(typeof(Base64UrlConverter))]
public byte[] Attestation => PublicKeyCred.AuthenticatorResponse.AttestationObject;
/// <summary>
/// ID of an existing user Factor.
/// </summary>
[JsonPropertyName("clientData")]
[JsonConverter(typeof(Base64UrlConverter))]
public byte[] ClientData => PublicKeyCred.AuthenticatorResponse.ClientDataJson;
public OktaWebauthnAttestationResponse(PublicKeyCredential publicKeyCredential, byte[] userId, string factorId)
{
PublicKeyCred = publicKeyCredential;
UserId = Base64UrlConverter.ToBase64UrlString(userId);
FactorId = factorId;
}
override public string ToString()
{
return JsonSerializer.Serialize(this);
}
}
}
@@ -0,0 +1,44 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DSInternals.Win32.WebAuthn.Okta
{
public class Embedded
{
/// <summary>
/// Defines public key options for the creation of a new WebAuthn public key credential.
/// </summary>
[JsonPropertyName("activation")]
public PublicKeyCredentialCreationOptions PublicKeyOptions { get; set; }
}
/// <summary>
///
/// </summary>
public class OktaWebauthnCredentialCreationOptions : WebauthnCredentialCreationOptions
{
/// <summary>
/// The factor id of the Okta factor being registered.
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }
/// <summary>
/// A wrapper around the PublicKeyCredentialCreationOptions
/// </summary>
[JsonPropertyName("_embedded")]
public Embedded Embedded { get; set; }
public override PublicKeyCredentialCreationOptions PublicKeyOptions { get => Embedded.PublicKeyOptions; set { }}
public static OktaWebauthnCredentialCreationOptions Create(string json)
{
if(string.IsNullOrEmpty(json))
{
throw new ArgumentNullException(nameof(json));
}
return JsonSerializer.Deserialize<OktaWebauthnCredentialCreationOptions>(json);
}
}
}
@@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace DSInternals.Win32.WebAuthn
{
public abstract class WebauthnAttestationResponse
{
[JsonIgnore()]
public abstract PublicKeyCredential PublicKeyCred { get; set; }
}
}
@@ -0,0 +1,7 @@
namespace DSInternals.Win32.WebAuthn
{
public abstract class WebauthnCredentialCreationOptions
{
public abstract PublicKeyCredentialCreationOptions PublicKeyOptions { get; set; }
}
}
@@ -2,6 +2,16 @@
"version": 1,
"dependencies": {
".NETCoreApp,Version=v3.1": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -23,6 +33,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Security.Cryptography.Cng": {
"type": "Direct",
"requested": "[5.0.0, )",
@@ -51,6 +71,53 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ=="
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.CSharp": "4.5.0",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Security.Cryptography.Cng": "4.5.0",
"System.Text.Json": "8.0.5"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -84,6 +151,15 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
@@ -116,6 +192,16 @@
}
},
".NETFramework,Version=v4.7.2": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -137,6 +223,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Text.Json": {
"type": "Direct",
"requested": "[8.0.5, )",
@@ -160,6 +256,48 @@
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Memory": "4.5.5",
"System.Text.Json": "8.0.5"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -193,6 +331,15 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -238,6 +385,16 @@
}
},
".NETFramework,Version=v4.8": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -259,6 +416,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Text.Json": {
"type": "Direct",
"requested": "[8.0.5, )",
@@ -282,6 +449,48 @@
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Memory": "4.5.5",
"System.Text.Json": "8.0.5"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -315,6 +524,15 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -360,6 +578,16 @@
}
},
".NETCoreApp,Version=v5.0": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -381,6 +609,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Security.Cryptography.Cng": {
"type": "Direct",
"requested": "[5.0.0, )",
@@ -409,6 +647,53 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ=="
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.CSharp": "4.5.0",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Security.Cryptography.Cng": "4.5.0",
"System.Text.Json": "8.0.5"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -442,6 +727,14 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
@@ -474,6 +767,16 @@
}
},
"net6.0": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -495,6 +798,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Security.Cryptography.Cng": {
"type": "Direct",
"requested": "[5.0.0, )",
@@ -514,6 +827,50 @@
"System.Text.Encodings.Web": "8.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -542,6 +899,14 @@
"resolved": "1.0.0",
"contentHash": "fpRTBsYACMp7NvTECauYRomubWTC3vUNw4hMXdIedP8ctBGK6tea9HOJwE+qVzis6MZYkL3LIs8qeY3rc6Jdlw=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
@@ -562,6 +927,16 @@
}
},
"net7.0": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -583,6 +958,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Security.Cryptography.Cng": {
"type": "Direct",
"requested": "[5.0.0, )",
@@ -601,6 +986,50 @@
"System.Text.Encodings.Web": "8.0.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -629,11 +1058,24 @@
"resolved": "1.0.0",
"contentHash": "fpRTBsYACMp7NvTECauYRomubWTC3vUNw4hMXdIedP8ctBGK6tea9HOJwE+qVzis6MZYkL3LIs8qeY3rc6Jdlw=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
@@ -641,6 +1083,16 @@
}
},
"net8.0": {
"Microsoft.Identity.Client": {
"type": "Direct",
"requested": "[4.66.1, )",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.Windows.CsWin32": {
"type": "Direct",
"requested": "[0.3.106, )",
@@ -662,6 +1114,16 @@
"PeterO.URIUtility": "1.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Direct",
"requested": "[8.2.0, )",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Security.Cryptography.Cng": {
"type": "Direct",
"requested": "[5.0.0, )",
@@ -677,6 +1139,42 @@
"resolved": "8.0.5",
"contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg=="
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA=="
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0"
}
},
"Microsoft.Windows.SDK.Win32Docs": {
"type": "Transitive",
"resolved": "0.1.42-alpha",
@@ -705,10 +1203,23 @@
"resolved": "1.0.0",
"contentHash": "fpRTBsYACMp7NvTECauYRomubWTC3vUNw4hMXdIedP8ctBGK6tea9HOJwE+qVzis6MZYkL3LIs8qeY3rc6Jdlw=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Formats.Asn1": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "MTvUIktmemNB+El0Fgw9egyqT9AYSIk6DTJeoDSpc3GIHxHCMo8COqkWT1mptX5tZ1SlQ6HJZ0OsSvMth1c12w=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
}
}
}
+71
View File
@@ -46,6 +46,57 @@
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Bcl.TimeProvider": {
"type": "Transitive",
"resolved": "8.0.1",
"contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Identity.Client": {
"type": "Transitive",
"resolved": "4.66.1",
"contentHash": "mE+m3pZ7zSKocSubKXxwZcUrCzLflC86IdLxrVjS8tialy0b1L+aECBqRBC/ykcPlB4y7skg49TaTiA+O2UfDw==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "6.35.0",
"System.Diagnostics.DiagnosticSource": "6.0.1"
}
},
"Microsoft.IdentityModel.Abstractions": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "27ClfnelIu92kLGOaz0vjdXR1Jv7hAdLffxxNgR8T0+IMWmxeVyO3cU8oohmuTrWUFOfd2tsSGaRNewnuClIZw=="
},
"Microsoft.IdentityModel.JsonWebTokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/DAx+9HeqfkH/PccwHx7cUtQe9fYM6AxEmTla8WXUT+w+mapKLnigWmdKtF55hNvxiSnmGhSSCcG7XrvkpGKFA==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"Microsoft.IdentityModel.Logging": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "mZsjOZlbmCZfM71y8Fyo+D5UJ1RZFvmKXkxTfE2llQ0/CrfEeWmbpoew51w++EWs+G8B/peZqR1DQtbX3bB6Fg==",
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.2.0"
}
},
"Microsoft.IdentityModel.Tokens": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "/I+6D3SwW8hQh5wznGzQCrS4L5y5Re/0AEKKfXXAduWzz4WKqJzY8RmjwZ6W66bIFUhPrqOy6zsLKPik4Ppnbw==",
"dependencies": {
"Microsoft.Bcl.TimeProvider": "8.0.1",
"Microsoft.IdentityModel.Logging": "8.2.0",
"System.Memory": "4.5.5",
"System.Text.Json": "8.0.5"
}
},
"Microsoft.Windows.SDK.Contracts": {
"type": "Transitive",
"resolved": "10.0.18362.2005",
@@ -84,6 +135,24 @@
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
"dependencies": {
"System.Memory": "4.5.4",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.IdentityModel.Tokens.Jwt": {
"type": "Transitive",
"resolved": "8.2.0",
"contentHash": "M61kEnR2ljMFL2vLHmHrodCqPO/zKsYsaRu3jdagYOP/y0ZdFoFoATyfwR0bJ5quBsWEPL5y8QU7CJiEh2kq+Q==",
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.2.0",
"Microsoft.IdentityModel.Tokens": "8.2.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
@@ -174,7 +243,9 @@
"dsinternals.win32.webauthn": {
"type": "Project",
"dependencies": {
"Microsoft.Identity.Client": "[4.66.1, )",
"PeterO.Cbor": "[4.5.3, )",
"System.IdentityModel.Tokens.Jwt": "[8.2.0, )",
"System.Text.Json": "[8.0.5, )"
}
}