/// <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));
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
 /// <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));
        }