Skip to content

Commit

Permalink
AuthHandshakeMessageHandler: sync behavior with other clients. (#43491)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmds authored Oct 7, 2024
1 parent 0a1cd4f commit 0b3ce51
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,19 @@ private static bool TryParseAuthenticationInfo(HttpResponseMessage msg, [NotNull
if (header.Scheme is not null)
{
scheme = header.Scheme;
var keyValues = ParseBearerArgs(header.Parameter);
if (keyValues is null)
{
return false;
}

if (header.Scheme.Equals(BasicAuthScheme, StringComparison.OrdinalIgnoreCase))
{
return TryParseBasicAuthInfo(keyValues, msg.RequestMessage!.RequestUri!, out bearerAuthInfo);
bearerAuthInfo = null;
return true;
}
else if (header.Scheme.Equals(BearerAuthScheme, StringComparison.OrdinalIgnoreCase))
{
var keyValues = ParseBearerArgs(header.Parameter);
if (keyValues is null)
{
return false;
}
return TryParseBearerAuthInfo(keyValues, out bearerAuthInfo);
}
else
Expand Down Expand Up @@ -110,12 +111,6 @@ static bool TryParseBearerAuthInfo(Dictionary<string, string> authValues, [NotNu
}
}

static bool TryParseBasicAuthInfo(Dictionary<string, string> authValues, Uri requestUri, out AuthInfo? authInfo)
{
authInfo = null;
return true;
}

static Dictionary<string, string>? ParseBearerArgs(string? bearerHeaderArgs)
{
if (bearerHeaderArgs is null)
Expand Down Expand Up @@ -159,7 +154,6 @@ public DateTimeOffset ResolvedExpiration
/// </summary>
private async Task<(AuthenticationHeaderValue, DateTimeOffset)?> GetAuthenticationAsync(string registry, string scheme, AuthInfo? bearerAuthInfo, CancellationToken cancellationToken)
{

DockerCredentials? privateRepoCreds;
// Allow overrides for auth via environment variables
if (GetDockerCredentialsFromEnvironment(_registryMode) is (string credU, string credP))
Expand All @@ -180,14 +174,20 @@ public DateTimeOffset ResolvedExpiration
{
Debug.Assert(bearerAuthInfo is not null);

var authenticationValueAndDuration = await TryOAuthPostAsync(privateRepoCreds, bearerAuthInfo, cancellationToken).ConfigureAwait(false);
if (authenticationValueAndDuration is not null)
// Obtain a Bearer token, when the credentials are:
// - an identity token: use it for OAuth
// - a username/password: use them for Basic auth, and fall back to OAuth

if (string.IsNullOrWhiteSpace(privateRepoCreds.IdentityToken))
{
return authenticationValueAndDuration;
var authenticationValueAndDuration = await TryTokenGetAsync(privateRepoCreds, bearerAuthInfo, cancellationToken).ConfigureAwait(false);
if (authenticationValueAndDuration is not null)
{
return authenticationValueAndDuration;
}
}

authenticationValueAndDuration = await TryTokenGetAsync(privateRepoCreds, bearerAuthInfo, cancellationToken).ConfigureAwait(false);
return authenticationValueAndDuration;
return await TryOAuthPostAsync(privateRepoCreds, bearerAuthInfo, cancellationToken).ConfigureAwait(false);
}
else
{
Expand Down Expand Up @@ -293,8 +293,7 @@ internal static (string credU, string credP)? GetDockerCredentialsFromEnvironmen
if (!postResponse.IsSuccessStatusCode)
{
await postResponse.LogHttpResponseAsync(_logger, cancellationToken).ConfigureAwait(false);
//return null to try HTTP GET instead
return null;
return null; // try next method
}
_logger.LogTrace("Received '{statuscode}'.", postResponse.StatusCode);
TokenResponse? tokenResponse = JsonSerializer.Deserialize<TokenResponse>(postResponse.Content.ReadAsStream(cancellationToken));
Expand All @@ -306,8 +305,7 @@ internal static (string credU, string credP)? GetDockerCredentialsFromEnvironmen
else
{
_logger.LogTrace(Resource.GetString(nameof(Strings.CouldntDeserializeJsonToken)));
// logging and returning null to try HTTP GET instead
return null;
return null; // try next method
}
}

Expand All @@ -318,9 +316,7 @@ internal static (string credU, string credP)? GetDockerCredentialsFromEnvironmen
{
// this doesn't seem to be called out in the spec, but actual username/password auth information should be converted into Basic auth here,
// even though the overall Scheme we're authenticating for is Bearer
var header = privateRepoCreds.Username == "<token>"
? new AuthenticationHeaderValue(BearerAuthScheme, privateRepoCreds.Password)
: new AuthenticationHeaderValue(BasicAuthScheme, Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
var header = new AuthenticationHeaderValue(BasicAuthScheme, Convert.ToBase64String(Encoding.ASCII.GetBytes($"{privateRepoCreds.Username}:{privateRepoCreds.Password}")));
var builder = new UriBuilder(new Uri(bearerAuthInfo.Realm));

_logger.LogTrace("Attempting to authenticate on {uri} using GET.", bearerAuthInfo.Realm);
Expand All @@ -340,7 +336,8 @@ internal static (string credU, string credP)? GetDockerCredentialsFromEnvironmen
using var tokenResponse = await base.SendAsync(message, cancellationToken).ConfigureAwait(false);
if (!tokenResponse.IsSuccessStatusCode)
{
throw new UnableToAccessRepositoryException(_registryName);
await tokenResponse.LogHttpResponseAsync(_logger, cancellationToken).ConfigureAwait(false);
return null; // try next method
}

TokenResponse? token = JsonSerializer.Deserialize<TokenResponse>(tokenResponse.Content.ReadAsStream(cancellationToken));
Expand Down Expand Up @@ -412,7 +409,8 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
request.Headers.Authorization = authHeader;
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
return response;

throw new UnableToAccessRepositoryException(_registryName);
}
else
{
Expand Down
Loading

0 comments on commit 0b3ce51

Please sign in to comment.