/// <summary> /// Add blob key storage /// </summary> /// <param name="builder"></param> /// <param name="configuration"></param> public static IDataProtectionBuilder AddAzureBlobKeyStorage( this IDataProtectionBuilder builder, IConfiguration configuration = null) { if (configuration == null) { configuration = builder.Services.BuildServiceProvider() .GetRequiredService <IConfiguration>(); } var storage = new DataProtectionConfig(configuration); var containerName = storage.BlobStorageContainerDataProtection; if (string.IsNullOrEmpty(storage.BlobStorageConnString)) { return(builder); } var storageAccount = CloudStorageAccount.Parse(storage.BlobStorageConnString); var relativePath = $"{containerName}/keys.xml"; var uriBuilder = new UriBuilder(storageAccount.BlobEndpoint); uriBuilder.Path = uriBuilder.Path.TrimEnd('/') + "/" + relativePath.TrimStart('/'); var block = new CloudBlockBlob(uriBuilder.Uri, storageAccount.Credentials); Try.Op(() => block.Container.Create()); return(builder.PersistKeysToAzureBlobStorage(block)); }
protected internal override void AddInternal(IDataProtectionBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.PersistKeysToAzureBlobStorage(new Uri(this.Url)); }
///<inheritdoc/> public void Configure(IDataProtectionBuilder builder) { if (string.IsNullOrWhiteSpace(BlobUrl)) { throw new InvalidOperationException("Missing the Azure Storage Blob url for DataProtection."); } builder.PersistKeysToAzureBlobStorage(new Uri(BlobUrl)); }
/// <summary> /// Add blob key storage /// </summary> /// <param name="builder"></param> /// <param name="configuration"></param> public static IDataProtectionBuilder AddAzureBlobKeyStorage( this IDataProtectionBuilder builder, IConfiguration configuration) { var storage = new DataProtectionConfig(configuration); var containerName = storage.BlobStorageContainerDataProtection; var connectionString = storage.GetStorageConnString(); if (string.IsNullOrEmpty(connectionString)) { throw new InvalidConfigurationException( "Storage configuration is missing in your configuration for " + "dataprotection to store all keys across all instances."); } var storageAccount = CloudStorageAccount.Parse(storage.GetStorageConnString()); var relativePath = $"{containerName}/keys.xml"; var uriBuilder = new UriBuilder(storageAccount.BlobEndpoint); uriBuilder.Path = uriBuilder.Path.TrimEnd('/') + "/" + relativePath.TrimStart('/'); var block = new CloudBlockBlob(uriBuilder.Uri, storageAccount.Credentials); Try.Op(() => block.Container.Create()); return(builder.PersistKeysToAzureBlobStorage(block)); }
public static IDataProtectionBuilder ConfigureDataProtection(this IDataProtectionBuilder builder, IConfiguration configuration) { var dataProtectionsOptions = configuration.Get <Aguacongas.TheIdServer.Models.DataProtectionOptions>(); if (dataProtectionsOptions == null) { return(builder); } builder.AddKeyManagementOptions(options => configuration.GetSection(nameof(KeyManagementOptions))?.Bind(options)); ConfigureEncryptionAlgorithm(builder, configuration); switch (dataProtectionsOptions.StorageKind) { case StorageKind.AzureStorage: builder.PersistKeysToAzureBlobStorage(new Uri(dataProtectionsOptions.StorageConnectionString)); break; case StorageKind.EntityFramework: builder.PersistKeysToDbContext <OperationalDbContext>(); break; case StorageKind.FileSytem: builder.PersistKeysToFileSystem(new DirectoryInfo(dataProtectionsOptions.StorageConnectionString)); break; case StorageKind.Redis: var redis = ConnectionMultiplexer.Connect(dataProtectionsOptions.StorageConnectionString); if (string.IsNullOrEmpty(dataProtectionsOptions.RedisKey)) { builder.PersistKeysToStackExchangeRedis(redis); break; } builder.PersistKeysToStackExchangeRedis(redis, dataProtectionsOptions.RedisKey); break; case StorageKind.Registry: #pragma warning disable CA1416 // Validate platform compatibility builder.PersistKeysToRegistry(Registry.CurrentUser.OpenSubKey(dataProtectionsOptions.StorageConnectionString)); #pragma warning restore CA1416 // Validate platform compatibility break; } var protectOptions = dataProtectionsOptions.KeyProtectionOptions; if (protectOptions != null) { switch (protectOptions.KeyProtectionKind) { case KeyProtectionKind.AzureKeyVault: builder.ProtectKeysWithAzureKeyVault(protectOptions.AzureKeyVaultKeyId, protectOptions.AzureKeyVaultClientId, protectOptions.AzureKeyVaultClientSecret); break; case KeyProtectionKind.WindowsDpApi: builder.ProtectKeysWithDpapi(protectOptions.WindowsDPAPILocalMachine); break; case KeyProtectionKind.WindowsDpApiNg: ConfigureWindowsDpApiNg(builder, protectOptions); break; case KeyProtectionKind.X509: if (!string.IsNullOrEmpty(protectOptions.X509CertificatePath)) { var certificate = SigningKeysLoader.LoadFromFile(protectOptions.X509CertificatePath, protectOptions.X509CertificatePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet); builder.ProtectKeysWithCertificate(certificate); break; } builder.ProtectKeysWithCertificate(protectOptions.X509CertificateThumbprint); break; } } return(builder); }
/// <summary> /// Required key : Azure:KeyVault:CookieAuthentication:BlobStorageUri /// </summary> /// <param name="builder"></param> /// <param name="keyVaultUri">https://[#name#].vault.azure.net/keys/DATAPROTECTIONKEY/</param> public static void PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder) => builder.PersistKeysToAzureBlobStorage(BlobStorageUri, new DefaultAzureCredential());
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { if (_environment.IsDevelopment()) { services.AddDatabaseDeveloperPageExceptionFilter(); } // The DevelopmentStorageAccount will only work if you have the Storage emulator v4.3 installed: https://go.microsoft.com/fwlink/?linkid=717179&clcid=0x409 string storageConnectionString = _configuration["Storage:ConnectionString"]; TableServiceClient tableServiceClient = null; if (!string.IsNullOrEmpty(storageConnectionString)) { tableServiceClient = new TableServiceClient(storageConnectionString); } // Necessary to persist keys (like the ones used to generate auth cookies) // By default Azure Websites can persist keys across instances within a slot, but not across slots. // This means a slot swap will require users to re-log in. // See https://github.com/aspnet/Home/issues/466 and https://github.com/aspnet/DataProtection/issues/92 for details. IDataProtectionBuilder dataProtectionBuilder = services.AddDataProtection(); if (!string.IsNullOrEmpty(storageConnectionString)) { dataProtectionBuilder.PersistKeysToAzureBlobStorage(storageConnectionString, "key-container", "keys.xml"); } // Add Entity framework services. services.AddDbContext <ApplicationDbContext>(options => { options.UseSqlServer(_configuration["Database:ConnectionString"]); // Register the entity sets needed by OpenIddict. options.UseOpenIddict(); }); services.AddIdentity <ApplicationUser, IdentityRole>(options => { // We need to disallow '@' since we need to disambiguate between user names and email addresses during log in options.User.AllowedUserNameCharacters = options.User.AllowedUserNameCharacters.Replace("@", string.Empty, StringComparison.Ordinal); options.User.RequireUniqueEmail = true; options.Password.RequiredLength = 4; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; // Configure Identity to use the same JWT claims as OpenIddict instead // of the legacy WS-Federation claims it uses by default (ClaimTypes), // which saves you from doing the mapping in your authorization controller. options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject; options.ClaimsIdentity.EmailClaimType = OpenIddictConstants.Claims.Email; options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role; }) .AddEntityFrameworkStores <ApplicationDbContext>() .AddDefaultTokenProviders(); // Register the OpenIddict services. services.AddOpenIddict() // Register the OpenIddict core services. .AddCore(options => { // Configure OpenIddict to use the Entity Framework Core stores and models. options.UseEntityFrameworkCore() .UseDbContext <ApplicationDbContext>(); }) // Register the OpenIddict server handler. .AddServer(options => { // Enable the token endpoint (required to use the password flow). options.SetTokenEndpointUris("/api/auth/token"); // Allow client applications to use the grant_type=password flow. options.AllowPasswordFlow() .AllowRefreshTokenFlow() .AllowCustomFlow(GoogleAssertionGrantHandler.GrantType) .AllowCustomFlow(FacebookAssertionGrantHandler.GrantType) .AllowCustomFlow(MicrosoftAssertionGrantHandler.GrantType); // Mark the "email", "profile" and "roles" scopes as supported scopes. options.RegisterScopes( OpenIddictConstants.Scopes.Email, OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles); // Accept anonymous clients (i.e clients that don't send a client_id). options.AcceptAnonymousClients(); // Use ASP.NET Core data protection options.UseDataProtection(); // Register the signing and encryption credentials. // Use ephemeral keys since we're using Data Protection to validate and issue tokens anyway, // but OpenIddict doesn't allow us to not register anything. options.AddEphemeralEncryptionKey() .AddEphemeralSigningKey(); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. options.UseAspNetCore() .EnableTokenEndpointPassthrough(); }) // Register the OpenIddict validation components. .AddValidation(options => { // Import the configuration from the local OpenIddict server instance. options.UseLocalServer(); // Register the ASP.NET Core host. options.UseAspNetCore(); // Use ASP.NET Core data protection options.UseDataProtection(); }); services.Configure((AssertionGrantOptions options) => { options.AddAssertionGrantType <GoogleAssertionGrantHandler>(GoogleAssertionGrantHandler.GrantType); options.AddAssertionGrantType <FacebookAssertionGrantHandler>(FacebookAssertionGrantHandler.GrantType); options.AddAssertionGrantType <MicrosoftAssertionGrantHandler>(MicrosoftAssertionGrantHandler.GrantType); }); services.AddSingleton <GoogleAssertionGrantHandler>(); services.AddSingleton <FacebookAssertionGrantHandler>(); services.AddSingleton <MicrosoftAssertionGrantHandler>(); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); }); IBuildInfoProvider buildInfoProvider = _environment.IsDevelopment() ? new DeveloperBuildInfoProvider() : new FileBuildInfoProvider(Path.Combine(_environment.ContentRootPath, "BuildInfo.json")); services.AddApplicationInsightsTelemetry(new ApplicationInsightsServiceOptions { ApplicationVersion = buildInfoProvider.BuildUrl, DeveloperMode = _environment.IsDevelopment(), }); services.AddCors(options => { // Allow WebClient dev-server to call the API locally. if (_environment.IsDevelopment()) { options.AddDefaultPolicy(builder => { builder .WithOrigins("http://localhost:4200") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); } }); services.AddControllers() .AddJsonOptions(options => { // Beautify by default for debuggability. When gzipping, this barely adds anything to the payload. options.JsonSerializerOptions.WriteIndented = true; // Omit nulls options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; // Use camel-casing for fields (lower case first character) options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; // Convert enum values to strings options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); }); // Allow IOptions<T> to be available through DI services.AddOptions(); // Container controlled registrations if (tableServiceClient != null) { services.AddSingleton(tableServiceClient); services.AddSingleton <ISiteNewsProvider, AzureStorageSiteNewsProvider>(); } else { services.AddSingleton <ISiteNewsProvider, InMemorySiteNewsProvider>(); } services.AddSingleton(_ => GameData.Parse(Path.Combine(_environment.ContentRootPath, "GameData.json"))); services.AddSingleton(_ => new HttpClient()); services.AddSingleton <IAssertionGrantHandlerProvider, AssertionGrantHandlerProvider>(); services.AddSingleton(buildInfoProvider); services.AddSingleton <IEmailSender, EmailSender>(); services.AddSingleton <IOptions <PasswordHasherOptions>, PasswordHasherOptionsAccessor>(); // Per request registrations services.AddScoped <IClanManager, ClanManager>(); services.AddScoped <IDatabaseCommandFactory, DatabaseCommandFactory>(); services.AddScoped <IUserSettingsProvider, UserSettingsProvider>(); // configuration services.Configure <AuthenticationSettings>(options => _configuration.GetSection("Authentication").Bind(options)); services.Configure <DatabaseSettings>(options => _configuration.GetSection("Database").Bind(options)); services.Configure <EmailSenderSettings>(options => _configuration.GetSection("EmailSender").Bind(options)); }