« AWS SDK for .NET » : différence entre les versions
De Banane Atomic
Aller à la navigationAller à la recherche
Aucun résumé des modifications |
|||
Ligne 217 : | Ligne 217 : | ||
{{warn | Cognito is not a fully OIDC-compliant provider}} | {{warn | Cognito is not a fully OIDC-compliant provider}} | ||
== Validate Issuer Signing Key == | |||
Useful id an ID token is used instead of an Access token. | |||
<filebox fn='Program.cs'> | |||
var signingKeys = await GetSigningKeysAsync(); | |||
builder.Services.AddJwtBearer(options => | |||
{ | |||
options.TokenValidationParameters = new TokenValidationParameters | |||
{ | |||
ValidateIssuerSigningKey = true, // needed if an ID token is used | |||
IssuerSigningKeys = signingKeys | |||
}; | |||
}); | |||
async Task<IList<JsonWebKey>> GetSigningKeysAsync() | |||
{ | |||
var httpClient = new HttpClient(); | |||
var response = await httpClient.GetAsync($"{builder.Configuration["AWSCognito:Authority"]}/.well-known/jwks.json"); | |||
var keySet = await response.Content.ReadAsAsync<JsonWebKeySet>(); | |||
return keySet.Keys; | |||
} | |||
</filebox> | |||
* [https://medium.com/@marcio_30193/jwt-machine-to-machine-usando-aws-cognito-and-c-b1fab0524712 JWT “Machine to Machine” usando AWS Cognito and C#] | |||
* [https://stackoverflow.com/questions/66892866/issuersigningkeyresolver-call-async-method IssuerSigningKeyResolver call async method] | |||
= Get user info from Cognito in an ASP.NET web API = | = Get user info from Cognito in an ASP.NET web API = |
Version du 8 mars 2024 à 11:01
Links
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 |
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
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 |
Validate Issuer Signing Key
Useful id an ID token is used instead of an Access token.
Program.cs |
var signingKeys = await GetSigningKeysAsync(); builder.Services.AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, // needed if an ID token is used IssuerSigningKeys = signingKeys }; }); async Task<IList<JsonWebKey>> GetSigningKeysAsync() { var httpClient = new HttpClient(); var response = await httpClient.GetAsync($"{builder.Configuration["AWSCognito:Authority"]}/.well-known/jwks.json"); var keySet = await response.Content.ReadAsAsync<JsonWebKeySet>(); return keySet.Keys; } |
Get user info from Cognito in an ASP.NET web API
Usually a web API is called with an Access Token which doesn't contain information regarding the user but instead authorizations for actions.
The AccessToken needs to have the scope aws.cognito.signin.user.admin to be allowed to call cognitoService.GetUserAsync |
Program.cs |
builder.Services.AddHttpContextAccessor() .AddAWSService<IAmazonCognitoIdentityProvider>(); |
ApplicationUserProvider.cs |
private readonly IHttpContextAccessor httpContextAccessor; private readonly IAmazonCognitoIdentityProvider cognitoService; public ApplicationUserProvider(IHttpContextAccessor httpContextAccessor, IAmazonCognitoIdentityProvider cognitoService) { this.httpContextAccessor = httpContextAccessor; this.cognitoService = cognitoService; } public async Task GetUserInfo() { var accessToken = await httpContextAccessor.HttpContext!.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); // "access_token" if (accessToken is not null) { var userResponse = await cognitoService.GetUserAsync(new GetUserRequest { AccessToken = accessToken }); } } |