-
Notifications
You must be signed in to change notification settings - Fork 406
Add Pop support to wwwAuthenticateParameters #3436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 12 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
daaa43d
Add new api to gather all wwwAuthenticateParameters
7108c00
refactor
4ef405b
Request uri fix
51f71f1
Add additional tests.
ff29f70
Rename scheme to authScheme
e790052
Refactoring api
bd59b3d
Refactoring.
cab97ae
Refactoring to use Dictionary return type
6677c1f
Merge remote-tracking branch 'origin/main' into trwalke/wwwAuthentica…
37d9c43
Adding Authentication header parser and support for authentication info
bae9643
Refactoring
af2f19e
Merge remote-tracking branch 'origin/main' into trwalke/wwwAuthentica…
eb8032f
Apply suggestions from code review
trwalke 8949f55
Addressing PR Feedback.
8c46a01
Refactoring, more tests
e87abff
Build fix
4f4cc68
Adding additional tests
264921f
Adding NTLM support
8fbd0e4
Apply suggestions from code review
trwalke 6a58dac
Update src/client/Microsoft.Identity.Client/WwwAuthenticateParameters.cs
trwalke 9b31775
Update src/client/Microsoft.Identity.Client/WwwAuthenticateParameters.cs
trwalke b068acc
Recfactoring
384cd11
Merge remote-tracking branch 'origin/main' into trwalke/wwwAuthentica…
4b56fb2
resolving build errors
ddbadf8
resolving tests
0867daa
Updating parameter parsing
b55f3c2
Updating parsing logic
1a08dda
Clean up
fd6733a
Pr Feedback
9626ec3
test update
a1cdedf
Update src/client/Microsoft.Identity.Client/WwwAuthenticateParameters.cs
trwalke 37b1ab7
Adding additional tests
d6a1e1b
Refactoring tests.
1e9ce55
Merge remote-tracking branch 'origin/pmaytak/net6-only' into trwalke/…
c3addbe
Refactoring
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
154 changes: 154 additions & 0 deletions
154
src/client/Microsoft.Identity.Client/Http/Headers/AuthenticationHeaderParser.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Net.Http; | ||
| using System.Net.Http.Headers; | ||
| using System.Text; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Identity.Client.PlatformsCommon.Factories; | ||
| using Microsoft.Identity.Client.Utils; | ||
|
|
||
| namespace Microsoft.Identity.Client | ||
| { | ||
| /// <summary> | ||
| /// Parsed authentication headers to retreve header values from HttpResponseHeaders. | ||
| /// </summary> | ||
| public class AuthenticationHeaderParser | ||
| { | ||
| /// <summary> | ||
| /// Parameters returned by the WWW-Authenticate header. This allows for dynamic | ||
| /// scenarios such as claim challenge, CAE, CA auth context. | ||
| /// See https://aka.ms/msal-net/wwwAuthenticate. | ||
| /// </summary> | ||
| public IReadOnlyList<WwwAuthenticateParameters> ParsedWwwAuthenticateParameters { get; private set; } | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// <summary> | ||
| /// Parameters returned by the Authenticatin-Info header. | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| /// This allows for authentication scenarios such as Proof-Of-Posession. | ||
| /// </summary> | ||
| public AuthenticationInfoParameters ParsedAuthenticationInfoParameters { get; private set; } | ||
|
|
||
| /// <summary> | ||
| /// Nonce parsed from HttpResponseHeaders | ||
| /// </summary> | ||
| public string Nonce { get; private set; } | ||
|
|
||
| /// <summary> | ||
| /// Creates the authenticate parameters by attempting to call the resource unauthenticated, and analyzing the response. | ||
| /// </summary> | ||
| /// <param name="resourceUri">URI of the resource.</param> | ||
| /// <returns></returns> | ||
| public static async Task<AuthenticationHeaderParser> ParseAuthenticationHeadersAsync(string resourceUri) | ||
| { | ||
| return await ParseAuthenticationHeadersAsync(resourceUri, new CancellationToken()).ConfigureAwait(false); | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates the authenticate parameters by attempting to call the resource unauthenticated, and analyzing the response. | ||
| /// </summary> | ||
| /// <param name="resourceUri">URI of the resource.</param> | ||
| /// <param name="cancellationToken">The cancellation token to cancel operation.</param> | ||
| /// <returns></returns> | ||
| public static async Task<AuthenticationHeaderParser> ParseAuthenticationHeadersAsync(string resourceUri, CancellationToken cancellationToken) | ||
| { | ||
| return await ParseAuthenticationHeadersAsync(resourceUri, cancellationToken, GetHttpClient()).ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates the authenticate parameters by attempting to call the resource unauthenticated, and analyzing the response. | ||
| /// </summary> | ||
| /// <param name="resourceUri">URI of the resource.</param> | ||
| /// <param name="cancellationToken">The cancellation token to cancel operation.</param> | ||
| /// <param name="httpClient">Instance of <see cref="HttpClient"/> to make the request with.</param> | ||
| /// <returns></returns> | ||
| /// <exception cref="ArgumentNullException"></exception> | ||
| public static async Task<AuthenticationHeaderParser> ParseAuthenticationHeadersAsync(string resourceUri, CancellationToken cancellationToken, HttpClient httpClient) | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| { | ||
| if (httpClient is null) | ||
| { | ||
| throw new ArgumentNullException(nameof(httpClient)); | ||
| } | ||
| if (string.IsNullOrWhiteSpace(resourceUri)) | ||
| { | ||
| throw new ArgumentNullException(nameof(resourceUri)); | ||
| } | ||
|
|
||
| // call this endpoint and see what the header says and return that | ||
| HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(resourceUri, cancellationToken).ConfigureAwait(false); | ||
|
|
||
| return ParseAuthenticationHeaders(httpResponseMessage.Headers); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a parsed set of parameters from the provided HttpResponseHeaders. | ||
| /// </summary> | ||
| /// <param name="httpResponseHeaders"></param> | ||
| /// <remarks>For known values, such as the nonce used for Proof-of-Possession, the parser will first chack for in the WWW-Authenticate headers | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| /// If it cannot find it, it will then check the Authentication-Info parameters for the value.</remarks> | ||
| /// <returns></returns> | ||
| public static AuthenticationHeaderParser ParseAuthenticationHeaders(HttpResponseHeaders httpResponseHeaders) | ||
| { | ||
| AuthenticationHeaderParser authenticationHeaderParser = new AuthenticationHeaderParser(); | ||
| string serverNonce = null; | ||
|
|
||
| //Check for WWW-AuthenticateHeaders | ||
| if (httpResponseHeaders.WwwAuthenticate.Count != 0) | ||
| { | ||
| var WwwParameters = WwwAuthenticateParameters.CreateFromAuthenticateHeaders(httpResponseHeaders); | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
|
|
||
| if (WwwParameters.Any(parameter => parameter.AuthScheme == "PoP")) | ||
| { | ||
| serverNonce = WwwParameters.Where(parameter => parameter.AuthScheme == "PoP").Single().ServerNonce; | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| authenticationHeaderParser.ParsedWwwAuthenticateParameters = WwwParameters; | ||
| } | ||
| else | ||
| { | ||
| authenticationHeaderParser.ParsedWwwAuthenticateParameters = new List<WwwAuthenticateParameters>(); | ||
| } | ||
|
|
||
| //If no WWW-AuthenticateHeaders exist, attempt to parse AuthenticationInfo headers instead | ||
| AuthenticationInfoParameters authenticationInfoParameters = AuthenticationInfoParameters.CreateFromHeaders(httpResponseHeaders); | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| authenticationHeaderParser.ParsedAuthenticationInfoParameters = authenticationInfoParameters; | ||
|
|
||
| //If server nonce is not acquired from WWW-Authenticate headers, use next nonce from Authentication-Info parameters. | ||
| authenticationHeaderParser.Nonce = serverNonce?? authenticationInfoParameters.NextNonce; | ||
|
|
||
| return authenticationHeaderParser; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Created an HttpClient | ||
| /// </summary> | ||
| internal static HttpClient GetHttpClient() | ||
| { | ||
| var httpClientFactory = PlatformProxyFactory.CreatePlatformProxy(null).CreateDefaultHttpClientFactory(); | ||
| return httpClientFactory.GetHttpClient(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Extracts a key value pair from an expression of the form a=b | ||
| /// </summary> | ||
| /// <param name="assignment">assignment</param> | ||
| /// <returns>Key Value pair</returns> | ||
| internal static KeyValuePair<string, string> ExtractKeyValuePair(string assignment) | ||
| { | ||
| string[] segments = CoreHelpers.SplitWithQuotes(assignment, '=') | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| .Select(s => s.Trim().Trim('"')) | ||
| .ToArray(); | ||
|
|
||
| if (segments.Length != 2) | ||
| { | ||
| throw new ArgumentException(nameof(assignment), $"{assignment} isn't of the form a=b"); | ||
| } | ||
|
|
||
| return new KeyValuePair<string, string>(segments[0], segments[1]); | ||
| } | ||
| } | ||
|
|
||
| } | ||
57 changes: 57 additions & 0 deletions
57
src/client/Microsoft.Identity.Client/Http/Headers/AuthenticationInfoParameters.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Net.Http.Headers; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Identity.Client.Utils; | ||
| using Microsoft.Identity.Json.Linq; | ||
|
|
||
| namespace Microsoft.Identity.Client | ||
| { | ||
| /// <summary> | ||
|
pmaytak marked this conversation as resolved.
Outdated
|
||
| /// | ||
| /// </summary> | ||
| public class AuthenticationInfoParameters | ||
| { | ||
| private const string _authenticationInfoKey = "Authentication-Info"; | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| /// <summary> | ||
| /// | ||
| /// </summary> | ||
| public string NextNonce { get; private set; } | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// <summary> | ||
| /// | ||
| /// </summary> | ||
| /// <param name="respnseHeaders"></param> | ||
| /// <returns></returns> | ||
| public static AuthenticationInfoParameters CreateFromHeaders(HttpResponseHeaders respnseHeaders) | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| { | ||
| AuthenticationInfoParameters parameters = new AuthenticationInfoParameters(); | ||
|
|
||
| if (respnseHeaders.Any(header => header.Key == _authenticationInfoKey)) | ||
|
trwalke marked this conversation as resolved.
Outdated
|
||
| { | ||
| var authInfoValue = respnseHeaders.Where(header => header.Key == _authenticationInfoKey).Single().Value.FirstOrDefault(); | ||
|
trwalke marked this conversation as resolved.
Outdated
trwalke marked this conversation as resolved.
Outdated
|
||
|
|
||
| var AuthValuesSplit = authInfoValue.Split(new char[] { ' ' }, 2); | ||
|
trwalke marked this conversation as resolved.
Outdated
trwalke marked this conversation as resolved.
Outdated
|
||
|
|
||
| var paramValues = CoreHelpers.SplitWithQuotes(AuthValuesSplit[1], ',') | ||
| .Select(v => AuthenticationHeaderParser.ExtractKeyValuePair(v.Trim())) | ||
| .ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase); | ||
|
|
||
| string value; | ||
|
|
||
| if (paramValues.TryGetValue("nextnonce", out value)) | ||
|
trwalke marked this conversation as resolved.
Outdated
trwalke marked this conversation as resolved.
Outdated
trwalke marked this conversation as resolved.
Outdated
|
||
| { | ||
| parameters.NextNonce = value; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| return parameters; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.