Skip to content

Commit

Permalink
fix: Update auth resolver to correctly handle multiple schemes scenario
Browse files Browse the repository at this point in the history
  • Loading branch information
dscpinheiro committed Jan 24, 2025
1 parent e3d8781 commit fe09cd1
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Amazon.Runtime.Endpoints;
using Amazon.Runtime.Identity;
using Amazon.Runtime.Internal.Auth;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -51,7 +52,7 @@ protected void PreInvoke(IExecutionContext executionContext)
var authOptions = ResolveAuthOptions(executionContext);
if (authOptions == null || authOptions.Count == 0)
{
throw new AmazonClientException($"No valid authentication schemes defined for ${executionContext.RequestContext.RequestName}");
throw new AmazonClientException($"No valid authentication schemes defined for {executionContext.RequestContext.RequestName}");
}

var clientConfig = executionContext.RequestContext.ClientConfig;
Expand All @@ -65,40 +66,64 @@ protected void PreInvoke(IExecutionContext executionContext)
continue;
}

executionContext.RequestContext.Signer = GetSigner(scheme);

if ((scheme is AwsV4aAuthScheme || scheme is AwsV4AuthScheme) && clientConfig.DefaultAWSCredentials != null)
try
{
// We can use DefaultAWSCredentials if it was set by the user for these schemes.
executionContext.RequestContext.Identity = clientConfig.DefaultAWSCredentials;
break;
}
executionContext.RequestContext.Signer = GetSigner(scheme);

if (scheme is BearerAuthScheme && clientConfig.AWSTokenProvider != null)
{
// If the legacy token provider is set, we'll use it to resolve the identity.
#if NETFRAMEWORK
var resolvedToken = clientConfig.AWSTokenProvider.TryResolveToken(out var token);
if (!resolvedToken)
if ((scheme is AwsV4aAuthScheme || scheme is AwsV4AuthScheme) && clientConfig.DefaultAWSCredentials != null)
{
continue;
// We can use DefaultAWSCredentials if it was set by the user for these schemes.
executionContext.RequestContext.Identity = clientConfig.DefaultAWSCredentials;
return;
}

if (scheme is BearerAuthScheme && clientConfig.AWSTokenProvider != null)
{
// If the legacy token provider is set, we'll use it to resolve the identity.
#if NETFRAMEWORK
var resolvedToken = clientConfig.AWSTokenProvider.TryResolveToken(out var token);
if (!resolvedToken)
{
continue;
}
#else
var resolvedToken = clientConfig.AWSTokenProvider.TryResolveTokenAsync().GetAwaiter().GetResult();
if (!resolvedToken.Success)
var resolvedToken = clientConfig.AWSTokenProvider.TryResolveTokenAsync().GetAwaiter().GetResult();
if (!resolvedToken.Success)
{
continue;
}

var token = resolvedToken.Value;
#endif

executionContext.RequestContext.Identity = token;
return;
}

var identityResolver = scheme.GetIdentityResolver(clientConfig.IdentityResolverConfiguration);
executionContext.RequestContext.Identity = identityResolver.ResolveIdentity();

if (executionContext.RequestContext.Identity != null)
{
return;
}
}
catch (Exception)
{
// If there are multiple authentication schemes and we cannot resolve the identity for some reason (e.g. the CRT is
// required for SigV4A signing), we'll attempt the next option before returning.
if (authOptions.Count > 1)
{
continue;
}

var token = resolvedToken.Value;
#endif

executionContext.RequestContext.Identity = token;
break;
throw;
}
}

var identityResolver = scheme.GetIdentityResolver(clientConfig.IdentityResolverConfiguration);
executionContext.RequestContext.Identity = identityResolver.ResolveIdentity();
if (executionContext.RequestContext.Identity == null)
{
throw new AmazonClientException($"Could not determine which authentication scheme to use for {executionContext.RequestContext.RequestName}");
}
}

Expand All @@ -107,7 +132,7 @@ protected async Task PreInvokeAsync(IExecutionContext executionContext)
var authOptions = ResolveAuthOptions(executionContext);
if (authOptions == null || authOptions.Count == 0)
{
throw new AmazonClientException($"No valid authentication schemes defined for ${executionContext.RequestContext.RequestName}");
throw new AmazonClientException($"No valid authentication schemes defined for {executionContext.RequestContext.RequestName}");
}

var clientConfig = executionContext.RequestContext.ClientConfig;
Expand All @@ -122,35 +147,59 @@ protected async Task PreInvokeAsync(IExecutionContext executionContext)
continue;
}

executionContext.RequestContext.Signer = GetSigner(scheme);

if ((scheme is AwsV4aAuthScheme || scheme is AwsV4AuthScheme) && clientConfig.DefaultAWSCredentials != null)
try
{
// We can use DefaultAWSCredentials if it was set by the user for these schemes.
executionContext.RequestContext.Identity = clientConfig.DefaultAWSCredentials;
break;
}
executionContext.RequestContext.Signer = GetSigner(scheme);

if (scheme is BearerAuthScheme && clientConfig.AWSTokenProvider != null)
{
// If the legacy token provider is set, we'll use it to resolve the identity.
var resolvedToken = await clientConfig.AWSTokenProvider
.TryResolveTokenAsync(cancellationToken)
if ((scheme is AwsV4aAuthScheme || scheme is AwsV4AuthScheme) && clientConfig.DefaultAWSCredentials != null)
{
// We can use DefaultAWSCredentials if it was set by the user for these schemes.
executionContext.RequestContext.Identity = clientConfig.DefaultAWSCredentials;
return;
}

if (scheme is BearerAuthScheme && clientConfig.AWSTokenProvider != null)
{
// If the legacy token provider is set, we'll use it to resolve the identity.
var resolvedToken = await clientConfig.AWSTokenProvider
.TryResolveTokenAsync(cancellationToken)
.ConfigureAwait(false);

if (!resolvedToken.Success)
{
continue;
}

executionContext.RequestContext.Identity = resolvedToken.Value;
return;
}

var identityResolver = scheme.GetIdentityResolver(clientConfig.IdentityResolverConfiguration);
executionContext.RequestContext.Identity = await identityResolver
.ResolveIdentityAsync(cancellationToken)
.ConfigureAwait(false);

if (!resolvedToken.Success)
if (executionContext.RequestContext.Identity != null)
{
return;
}
}
catch (Exception)
{
// If there are multiple authentication schemes and we cannot resolve the identity for some reason (e.g. the CRT is
// required for SigV4A signing), we'll attempt the next option before returning.
if (authOptions.Count > 1)
{
continue;
}

executionContext.RequestContext.Identity = resolvedToken.Value;
break;
throw;
}
}

var identityResolver = scheme.GetIdentityResolver(clientConfig.IdentityResolverConfiguration);
executionContext.RequestContext.Identity = await identityResolver
.ResolveIdentityAsync(cancellationToken)
.ConfigureAwait(false);
if (executionContext.RequestContext.Identity == null)
{
throw new AmazonClientException($"Could not determine which authentication scheme to use for {executionContext.RequestContext.RequestName}");
}
}

Expand Down
48 changes: 7 additions & 41 deletions sdk/test/UnitTests/Custom/Runtime/BearerTokenSignerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,49 +230,16 @@ public async Task SignerShouldOverrideExistingHeaderAsync()
[TestCategory("Runtime")]
public void SignerRequiresAToken()
{
var mockConfig = new MockClientConfig
{
AWSTokenProvider = new AWSTokenProviderChain() // chain is empty
};

var pipeline = CreateMockPipeline();
var context = CreateTestContext(new BearerTokenSigner(), null, mockConfig);

Exception exception = null;

try
{
pipeline.InvokeSync(context);
}
catch (Exception e)
{
exception = e;
}

Assert.IsNotNull(exception);
Assert.IsInstanceOfType(exception, typeof(AmazonClientException));
Assert.AreEqual(exception.Message, "No Token found. Operation requires a Bearer token.");
}

#if AWS_ASYNC_API
[TestMethod]
[TestCategory("UnitTest")]
[TestCategory("Runtime")]
public async Task SignerRequiresATokenAsync()
{
var mockConfig = new MockClientConfig
{
AWSTokenProvider = new AWSTokenProviderChain() // chain is empty
};

var pipeline = CreateMockPipeline();
var context = CreateTestContext(new BearerTokenSigner(), null, mockConfig);

var signer = new BearerTokenSigner();
var invalidTokenIdentity = new AWSToken();

var mockRequest = new Mock<IRequest>();
mockRequest.Setup(x => x.Endpoint).Returns(new Uri("https://example.com"));
Exception exception = null;

try
{
await pipeline.InvokeAsync<AmazonWebServiceResponse>(context);
signer.Sign(mockRequest.Object, clientConfig: null, metrics: null, identity: invalidTokenIdentity);
}
catch (Exception e)
{
Expand All @@ -281,9 +248,8 @@ public async Task SignerRequiresATokenAsync()

Assert.IsNotNull(exception);
Assert.IsInstanceOfType(exception, typeof(AmazonClientException));
Assert.AreEqual(exception.Message, "No Token found. Operation requires a Bearer token.");
Assert.AreEqual("No Token found. Operation requires a Bearer token.", exception.Message);
}
#endif

private void AssertAuthorizationHeaderIs(IExecutionContext context, string expected)
{
Expand Down

0 comments on commit fe09cd1

Please sign in to comment.