Skip to content
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

AuthHandshakeMessageHandler: sync behavior with other clients. #43491

Merged
merged 4 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading