From 17690d5afd7015e7d42b8e8e48c776ce74b04fa2 Mon Sep 17 00:00:00 2001 From: "Taofeek F. Obafemi-Babatunde" Date: Thu, 28 Dec 2023 10:52:21 -0800 Subject: [PATCH 1/5] adding "-AT PoP" option to "Set-MgGraphOptions" --- docs/authentication.md | 18 ++++++++++++++++++ .../Interfaces/IGraphOptions.cs | 1 + .../Microsoft.Graph.Authentication.Core.csproj | 5 ++++- .../Authentication/Cmdlets/SetMgGraphOption.cs | 8 ++++++++ .../Microsoft.Graph.Authentication.psd1 | 4 ++-- .../Authentication/Models/GraphOption.cs | 1 + .../test/Set-MgGraphOption.Tests.ps1 | 14 ++++++++++++-- src/Authentication/docs/Set-MgGraphOption.md | 13 +++++++++++++ .../examples/Set-MgGraphOption.md | 8 +++++++- 9 files changed, 66 insertions(+), 6 deletions(-) diff --git a/docs/authentication.md b/docs/authentication.md index 6cfbfda695..6aeb7e32f0 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -112,6 +112,24 @@ When using `-AccessToken`, we won't have access to the refresh token and the cli Before using the provided `-AccessToken` to get Microsoft Graph resources, customers should ensure that the access token has the necessary scopes/ permissions needed to access/modify a resource. +### Access Token Proof of Possession (AT PoP) + +AT PoP is a security mechanism that binds an access token to a cryptographic key that only the intended recipient has. This prevents unauthorized use of the token by malicious actors. AT PoP enhances data protection, reduces token replay attacks, and enables fine-grained authorization policies. + +Microsoft Graph PowerShell module supports AT PoP in the following scenario: + +- To enable AT PoP on supported devices + +```PowerShell +Set-MgGraphOption -EnableATPoP $true +``` + +- To disable AT PoP on supported devices + +```PowerShell +Set-MgGraphOption -EnableATPoP $false +``` + ## Web Account Manager (WAM) WAM is a Windows 10+ component that acts as an authentication broker allowing the users of an app benefit from integration with accounts known to Windows, such as the account already signed into an active Windows session. diff --git a/src/Authentication/Authentication.Core/Interfaces/IGraphOptions.cs b/src/Authentication/Authentication.Core/Interfaces/IGraphOptions.cs index 3dd2483694..6f7f2dbbfb 100644 --- a/src/Authentication/Authentication.Core/Interfaces/IGraphOptions.cs +++ b/src/Authentication/Authentication.Core/Interfaces/IGraphOptions.cs @@ -11,5 +11,6 @@ namespace Microsoft.Graph.PowerShell.Authentication public interface IGraphOption { bool EnableWAMForMSGraph { get; set; } + bool EnableATPoPForMSGraph { get; set; } } } \ No newline at end of file diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index c385a4f63a..bff781160c 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -12,8 +12,11 @@ - + + + + diff --git a/src/Authentication/Authentication/Cmdlets/SetMgGraphOption.cs b/src/Authentication/Authentication/Cmdlets/SetMgGraphOption.cs index ad4f0f76a1..fc4da16a8e 100644 --- a/src/Authentication/Authentication/Cmdlets/SetMgGraphOption.cs +++ b/src/Authentication/Authentication/Cmdlets/SetMgGraphOption.cs @@ -13,6 +13,9 @@ public class SetMgGraphOption : PSCmdlet { [Parameter] public bool EnableLoginByWAM { get; set; } + + [Parameter] + public bool EnableATPoP { get; set; } protected override void BeginProcessing() { @@ -27,6 +30,11 @@ protected override void ProcessRecord() GraphSession.Instance.GraphOption.EnableWAMForMSGraph = EnableLoginByWAM; WriteDebug($"Signin by Web Account Manager (WAM) is {(EnableLoginByWAM ? "enabled" : "disabled")}."); } + if (this.IsParameterBound(nameof(EnableATPoP))) + { + GraphSession.Instance.GraphOption.EnableATPoPForMSGraph = EnableATPoP; + WriteDebug($"Access Token Proof of Posession (AT-PoP) is {(EnableATPoP ? "enabled" : "disabled")}."); + } File.WriteAllText(Constants.GraphOptionsFilePath, JsonConvert.SerializeObject(GraphSession.Instance.GraphOption, Formatting.Indented)); } diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 index 322b679fc0..02c29cc4e9 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft # -# Generated on: 21/09/2023 +# Generated on: 12/28/2023 # @{ @@ -12,7 +12,7 @@ RootModule = './Microsoft.Graph.Authentication.psm1' # Version number of this module. -ModuleVersion = '2.6.1' +ModuleVersion = '2.11.1' # Supported PSEditions CompatiblePSEditions = 'Core', 'Desktop' diff --git a/src/Authentication/Authentication/Models/GraphOption.cs b/src/Authentication/Authentication/Models/GraphOption.cs index d8c48d7f70..e8c83e6ef0 100644 --- a/src/Authentication/Authentication/Models/GraphOption.cs +++ b/src/Authentication/Authentication/Models/GraphOption.cs @@ -9,6 +9,7 @@ namespace Microsoft.Graph.PowerShell.Authentication internal class GraphOption : IGraphOption { public bool EnableWAMForMSGraph { get; set; } + public bool EnableATPoPForMSGraph { get; set; } } } \ No newline at end of file diff --git a/src/Authentication/Authentication/test/Set-MgGraphOption.Tests.ps1 b/src/Authentication/Authentication/test/Set-MgGraphOption.Tests.ps1 index 6a2cb60693..68a31efb74 100644 --- a/src/Authentication/Authentication/test/Set-MgGraphOption.Tests.ps1 +++ b/src/Authentication/Authentication/test/Set-MgGraphOption.Tests.ps1 @@ -9,14 +9,14 @@ Describe "Set-MgGraphOption" { Import-Module $ModulePath -Force -ErrorAction SilentlyContinue } Context "When executing the command" { - it 'Should have one ParameterSets' { + it 'Should have two ParameterSets' { $SetMgGraphOptionCommand = Get-Command Set-MgGraphOption $SetMgGraphOptionCommand | Should -Not -BeNullOrEmpty $SetMgGraphOptionCommand.ParameterSets | Should -HaveCount 1 $SetMgGraphOptionCommand.ParameterSets.Parameters | Should -HaveCount 13 # PS common parameters. } - It 'Executes successfully whren toggling WAM on' { + It 'Executes successfully when toggling WAM on' { { Set-MgGraphOption -EnableLoginByWAM $true -Debug | Out-Null } | Should -Not -Be $null { Set-MgGraphOption -EnableLoginByWAM $true -ErrorAction SilentlyContinue } | Should -Not -Throw } @@ -25,5 +25,15 @@ Describe "Set-MgGraphOption" { { Set-MgGraphOption -EnableLoginByWAM $false -Debug | Out-Null } | Should -Not -Be $null { Set-MgGraphOption -EnableLoginByWAM $false -ErrorAction SilentlyContinue } | Should -Not -Throw } + + It 'Executes successfully when toggling AT PoP on' { + { Set-MgGraphOption -EnableATPoP $true -Debug | Out-Null } | Should -Not -Be $null + { Set-MgGraphOption -EnableATPoP $true -ErrorAction SilentlyContinue } | Should -Not -Throw + } + + It 'Executes successfully when toggling AT PoP off' { + { Set-MgGraphOption -EnableATPoP $false -Debug | Out-Null } | Should -Not -Be $null + { Set-MgGraphOption -EnableATPoP $false -ErrorAction SilentlyContinue } | Should -Not -Throw + } } } \ No newline at end of file diff --git a/src/Authentication/docs/Set-MgGraphOption.md b/src/Authentication/docs/Set-MgGraphOption.md index 85ee3e9fca..8aaea7efb1 100644 --- a/src/Authentication/docs/Set-MgGraphOption.md +++ b/src/Authentication/docs/Set-MgGraphOption.md @@ -15,6 +15,9 @@ Sets global configurations that apply to the SDK. For example, toggle Web Accoun ``` Set-MgGraphOption [-EnableLoginByWAM ] [] ``` +``` +Set-MgGraphOption [-EnableATPoP ] [] +``` ## DESCRIPTION Sets global configurations that apply to the SDK. For example, toggle Web Account Manager (WAM) support. @@ -28,11 +31,21 @@ PS C:\> Set-MgGraphOption -EnableLoginByWAM $True Sets web account manager support +### Example 2: Set access token proof of possession support +```powershell +PS C:\> Set-MgGraphOption -EnableATPoP $True +``` + + Sets access token proof of possession support + ## PARAMETERS ### -EnableLoginByWAM {{ Fill EnableLoginByWAM Description }} +### -EnableATPoP +{{ Fill EnableATPoP Description }} + ```yaml Type: Boolean Parameter Sets: (All) diff --git a/src/Authentication/examples/Set-MgGraphOption.md b/src/Authentication/examples/Set-MgGraphOption.md index 055431b592..afc23d97cb 100644 --- a/src/Authentication/examples/Set-MgGraphOption.md +++ b/src/Authentication/examples/Set-MgGraphOption.md @@ -2,4 +2,10 @@ ```powershell PS C:\> Set-MgGraphOption -EnableLoginByWAM $True ``` - Sets web account manager support \ No newline at end of file + Sets web account manager support + +### Example 2: Set access token proof of possession support +```powershell +PS C:\> Set-MgGraphOption -EnableATPoP $True +``` + Sets access token proof of possession support \ No newline at end of file From 4a141ca2deec0e6530e8ac9705d11e1e2cb35f47 Mon Sep 17 00:00:00 2001 From: "Taofeek F. Obafemi-Babatunde" <22969702+FehintolaObafemi@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:10:02 -0800 Subject: [PATCH 2/5] Adding AT PoP skeleton (#2511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding "-AT PoP" option to "Set-MgGraphOptions" * Adding AT PoP skeleton --------- Co-authored-by: Tim Co-authored-by: Peter Ombwa Co-authored-by: Peter Ombwa Co-authored-by: Mustafa Zengin Co-authored-by: Clément Notin Co-authored-by: Microsoft Graph DevX Tooling Co-authored-by: Vincent Biret Co-authored-by: Vincent Biret Co-authored-by: Subhajit Ray (from Dev Box) --- docs/authentication.md | 2 + ...Microsoft.Graph.Authentication.Core.csproj | 16 ++-- .../Utilities/AuthenticationHelpers.cs | 33 ++++++- .../Utilities/PopClient.cs | 88 +++++++++++++++++++ .../Utilities/PopClientOptions.cs | 2 + 5 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/Authentication/Authentication.Core/Utilities/PopClient.cs create mode 100644 src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs diff --git a/docs/authentication.md b/docs/authentication.md index 6aeb7e32f0..3068135b3a 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -116,6 +116,8 @@ Before using the provided `-AccessToken` to get Microsoft Graph resources, custo AT PoP is a security mechanism that binds an access token to a cryptographic key that only the intended recipient has. This prevents unauthorized use of the token by malicious actors. AT PoP enhances data protection, reduces token replay attacks, and enables fine-grained authorization policies. +Note: AT PoP requires WAM to function. + Microsoft Graph PowerShell module supports AT PoP in the following scenario: - To enable AT PoP on supported devices diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index bff781160c..1dbb7dc287 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -1,22 +1,22 @@ - + 9.0 netstandard2.0;net6.0;net472 Microsoft.Graph.PowerShell.Authentication.Core - 2.18.0 + 2.12.0 true true - - - - - - + + + + + + diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 3d00433610..6a81e3ef46 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------------ using Azure.Core; using Azure.Core.Diagnostics; +using Azure.Core.Pipeline; using Azure.Identity; using Azure.Identity.Broker; using Microsoft.Graph.Authentication; @@ -86,6 +87,12 @@ private static bool IsWamSupported() return GraphSession.Instance.GraphOption.EnableWAMForMSGraph && SharedUtilities.IsWindowsPlatform(); } + //Check to see if ATPoP is Supported + private static bool IsATPoPSupported() + { + return GraphSession.Instance.GraphOption.EnableATPoPForMSGraph; + } + private static async Task GetClientSecretCredentialAsync(IAuthContext authContext) { if (authContext is null) @@ -125,11 +132,29 @@ private static async Task GetInteractiveBrowserCre var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions); if (IsWamSupported()) { - authRecord = await Task.Run(() => + // Adding a scenario to account for Access Token Proof of Possession + if (IsATPoPSupported()) { - // Run the thread in MTA. - return interactiveBrowserCredential.Authenticate(new TokenRequestContext(authContext.Scopes), cancellationToken); - }); + // Logic to implement ATPoP Authentication + var client = new PopClient(interactiveBrowserCredential, authContext, new PopClientOptions() + { + Diagnostics = + { + IsLoggingContentEnabled = true, + LoggedHeaderNames = { "Authorization" } + } + }); + //var response = client.Get(new Uri("https://20.190.132.47/beta/me"), CancellationToken.None); + authRecord = client.GetAuthRecord(); + } + else + { + authRecord = await Task.Run(() => + { + // Run the thread in MTA. + return interactiveBrowserCredential.Authenticate(new TokenRequestContext(authContext.Scopes), cancellationToken); + }); + } } else { diff --git a/src/Authentication/Authentication.Core/Utilities/PopClient.cs b/src/Authentication/Authentication.Core/Utilities/PopClient.cs new file mode 100644 index 0000000000..7343a6960d --- /dev/null +++ b/src/Authentication/Authentication.Core/Utilities/PopClient.cs @@ -0,0 +1,88 @@ +using System; +using System.IdentityModel; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.Identity; +using Azure.Identity.Broker; +using Microsoft.Identity.Client.NativeInterop; + +namespace Microsoft.Graph.PowerShell.Authentication.Core.Utilities +{ + public class PopClient + { + private readonly HttpPipeline _pipeline; + private AuthenticationRecord _authenticationRecord; + private readonly InteractiveBrowserCredential _interactiveBrowserCredential; + + public PopClient(TokenCredential credential, IAuthContext authContext, ClientOptions options = null) + { + //_interactiveBrowserCredential = (InteractiveBrowserCredential)credential; + _interactiveBrowserCredential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialBrokerOptions(WindowHandleUtlities.GetConsoleOrTerminalWindow())); + + if (!(credential is ISupportsProofOfPossession)) + { + throw new ArgumentException("The provided TokenCredential does not support proof of possession.", nameof(credential)); + } + + var pipelineOptions = new HttpPipelineOptions(options); + pipelineOptions.PerRetryPolicies.Add(new InteractivePopTokenAuthenticationPolicy(_interactiveBrowserCredential, "https://graph.microsoft.com/.default", () => _authenticationRecord)); + + _pipeline = HttpPipelineBuilder.Build(pipelineOptions); + } + + public async ValueTask GetAsync(Uri uri, CancellationToken cancellationToken = default) + { + using var request = _pipeline.CreateRequest(); + request.Method = RequestMethod.Get; + request.Uri.Reset(uri); + return await _pipeline.SendRequestAsync(request, cancellationToken).ConfigureAwait(false); + } + + public Response Get(Uri uri, CancellationToken cancellationToken = default) + { + using var request = _pipeline.CreateRequest(); + request.Method = RequestMethod.Get; + request.Uri.Reset(uri); + return _pipeline.SendRequest(request, cancellationToken); + } + + public async ValueTask GetAuthRecordAsync() + { + _authenticationRecord ??= await _interactiveBrowserCredential.AuthenticateAsync(); + return _authenticationRecord; + } + + public AuthenticationRecord GetAuthRecord() + { + _authenticationRecord ??= _interactiveBrowserCredential.Authenticate(); + return _authenticationRecord; + } + } + + public class InteractivePopTokenAuthenticationPolicy : PopTokenAuthenticationPolicy + { + private readonly InteractiveBrowserCredential _interactiveBrowserCredential; + private readonly Func _getAuthRecord; + + public InteractivePopTokenAuthenticationPolicy(InteractiveBrowserCredential credential, string scope, Func getAuthRecord) + : base(credential, scope) + { + _interactiveBrowserCredential = credential; + _getAuthRecord = getAuthRecord; + } + + protected override ValueTask AuthorizeRequestAsync(HttpMessage message) + { + var authRecord = _getAuthRecord(); + if (authRecord != null) + { + _interactiveBrowserCredential.AuthenticateAsync(new TokenRequestContext(new[] { "https://graph.microsoft.com/.default" })).ConfigureAwait(false); + } + + return base.AuthorizeRequestAsync(message); + } + } +} diff --git a/src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs b/src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs new file mode 100644 index 0000000000..f0954c0b32 --- /dev/null +++ b/src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs @@ -0,0 +1,2 @@ +using Azure.Core; +public class PopClientOptions : ClientOptions { } \ No newline at end of file From b28ead856385b3ed72fa900c8f58abce6fcfb3df Mon Sep 17 00:00:00 2001 From: "Taofeek F. Obafemi-Babatunde" Date: Mon, 26 Feb 2024 14:44:43 -0800 Subject: [PATCH 3/5] Filling ou tthe AT PoP skeleton --- .../Utilities/AuthenticationHelpers.cs | 37 ++++++-- .../Utilities/PopClient.cs | 88 ------------------- .../Utilities/PopClientOptions.cs | 2 - 3 files changed, 28 insertions(+), 99 deletions(-) delete mode 100644 src/Authentication/Authentication.Core/Utilities/PopClient.cs delete mode 100644 src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 6a81e3ef46..8026b88eac 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -136,16 +136,32 @@ private static async Task GetInteractiveBrowserCre if (IsATPoPSupported()) { // Logic to implement ATPoP Authentication - var client = new PopClient(interactiveBrowserCredential, authContext, new PopClientOptions() - { - Diagnostics = - { - IsLoggingContentEnabled = true, - LoggedHeaderNames = { "Authorization" } - } + authRecord = await Task.Run(() => + { + var popTokenAuthenticationPolicy = new PopTokenAuthenticationPolicy(interactiveBrowserCredential as ISupportsProofOfPossession, $"https://graph.microsoft.com/.default"); + + var pipelineOptions = new HttpPipelineOptions(new PopClientOptions() + { + Diagnostics = + { + IsLoggingContentEnabled = true, + LoggedHeaderNames = { "Authorization" } + }, + }); + pipelineOptions.PerRetryPolicies.Add(popTokenAuthenticationPolicy); + + var _pipeline = HttpPipelineBuilder.Build(pipelineOptions, new HttpPipelineTransportOptions { ServerCertificateCustomValidationCallback = (_) => true }); + using var request = _pipeline.CreateRequest(); + request.Method = RequestMethod.Get; + request.Uri.Reset(new Uri("https://20.190.132.47/beta/me")); + var response = _pipeline.SendRequest(request, cancellationToken); + var message = new HttpMessage(request, new ResponseClassifier()); + + // Manually invoke the authentication policy's process method + popTokenAuthenticationPolicy.ProcessAsync(message, ReadOnlyMemory.Empty); + // Run the thread in MTA. + return interactiveBrowserCredential.Authenticate(new TokenRequestContext(authContext.Scopes), cancellationToken); }); - //var response = client.Get(new Uri("https://20.190.132.47/beta/me"), CancellationToken.None); - authRecord = client.GetAuthRecord(); } else { @@ -472,4 +488,7 @@ public static Task DeleteAuthRecordAsync() return Task.CompletedTask; } } + internal class PopClientOptions : ClientOptions + { + } } \ No newline at end of file diff --git a/src/Authentication/Authentication.Core/Utilities/PopClient.cs b/src/Authentication/Authentication.Core/Utilities/PopClient.cs deleted file mode 100644 index 7343a6960d..0000000000 --- a/src/Authentication/Authentication.Core/Utilities/PopClient.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.IdentityModel; -using System.Threading; -using System.Threading.Tasks; -using Azure; -using Azure.Core; -using Azure.Core.Pipeline; -using Azure.Identity; -using Azure.Identity.Broker; -using Microsoft.Identity.Client.NativeInterop; - -namespace Microsoft.Graph.PowerShell.Authentication.Core.Utilities -{ - public class PopClient - { - private readonly HttpPipeline _pipeline; - private AuthenticationRecord _authenticationRecord; - private readonly InteractiveBrowserCredential _interactiveBrowserCredential; - - public PopClient(TokenCredential credential, IAuthContext authContext, ClientOptions options = null) - { - //_interactiveBrowserCredential = (InteractiveBrowserCredential)credential; - _interactiveBrowserCredential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialBrokerOptions(WindowHandleUtlities.GetConsoleOrTerminalWindow())); - - if (!(credential is ISupportsProofOfPossession)) - { - throw new ArgumentException("The provided TokenCredential does not support proof of possession.", nameof(credential)); - } - - var pipelineOptions = new HttpPipelineOptions(options); - pipelineOptions.PerRetryPolicies.Add(new InteractivePopTokenAuthenticationPolicy(_interactiveBrowserCredential, "https://graph.microsoft.com/.default", () => _authenticationRecord)); - - _pipeline = HttpPipelineBuilder.Build(pipelineOptions); - } - - public async ValueTask GetAsync(Uri uri, CancellationToken cancellationToken = default) - { - using var request = _pipeline.CreateRequest(); - request.Method = RequestMethod.Get; - request.Uri.Reset(uri); - return await _pipeline.SendRequestAsync(request, cancellationToken).ConfigureAwait(false); - } - - public Response Get(Uri uri, CancellationToken cancellationToken = default) - { - using var request = _pipeline.CreateRequest(); - request.Method = RequestMethod.Get; - request.Uri.Reset(uri); - return _pipeline.SendRequest(request, cancellationToken); - } - - public async ValueTask GetAuthRecordAsync() - { - _authenticationRecord ??= await _interactiveBrowserCredential.AuthenticateAsync(); - return _authenticationRecord; - } - - public AuthenticationRecord GetAuthRecord() - { - _authenticationRecord ??= _interactiveBrowserCredential.Authenticate(); - return _authenticationRecord; - } - } - - public class InteractivePopTokenAuthenticationPolicy : PopTokenAuthenticationPolicy - { - private readonly InteractiveBrowserCredential _interactiveBrowserCredential; - private readonly Func _getAuthRecord; - - public InteractivePopTokenAuthenticationPolicy(InteractiveBrowserCredential credential, string scope, Func getAuthRecord) - : base(credential, scope) - { - _interactiveBrowserCredential = credential; - _getAuthRecord = getAuthRecord; - } - - protected override ValueTask AuthorizeRequestAsync(HttpMessage message) - { - var authRecord = _getAuthRecord(); - if (authRecord != null) - { - _interactiveBrowserCredential.AuthenticateAsync(new TokenRequestContext(new[] { "https://graph.microsoft.com/.default" })).ConfigureAwait(false); - } - - return base.AuthorizeRequestAsync(message); - } - } -} diff --git a/src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs b/src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs deleted file mode 100644 index f0954c0b32..0000000000 --- a/src/Authentication/Authentication.Core/Utilities/PopClientOptions.cs +++ /dev/null @@ -1,2 +0,0 @@ -using Azure.Core; -public class PopClientOptions : ClientOptions { } \ No newline at end of file From 724d274cfa51f09d7c15d6c633738839540b9172 Mon Sep 17 00:00:00 2001 From: "Taofeek F. Obafemi-Babatunde" Date: Wed, 24 Apr 2024 17:00:18 -0700 Subject: [PATCH 4/5] AT PoP Version 1 --- ...Microsoft.Graph.Authentication.Core.csproj | 12 +++--- .../Utilities/AuthenticationHelpers.cs | 37 +++++++++++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index 1dbb7dc287..7cbc04ae08 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -11,12 +11,12 @@ true - - - - - - + + + + + + diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 8026b88eac..923fb66d46 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -15,7 +15,10 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; +using System.Security.Claims; using System.Security.Cryptography.X509Certificates; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -120,7 +123,9 @@ private static async Task GetInteractiveBrowserCre { if (authContext is null) throw new AuthenticationException(ErrorConstants.Message.MissingAuthContext); - var interactiveOptions = IsWamSupported() ? new InteractiveBrowserCredentialBrokerOptions(WindowHandleUtlities.GetConsoleOrTerminalWindow()) : new InteractiveBrowserCredentialOptions(); + var interactiveOptions = IsWamSupported() ? + new InteractiveBrowserCredentialBrokerOptions(WindowHandleUtlities.GetConsoleOrTerminalWindow()) : + new InteractiveBrowserCredentialOptions(); interactiveOptions.ClientId = authContext.ClientId; interactiveOptions.TenantId = authContext.TenantId ?? "common"; interactiveOptions.AuthorityHost = new Uri(GetAuthorityUrl(authContext)); @@ -138,8 +143,21 @@ private static async Task GetInteractiveBrowserCre // Logic to implement ATPoP Authentication authRecord = await Task.Run(() => { + // Creating a Request to retrieve nonce value + string popNonce = null; + var popNonceToken = "nonce=\""; + Uri resourceUri = new Uri("https://canary.graph.microsoft.com/beta/me"); //PPE (https://graph.microsoft-ppe.com) or Canary (https://canary.graph.microsoft.com) or (https://20.190.132.47/beta/me) + HttpClient httpClient = new(new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true }); + HttpResponseMessage response = httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, resourceUri)).Result; + + // Find the WWW-Authenticate header in the response. + var popChallenge = response.Headers.WwwAuthenticate.First(wa => wa.Scheme == "PoP"); + var nonceStart = popChallenge.Parameter.IndexOf(popNonceToken) + popNonceToken.Length; + var nonceEnd = popChallenge.Parameter.IndexOf('"', nonceStart); + popNonce = popChallenge.Parameter.Substring(nonceStart, nonceEnd - nonceStart); + + // Refresh token logic --- start var popTokenAuthenticationPolicy = new PopTokenAuthenticationPolicy(interactiveBrowserCredential as ISupportsProofOfPossession, $"https://graph.microsoft.com/.default"); - var pipelineOptions = new HttpPipelineOptions(new PopClientOptions() { Diagnostics = @@ -151,16 +169,19 @@ private static async Task GetInteractiveBrowserCre pipelineOptions.PerRetryPolicies.Add(popTokenAuthenticationPolicy); var _pipeline = HttpPipelineBuilder.Build(pipelineOptions, new HttpPipelineTransportOptions { ServerCertificateCustomValidationCallback = (_) => true }); + using var request = _pipeline.CreateRequest(); request.Method = RequestMethod.Get; - request.Uri.Reset(new Uri("https://20.190.132.47/beta/me")); - var response = _pipeline.SendRequest(request, cancellationToken); - var message = new HttpMessage(request, new ResponseClassifier()); - + request.Uri.Reset(resourceUri); + // Manually invoke the authentication policy's process method - popTokenAuthenticationPolicy.ProcessAsync(message, ReadOnlyMemory.Empty); + popTokenAuthenticationPolicy.ProcessAsync(new HttpMessage(request, new ResponseClassifier()), ReadOnlyMemory.Empty); + // Refresh token logic --- end + // Run the thread in MTA. - return interactiveBrowserCredential.Authenticate(new TokenRequestContext(authContext.Scopes), cancellationToken); + var popContext = new PopTokenRequestContext(authContext.Scopes, isProofOfPossessionEnabled: true, proofOfPossessionNonce: popNonce, request: request); + //var token = interactiveBrowserCredential.GetToken(popContext, cancellationToken); + return interactiveBrowserCredential.Authenticate(popContext, cancellationToken); }); } else From 73422a09ca937a94140700ea8b0ec1fc73900517 Mon Sep 17 00:00:00 2001 From: "Taofeek F. Obafemi-Babatunde" Date: Tue, 21 May 2024 09:23:46 -0700 Subject: [PATCH 5/5] Making changes to how httpmethod and uri is processed --- .../Common/GraphSession.cs | 5 + .../IGraphRequestProofofPossession.cs | 22 ++++ .../Interfaces/IGraphSession.cs | 1 + ...Microsoft.Graph.Authentication.Core.csproj | 3 +- .../Utilities/AuthenticationHelpers.cs | 108 +++++++++++------- .../Cmdlets/InvokeMgGraphRequest.cs | 2 + .../Common/GraphSessionInitializer.cs | 3 +- .../Handlers/AuthenticationHandler.cs | 34 +++++- .../Authentication/Helpers/HttpHelpers.cs | 4 + .../Models/GraphRequestProofofPossession.cs | 24 ++++ 10 files changed, 161 insertions(+), 45 deletions(-) create mode 100644 src/Authentication/Authentication.Core/Interfaces/IGraphRequestProofofPossession.cs create mode 100644 src/Authentication/Authentication/Models/GraphRequestProofofPossession.cs diff --git a/src/Authentication/Authentication.Core/Common/GraphSession.cs b/src/Authentication/Authentication.Core/Common/GraphSession.cs index 6b893d4d1c..059aa94a4a 100644 --- a/src/Authentication/Authentication.Core/Common/GraphSession.cs +++ b/src/Authentication/Authentication.Core/Common/GraphSession.cs @@ -56,6 +56,11 @@ public class GraphSession : IGraphSession /// public IGraphOption GraphOption { get; set; } + /// + /// Temporarily stores the user's Graph request details such as Method and Uri. Essential as part of the Proof of Possession efforts. + /// + public IGraphRequestProofofPossession GraphRequestProofofPossession { get; set; } + /// /// Represents a collection of Microsoft Graph PowerShell meta-info. /// diff --git a/src/Authentication/Authentication.Core/Interfaces/IGraphRequestProofofPossession.cs b/src/Authentication/Authentication.Core/Interfaces/IGraphRequestProofofPossession.cs new file mode 100644 index 0000000000..1d2d4eef53 --- /dev/null +++ b/src/Authentication/Authentication.Core/Interfaces/IGraphRequestProofofPossession.cs @@ -0,0 +1,22 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ + +using Azure.Core; +using Azure.Identity; +using System; +using System.Net.Http; + +namespace Microsoft.Graph.PowerShell.Authentication +{ + public interface IGraphRequestProofofPossession + { + Uri Uri { get; set; } + HttpMethod HttpMethod { get; set; } + AccessToken AccessToken { get; set; } + string ProofofPossessionNonce { get; set; } + PopTokenRequestContext PopTokenContext { get; set; } + Request Request { get; set; } + InteractiveBrowserCredential BrowserCredential { get; set; } + } +} \ No newline at end of file diff --git a/src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs b/src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs index c7f8ba48c3..88c6fe4373 100644 --- a/src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs +++ b/src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs @@ -12,5 +12,6 @@ public interface IGraphSession IDataStore DataStore { get; set; } IRequestContext RequestContext { get; set; } IGraphOption GraphOption { get; set; } + IGraphRequestProofofPossession GraphRequestProofofPossession { get; set; } } } \ No newline at end of file diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index 7cbc04ae08..8c48e367e9 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -13,7 +13,8 @@ - + + diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 923fb66d46..c07ea5205f 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -1,6 +1,7 @@ // ------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ +using Azure; using Azure.Core; using Azure.Core.Diagnostics; using Azure.Core.Pipeline; @@ -16,6 +17,7 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; @@ -91,7 +93,7 @@ private static bool IsWamSupported() } //Check to see if ATPoP is Supported - private static bool IsATPoPSupported() + public static bool IsATPoPSupported() { return GraphSession.Instance.GraphOption.EnableATPoPForMSGraph; } @@ -131,10 +133,17 @@ private static async Task GetInteractiveBrowserCre interactiveOptions.AuthorityHost = new Uri(GetAuthorityUrl(authContext)); interactiveOptions.TokenCachePersistenceOptions = GetTokenCachePersistenceOptions(authContext); + var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions); + if (IsATPoPSupported()) + { + GraphSession.Instance.GraphRequestProofofPossession.PopTokenContext = CreatePopTokenRequestContext(authContext); + GraphSession.Instance.GraphRequestProofofPossession.BrowserCredential = interactiveBrowserCredential; + } + if (!File.Exists(Constants.AuthRecordPath)) { AuthenticationRecord authRecord; - var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions); + //var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions); if (IsWamSupported()) { // Adding a scenario to account for Access Token Proof of Possession @@ -143,45 +152,9 @@ private static async Task GetInteractiveBrowserCre // Logic to implement ATPoP Authentication authRecord = await Task.Run(() => { - // Creating a Request to retrieve nonce value - string popNonce = null; - var popNonceToken = "nonce=\""; - Uri resourceUri = new Uri("https://canary.graph.microsoft.com/beta/me"); //PPE (https://graph.microsoft-ppe.com) or Canary (https://canary.graph.microsoft.com) or (https://20.190.132.47/beta/me) - HttpClient httpClient = new(new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true }); - HttpResponseMessage response = httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, resourceUri)).Result; - - // Find the WWW-Authenticate header in the response. - var popChallenge = response.Headers.WwwAuthenticate.First(wa => wa.Scheme == "PoP"); - var nonceStart = popChallenge.Parameter.IndexOf(popNonceToken) + popNonceToken.Length; - var nonceEnd = popChallenge.Parameter.IndexOf('"', nonceStart); - popNonce = popChallenge.Parameter.Substring(nonceStart, nonceEnd - nonceStart); - - // Refresh token logic --- start - var popTokenAuthenticationPolicy = new PopTokenAuthenticationPolicy(interactiveBrowserCredential as ISupportsProofOfPossession, $"https://graph.microsoft.com/.default"); - var pipelineOptions = new HttpPipelineOptions(new PopClientOptions() - { - Diagnostics = - { - IsLoggingContentEnabled = true, - LoggedHeaderNames = { "Authorization" } - }, - }); - pipelineOptions.PerRetryPolicies.Add(popTokenAuthenticationPolicy); - - var _pipeline = HttpPipelineBuilder.Build(pipelineOptions, new HttpPipelineTransportOptions { ServerCertificateCustomValidationCallback = (_) => true }); - - using var request = _pipeline.CreateRequest(); - request.Method = RequestMethod.Get; - request.Uri.Reset(resourceUri); - - // Manually invoke the authentication policy's process method - popTokenAuthenticationPolicy.ProcessAsync(new HttpMessage(request, new ResponseClassifier()), ReadOnlyMemory.Empty); - // Refresh token logic --- end - // Run the thread in MTA. - var popContext = new PopTokenRequestContext(authContext.Scopes, isProofOfPossessionEnabled: true, proofOfPossessionNonce: popNonce, request: request); - //var token = interactiveBrowserCredential.GetToken(popContext, cancellationToken); - return interactiveBrowserCredential.Authenticate(popContext, cancellationToken); + //GraphSession.Instance.GraphRequestProofofPossession.AccessToken = interactiveBrowserCredential.GetTokenAsync(GraphSession.Instance.GraphRequestProofofPossession.PopTokenContext, cancellationToken).Result; + return interactiveBrowserCredential.AuthenticateAsync(GraphSession.Instance.GraphRequestProofofPossession.PopTokenContext, cancellationToken); }); } else @@ -508,6 +481,61 @@ public static Task DeleteAuthRecordAsync() File.Delete(Constants.AuthRecordPath); return Task.CompletedTask; } + + public static PopTokenRequestContext CreatePopTokenRequestContext(IAuthContext authContext) + { + // Creating a httpclient that would handle all pop calls + Uri popResourceUri = GraphSession.Instance.GraphRequestProofofPossession.Uri ?? new Uri("https://canary.graph.microsoft.com/beta/me"); //PPE (https://graph.microsoft-ppe.com) or Canary (https://canary.graph.microsoft.com) or (https://20.190.132.47/beta/me) + HttpClient popHttpClient = new(new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true }); + + // Find the WWW-Authenticate header in the response. + var popMethod = GraphSession.Instance.GraphRequestProofofPossession.HttpMethod ?? HttpMethod.Get; + var popResponse = popHttpClient.SendAsync(new HttpRequestMessage(popMethod, popResourceUri)).Result; + var popChallenge = popResponse.Headers.WwwAuthenticate.First(wa => wa.Scheme == "PoP"); + var nonceStart = popChallenge.Parameter.IndexOf("nonce=\"") + "nonce=\"".Length; + var nonceEnd = popChallenge.Parameter.IndexOf('"', nonceStart); + GraphSession.Instance.GraphRequestProofofPossession.ProofofPossessionNonce = popChallenge.Parameter.Substring(nonceStart, nonceEnd - nonceStart); + + // Refresh token logic --- start + var popPipelineOptions = new HttpPipelineOptions(new PopClientOptions() + { + + }); + + var _popPipeline = HttpPipelineBuilder.Build(popPipelineOptions, new HttpPipelineTransportOptions { ServerCertificateCustomValidationCallback = (_) => true }); + GraphSession.Instance.GraphRequestProofofPossession.Request = _popPipeline.CreateRequest(); + GraphSession.Instance.GraphRequestProofofPossession.Request.Method = ConvertToAzureRequestMethod(popMethod); + GraphSession.Instance.GraphRequestProofofPossession.Request.Uri.Reset(popResourceUri); + + // Refresh token logic --- end + var popContext = new PopTokenRequestContext(authContext.Scopes, isProofOfPossessionEnabled: true, proofOfPossessionNonce: GraphSession.Instance.GraphRequestProofofPossession.ProofofPossessionNonce, request: GraphSession.Instance.GraphRequestProofofPossession.Request); + return popContext; + } + public static RequestMethod ConvertToAzureRequestMethod(HttpMethod httpMethod) + { + // Mapping known HTTP methods + switch (httpMethod.Method.ToUpper()) + { + case "GET": + return RequestMethod.Get; + case "POST": + return RequestMethod.Post; + case "PUT": + return RequestMethod.Put; + case "DELETE": + return RequestMethod.Delete; + case "HEAD": + return RequestMethod.Head; + case "OPTIONS": + return RequestMethod.Options; + case "PATCH": + return RequestMethod.Patch; + case "TRACE": + return RequestMethod.Trace; + default: + throw new ArgumentException($"Unsupported HTTP method: {httpMethod.Method}"); + } + } } internal class PopClientOptions : ClientOptions { diff --git a/src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs b/src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs index d211b0c2aa..4d4e177963 100644 --- a/src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs +++ b/src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs @@ -1023,6 +1023,8 @@ private async Task ProcessRecordAsync() try { PrepareSession(); + GraphSession.Instance.GraphRequestProofofPossession.Uri = Uri; + GraphSession.Instance.GraphRequestProofofPossession.HttpMethod = GetHttpMethod(Method); var client = HttpHelpers.GetGraphHttpClient(); ValidateRequestUri(); using (var httpRequestMessage = GetRequest(client, Uri)) diff --git a/src/Authentication/Authentication/Common/GraphSessionInitializer.cs b/src/Authentication/Authentication/Common/GraphSessionInitializer.cs index 4d3d527da1..5d4d0ff5d3 100644 --- a/src/Authentication/Authentication/Common/GraphSessionInitializer.cs +++ b/src/Authentication/Authentication/Common/GraphSessionInitializer.cs @@ -47,7 +47,8 @@ internal static GraphSession CreateInstance(IDataStore dataStore = null) { DataStore = dataStore ?? new DiskDataStore(), RequestContext = new RequestContext(), - GraphOption = graphOptions ?? new GraphOption() + GraphOption = graphOptions ?? new GraphOption(), + GraphRequestProofofPossession = new GraphRequestProofofPossession() }; } /// diff --git a/src/Authentication/Authentication/Handlers/AuthenticationHandler.cs b/src/Authentication/Authentication/Handlers/AuthenticationHandler.cs index e57d74186b..c326264a71 100644 --- a/src/Authentication/Authentication/Handlers/AuthenticationHandler.cs +++ b/src/Authentication/Authentication/Handlers/AuthenticationHandler.cs @@ -3,8 +3,13 @@ // ------------------------------------------------------------------------------ +using Azure.Core; +using Azure.Identity; +using Azure.Identity.Broker; using Microsoft.Graph.Authentication; +using Microsoft.Graph.PowerShell.Authentication.Core.Utilities; using Microsoft.Graph.PowerShell.Authentication.Extensions; +using Microsoft.Identity.Client; using System; using System.Collections.Generic; using System.Linq; @@ -63,9 +68,24 @@ private async Task AuthenticateRequestAsync(HttpRequestMessage httpRequestMessag { if (AuthenticationProvider != null) { - var accessToken = await AuthenticationProvider.GetAuthorizationTokenAsync(httpRequestMessage.RequestUri, additionalAuthenticationContext, cancellationToken: cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrEmpty(accessToken)) - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(BearerAuthenticationScheme, accessToken); + if (AuthenticationHelpers.IsATPoPSupported()) + { + GraphSession.Instance.GraphRequestProofofPossession.Request.Method = AuthenticationHelpers.ConvertToAzureRequestMethod(httpRequestMessage.Method); + GraphSession.Instance.GraphRequestProofofPossession.Request.Uri.Reset(httpRequestMessage.RequestUri); + foreach (var header in httpRequestMessage.Headers) + { + GraphSession.Instance.GraphRequestProofofPossession.Request.Headers.Add(header.Key, header.Value.First()); + } + + var accessToken = GraphSession.Instance.GraphRequestProofofPossession.BrowserCredential.GetTokenAsync(GraphSession.Instance.GraphRequestProofofPossession.PopTokenContext, cancellationToken).Result; + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Pop", accessToken.Token); + } + else + { + var accessToken = await AuthenticationProvider.GetAuthorizationTokenAsync(httpRequestMessage.RequestUri, additionalAuthenticationContext, cancellationToken: cancellationToken).ConfigureAwait(false); + if (!string.IsNullOrEmpty(accessToken)) + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(BearerAuthenticationScheme, accessToken); + } } } @@ -87,6 +107,14 @@ private async Task SendRetryAsync(HttpResponseMessage httpR } await DrainAsync(httpResponseMessage).ConfigureAwait(false); + if (AuthenticationHelpers.IsATPoPSupported()) + { + var popChallenge = httpResponseMessage.Headers.WwwAuthenticate.First(wa => wa.Scheme == "PoP"); + var nonceStart = popChallenge.Parameter.IndexOf("nonce=\"") + "nonce=\"".Length; + var nonceEnd = popChallenge.Parameter.IndexOf('"', nonceStart); + GraphSession.Instance.GraphRequestProofofPossession.ProofofPossessionNonce = popChallenge.Parameter.Substring(nonceStart, nonceEnd - nonceStart); + } + // Authenticate request using auth provider await AuthenticateRequestAsync(newRequest, additionalRequestInfo, cancellationToken).ConfigureAwait(false); httpResponseMessage = await base.SendAsync(newRequest, cancellationToken); diff --git a/src/Authentication/Authentication/Helpers/HttpHelpers.cs b/src/Authentication/Authentication/Helpers/HttpHelpers.cs index cfd9252f5f..01394d120c 100644 --- a/src/Authentication/Authentication/Helpers/HttpHelpers.cs +++ b/src/Authentication/Authentication/Helpers/HttpHelpers.cs @@ -1,16 +1,20 @@ // ------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ +using Azure.Core; using Microsoft.Graph.Authentication; using Microsoft.Graph.PowerShell.Authentication.Core.Interfaces; using Microsoft.Graph.PowerShell.Authentication.Core.Utilities; using Microsoft.Graph.PowerShell.Authentication.Handlers; +using Microsoft.Identity.Client; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; namespace Microsoft.Graph.PowerShell.Authentication.Helpers { diff --git a/src/Authentication/Authentication/Models/GraphRequestProofofPossession.cs b/src/Authentication/Authentication/Models/GraphRequestProofofPossession.cs new file mode 100644 index 0000000000..2c12f8c368 --- /dev/null +++ b/src/Authentication/Authentication/Models/GraphRequestProofofPossession.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ + +using Azure.Core; +using Azure.Identity; +using System; +using System.IO; +using System.Net.Http; + +namespace Microsoft.Graph.PowerShell.Authentication +{ + internal class GraphRequestProofofPossession : IGraphRequestProofofPossession + { + public Uri Uri { get; set; } + public HttpMethod HttpMethod { get; set; } + public AccessToken AccessToken { get; set; } + public string ProofofPossessionNonce { get; set; } + public PopTokenRequestContext PopTokenContext { get; set; } + public Request Request { get; set; } + public InteractiveBrowserCredential BrowserCredential { get; set; } + } + +} \ No newline at end of file