From bcb1f2cd1d67a6f5ed25554c10a84a33a6251a72 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] 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 6aeb7e32f0e..3068135b3aa 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 bff781160c9..1dbb7dc2875 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 3d004336109..6a81e3ef462 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 00000000000..7343a6960d7 --- /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 00000000000..f0954c0b32f --- /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