AWS SDK for .NET

De Banane Atomic
Aller à la navigationAller à la recherche

Config

This file contains the profiles.

∼/.aws/config
[default]
region = eu-central-1

[profile Profile1]
sso_start_url  = https://my-sso-portal.awsapps.com/start
sso_region     = us-west-1
sso_account_id = 111122223333
sso_role_name  = SampleRole
region         = eu-central-1
output         = yaml-stream
services       = local-dynamodb

[services local-dynamodb]
dynamodb = 
  endpoint_url = http://localhost:8000
Ps.svg
aws sso login --profile Profile1

Define the AWS_PROFILE in an env var while starting the project.

Properties\launchSettings.json
{
  "profiles": {
    "MyProfile1": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "AWS_PROFILE": "Profile1"
      },
      "applicationUrl": "https://localhost:5001;http://localhost:5000"
    }
}

Credentials

This file contains credentials linked to profiles.

∼/.aws/credentials
[default]
aws_access_key_id     = ...
aws_secret_access_key = ...
aws_session_token     = ...

[Profile1]
key = value

Example .NET applications

Cs.svg
var ssoCreds = this.LoadSsoCredentials("Profile1");
var token = new AmazonSecurityTokenServiceClient(ssoCreds);

var caller = await token.GetCallerIdentityAsync(new GetCallerIdentityRequest());
this.userId = caller?.UserId.Substring(caller.UserId.IndexOf(":") + 1) ?? string.Empty;

var userNames = await this.GetIamUserNamesAsync(ssoCreds);
var bucketNames = await this.GetS3BucketNames(ssoCreds);

private AWSCredentials LoadSsoCredentials(string profile)
{
    var chain = new CredentialProfileStoreChain();
    if (!chain.TryGetAWSCredentials(profile, out var credentials))
    {
        errors.Add($"Failed to find the {profile} profile");
    }

    // set ClientName and launch a browser window that prompts the SSO user to complete an SSO login
    // if the session doesn't already have a valid SSO token.
    if (credentials is SSOAWSCredentials ssoCredentials)
    {
        ssoCredentials.Options.ClientName = "Example-SSO-App";
        ssoCredentials.Options.SsoVerificationCallback = args =>
        {
            Process.Start(new ProcessStartInfo
            {
                FileName = args.VerificationUriComplete,
                UseShellExecute = true
            });
        };
    }

    return credentials;
}

private async Task<IReadOnlyCollection<string>> GetIamUserNamesAsync(AWSCredentials ssoCreds)
{
    var iamClient = new AmazonIdentityManagementServiceClient(ssoCreds);
    var listResponse = await iamClient.ListUsersAsync();
    return listResponse.Users.Select(x => x.UserName).ToList();
}

private async Task<IReadOnlyCollection<string>> GetS3BucketNames(AWSCredentials ssoCreds)
{
    var s3Client = new AmazonS3Client(ssoCreds);
    // Amazon.Runtime.AmazonClientException: 'No RegionEndpoint or ServiceURL configured
    // define a default profile in config with a region
    var listResponse = await s3Client.ListBucketsAsync();
    return listResponse.Buckets.Select(x => x.BucketName).ToList();
}

Install the following nuget packages: AWSSDK.Core AWSSDK.SecurityToken AWSSDK.SSO AWSSDK.SSOOIDC
For IAM users: AWSSDK.IdentityManagement
For S3 buckets: AWSSDK.S3

Load .NET configuration from Secrets Manager

AmazonSecretsManagerConfigurationProvider.cs
public class AmazonSecretsManagerConfigurationProvider : ConfigurationProvider
{
    private readonly string secretName;

    public AmazonSecretsManagerConfigurationProvider(string secretName)
    {
        this.secretName = secretName;
    }

    public override void Load()
    {
        var secret = GetSecret();
        Data = JsonSerializer.Deserialize<Dictionary<string, string>>(secret)!;
    }

    private string GetSecret()
    {
        var request = new GetSecretValueRequest
        {
            SecretId = this.secretName
        };

        using (var client = new AmazonSecretsManagerClient())
        {
            var response = client.GetSecretValueAsync(request).Result;
            return response.SecretString;
        }
    }
}
AmazonSecretsManagerConfigurationSource.cs
public class AmazonSecretsManagerConfigurationSource : IConfigurationSource
{
    private readonly string secretName;

    public AmazonSecretsManagerConfigurationSource(string secretName)
    {
        this.secretName = secretName;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new AmazonSecretsManagerConfigurationProvider(this.secretName);
    }
}
ConfigurationBuilderExtensions.cs
public static class ConfigurationBuilderExtensions
{
    public static void AddAmazonSecretsManager(
        this IConfigurationBuilder configurationBuilder, string secretName)
    {
        var configurationSource = new AmazonSecretsManagerConfigurationSource(secretName);
        configurationBuilder.Add(configurationSource);
    }
}
Program.cs
builder.Configuration.AddAmazonSecretsManager("Secret name");

var secretValue = builder.Configuration["Secret key"];

Authentication with Cognito JWT Token

Program.cs
builder.Services.AddCognitoIdentity();

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.Authority = builder.Configuration["AWSCognito:Authority"];
    options.Audience = builder.Configuration["AWSCognito:UserPoolClientId"];
    options.TokenValidationParameters.AudienceValidator = (audiences, securityToken, validationParameters) =>
    {
        // This is necessary because Cognito access tokens doesn't have "aud" claim.
        // Instead the audience is set in "client_id"
        var jwt = (JsonWebToken)securityToken;
        var audience = jwt.Claims.FirstOrDefault(x => x.Type == "client_id" || x.Type == "aud");
        if (audience is null)
            return false;
        
        return validationParameters.ValidAudience == audience.Value;
    };
});

Install the following nuget packages: Amazon.AspNetCore.Identity.Cognito

aws-aspnet-cognito-identity-provider

Cognito is not a fully OIDC-compliant provider