diff --git a/Configurations/XApiKeyConfiguration.cs b/Configurations/XApiKeyConfiguration.cs index c67b3f7..fa84557 100644 --- a/Configurations/XApiKeyConfiguration.cs +++ b/Configurations/XApiKeyConfiguration.cs @@ -5,6 +5,11 @@ namespace xIds.Configurations /// public class XApiKeyConfiguration { + /// + /// Generated Api Keys Prefix ... + /// + public string ApiKeyPrefix { get; set; } + /// /// Default expiration in minutes (e.g., 43200 = 30 days) /// diff --git a/DI/XDIHelperExtension.cs b/DI/XDIHelperExtension.cs index a39d80c..52559e8 100644 --- a/DI/XDIHelperExtension.cs +++ b/DI/XDIHelperExtension.cs @@ -25,7 +25,9 @@ using xIdentityModels.Configurations; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; +using xIdentityService.Constants; namespace xIds.DI { @@ -147,6 +149,64 @@ namespace xIds.DI services.AddSingleton(dataServiceConfiguration); } + /// + /// Add Specified ApiKey Configurations Instance ... + /// + /// + /// + public static void AddXApiKeyConfiguration( + this IServiceCollection source, + XApiKeyConfiguration configuration + ) + { + // + if (configuration.IsNull()) + { + return; + } + + // + source.AddSingleton(configuration); + } + + /// + /// Extract APpi KLey Configurations from app settings + /// and Register in Services ... + /// + /// + /// + public static void AddXApiKeyConfiguration( + this IServiceCollection source, + IConfiguration configuration + ) + { + // + var config = configuration.GetXApiKeyConfiguration(); + if (config.IsNull()) + { + return; + } + + // + source.AddXApiKeyConfiguration(config); + } + + /// + /// Register Application Provider ... + /// Managing Applications/ApiKeys/ApiKeyUsages ... + /// + /// + /// + public static void AddXApplicationProvider( + this IServiceCollection source, + IConfiguration configuration + ) + { + // + source.AddXApiKeyConfiguration(configuration); + source.AddScoped(); + } + /// /// Register All Requirements For XIdentityServer Usage /// @@ -336,24 +396,41 @@ namespace xIds.DI /// /// /// + /// public static void AddXIdentityServerAuthentication( this IServiceCollection services, - IConfiguration configuration + IConfiguration configuration, + bool addApiKeyAuthentication = false ) { // services.AddXIdentityResourceConfiguration(configuration); - services.AddAuthentication() - .AddJwtBearer(options => - { - // - options.SaveToken = true; - options.RequireHttpsMetadata = false; - // - options.ForwardDefault = XAuthentication.IDENTITY_SERVER_LOCAL_API; - }) - .AddLocalApi(); + // + var authBuilder = services.AddAuthentication(); + + // + // Add JWT Bearer ... + authBuilder.AddJwtBearer(options => + { + // + options.SaveToken = true; + options.RequireHttpsMetadata = false; + + // + options.ForwardDefault = XAuthentication.IDENTITY_SERVER_LOCAL_API; + }) + .AddLocalApi(); + + // + if (addApiKeyAuthentication) + { + // + authBuilder.AddScheme( + XAuthenticationScheme.XApiKey.GetStringValue(), + options => {} + ); + } } /// diff --git a/Helpers/XApiKeyHelper.cs b/Helpers/XApiKeyHelper.cs index 273bb22..89c00e3 100644 --- a/Helpers/XApiKeyHelper.cs +++ b/Helpers/XApiKeyHelper.cs @@ -9,11 +9,29 @@ namespace xIds.Helpers /// public static class XApiKeyHelper { + /// + /// Compute Hash of Specified Content using SHA256 ... + /// + /// + /// + public static string ComputeHash(string plain) + { + // + using var sha256 = SHA256.Create(); + var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(plain)); + var result = BitConverter.ToString(hashBytes) + .Replace("-", "") + .ToLower(); + + // + return result; + } + /// /// Generate an Api Key ... /// /// - public static (string plainKey, string hash, string prefix) Generate() + public static (string plainKey, string hash, string prefix) Generate(string keyPrefix) { // // Generate 32 bytes of cryptographically secure random data @@ -25,24 +43,19 @@ namespace xIds.Helpers // // Format: xapp_{base64url} - var plainKey = $"xapp_{Convert.ToBase64String(randomBytes) + var plainKey = $"{keyPrefix}_{Convert.ToBase64String(randomBytes) .Replace("+", "-").Replace("/", "_").TrimEnd('=')}"; // - // Hash with SHA256 for storage (never store plain key) - using (var sha256 = SHA256.Create()) - { - // - var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(plainKey)); - var hash = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); + // Compute Hash ... + var hash = ComputeHash(plainKey); - // - // Prefix for quick identification (first 12 chars) - var prefix = plainKey.Substring(0, 16) + "..."; + // + // Prefix for quick identification (first 12 chars) + var prefix = plainKey.Substring(0, 16) + "..."; - // - return (plainKey, hash, prefix); - } + // + return (plainKey, hash, prefix); } /// @@ -57,16 +70,8 @@ namespace xIds.Helpers ) { // - using (var sha256 = SHA256.Create()) - { - // - var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(plainKey)); - var computedHash = BitConverter.ToString(hashBytes) - .Replace("-", "").ToLower(); - - // - return computedHash == storedHash; - } + var hash = ComputeHash(plainKey); + return hash == storedHash; } } } \ No newline at end of file diff --git a/Interfaces/IXApplicationProvider.cs b/Interfaces/IXApplicationProvider.cs index 7feb598..2dbf7d3 100644 --- a/Interfaces/IXApplicationProvider.cs +++ b/Interfaces/IXApplicationProvider.cs @@ -194,7 +194,7 @@ namespace xIds.Interfaces /// /// Task UpdateApiKeyAsync( - int id, + Guid id, XApiKeyDto item, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default @@ -209,7 +209,7 @@ namespace xIds.Interfaces /// /// Task RemoveApiKeyAsync( - int id, + Guid id, bool softDelete = true, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default @@ -225,7 +225,7 @@ namespace xIds.Interfaces /// /// Task GetApiKeyAsync( - int id, + Guid id, bool ignoreSoftDeleteds = true, XUserClaimsInfoDto userInfo = null, Func, IIncludableQueryable> includeBuilder = null, @@ -348,7 +348,7 @@ namespace xIds.Interfaces /// /// Task UpdateApiKeyUsageAsync( - int id, + long id, XApiKeyUsageDto item, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default @@ -363,7 +363,7 @@ namespace xIds.Interfaces /// /// Task RemoveApiKeyUsageAsync( - int id, + long id, bool softDelete = true, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default @@ -379,7 +379,7 @@ namespace xIds.Interfaces /// /// Task GetApiKeyUsageAsync( - int id, + long id, bool ignoreSoftDeleteds = true, XUserClaimsInfoDto userInfo = null, Func, IIncludableQueryable> includeBuilder = null, @@ -547,12 +547,14 @@ namespace xIds.Interfaces /// /// /// + /// /// /// /// - Task ValidateApiKey( + Task ValidateApiKey( string apiKey, string clientIP, + string requiredScope = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ); @@ -632,7 +634,7 @@ namespace xIds.Interfaces /// /// /// - Task EnrichRangeAsync( + Task> EnrichRangeAsync( IEnumerable sources, CancellationToken cancellationToken = default ); @@ -643,7 +645,7 @@ namespace xIds.Interfaces /// /// /// - Task EnrichRangeAsync( + Task> EnrichRangeAsync( IEnumerable sources, CancellationToken cancellationToken = default ); @@ -654,7 +656,7 @@ namespace xIds.Interfaces /// /// /// - Task EnrichRangeAsync( + Task> EnrichRangeAsync( IEnumerable sources, CancellationToken cancellationToken = default ); @@ -665,7 +667,7 @@ namespace xIds.Interfaces /// /// /// - Task EnrichRangeAsync( + Task> EnrichRangeAsync( IEnumerable sources, CancellationToken cancellationToken = default ); @@ -676,7 +678,7 @@ namespace xIds.Interfaces /// /// /// - Task EnrichRangeAsync( + Task> EnrichRangeAsync( IEnumerable sources, CancellationToken cancellationToken = default ); @@ -687,7 +689,7 @@ namespace xIds.Interfaces /// /// /// - Task EnrichRangeAsync( + Task> EnrichRangeAsync( IEnumerable sources, CancellationToken cancellationToken = default ); diff --git a/Providers/XApiKeyAuthenticationHandler.cs b/Providers/XApiKeyAuthenticationHandler.cs new file mode 100644 index 0000000..6d7c629 --- /dev/null +++ b/Providers/XApiKeyAuthenticationHandler.cs @@ -0,0 +1,101 @@ +using System.Linq; +using IdentityModel; +using xIds.Interfaces; +using xCommons.Extensions; +using System.Threading.Tasks; +using System.Security.Claims; +using System.Text.Encodings.Web; +using xIdentityService.Constants; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Authentication; + +namespace xIds.Providers +{ + /// + /// an Authentication Handler for ApiKey's based App's ... + /// + public class XApiKeyAuthenticationHandler : AuthenticationHandler + { + private readonly IXApplicationProvider applicationProvider; + + // + public string HeaderName { get; } = XHeader.ApiKey.GetStringValue(); + public string AuthenticationScheme { get; } = XAuthenticationScheme.XApiKey.GetStringValue(); + + public XApiKeyAuthenticationHandler( + UrlEncoder encoder, + ISystemClock clock, + ILoggerFactory logger, + IXApplicationProvider applicationProvider, + IOptionsMonitor options + ) : base(options, logger, encoder, clock) + { + this.applicationProvider = applicationProvider; + } + + /// + /// Handle Authentication ... + /// + /// + protected override async Task HandleAuthenticateAsync() + { + // + var apiKey = + !Request.Headers.ContainsKey(HeaderName) + ? string.Empty + : Request.Headers[HeaderName].ToString(); + if (apiKey.IsNullOrEmpty()) + { + return AuthenticateResult.NoResult(); + } + + // + var clientIP = Request.HttpContext.Connection.RemoteIpAddress?.ToString(); + + // + var validationResult = await applicationProvider.ValidateApiKey( + apiKey: apiKey, + clientIP: clientIP + ); + var isValid = !validationResult.Errors.HasChild(); + if (!isValid) + { + // + var message = validationResult.Errors.ToListString('\n'); + return AuthenticateResult.Fail(message); + } + + // + var claims = new[] + { + new Claim(ClaimTypes.Name, validationResult.OwnerId), + new Claim("application_id", validationResult.ApplicationId.ToString()), + new Claim("auth_type", "apikey"), + new Claim(JwtClaimTypes.Scope, "apikey"), + }; + + // + var scopeClaims = validationResult.Scopes + .Select(s => new Claim(JwtClaimTypes.Scope, s)); + + // + var identity = new ClaimsIdentity( + claims.Union(scopeClaims), + AuthenticationScheme + ); + + // + var principal = new ClaimsPrincipal(identity); + + // + var ticket = new AuthenticationTicket( + principal, + AuthenticationScheme + ); + + // + return AuthenticateResult.Success(ticket); + } + } +} \ No newline at end of file diff --git a/Providers/XApplicationProvider.cs b/Providers/XApplicationProvider.cs index 4216a72..7adc86a 100644 --- a/Providers/XApplicationProvider.cs +++ b/Providers/XApplicationProvider.cs @@ -13,6 +13,7 @@ using System.Linq.Expressions; using IdentityServer4.Extensions; using xIdentityModels.Extensions; using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Caching.Memory; namespace xIds.Providers { @@ -21,6 +22,7 @@ namespace xIds.Providers /// public partial class XApplicationProvider : IXApplicationProvider { + private readonly IMemoryCache cache; private readonly string permittedRole; private readonly XIdentityDbContext dbContext; private readonly IXIdentityManager identityManager; @@ -28,6 +30,7 @@ namespace xIds.Providers private readonly XApiKeyConfiguration apiKeyConfiguration; public XApplicationProvider( + IMemoryCache cache, XIdentityDbContext dbContext, IXIdentityManager identityManager, XDataServiceConfiguration configuration, @@ -36,6 +39,7 @@ namespace xIds.Providers ) { // + this.cache = cache; this.dbContext = dbContext; this.permittedRole = permittedRole; this.configuration = configuration; @@ -190,7 +194,7 @@ namespace xIds.Providers // Return result ... return result; } - + /// /// Prepare a Queryable of Items ... /// @@ -233,7 +237,7 @@ namespace xIds.Providers // Return result ... return result; } - + /// /// Prepare a Queryable of Items ... /// @@ -249,7 +253,7 @@ namespace xIds.Providers { // // Global Result ... - var result = dbContext.ApiKeyUsage.AsQueryable(); + var result = dbContext.ApiKeyUsages.AsQueryable(); // // Handle Filtering ... @@ -275,6 +279,45 @@ namespace xIds.Providers // // Return result ... return result; - } + } + + /// + /// Handle Rate Limit Checking ... + /// + /// + /// + /// + private bool CheckRateLimit( + Guid apiKeyId, + int limitPerMinute + ) + { + // + var cacheKey = $"ratelimit:{apiKeyId}"; + var currentCount = cache.GetOrCreate( + cacheKey, + entry => + { + // + entry.SlidingExpiration = TimeSpan.FromMinutes(1); + return 0; + } + ); + + // + if (currentCount >= limitPerMinute) { + return false; + } + + // + cache.Set( + cacheKey, + currentCount + 1, + TimeSpan.FromMinutes(1) + ); + + // + return true; + } } } \ No newline at end of file diff --git a/Providers/XApplicationProvider/XApplicationProvider+Custom.cs b/Providers/XApplicationProvider/XApplicationProvider+Custom.cs index 70e646b..c738fa1 100644 --- a/Providers/XApplicationProvider/XApplicationProvider+Custom.cs +++ b/Providers/XApplicationProvider/XApplicationProvider+Custom.cs @@ -8,7 +8,8 @@ using xExceptions.Constants; using System.Threading.Tasks; using xIdentityModels.Models; using System.Collections.Generic; -using System.Security.Cryptography; +using xIdentityModels.Extensions; +using Microsoft.EntityFrameworkCore; namespace xIds.Providers { @@ -85,7 +86,7 @@ namespace xIds.Providers // // Generate New Key ... - (string plainKey, string keyHash, string prefix) = XApiKeyHelper.Generate(); + (string plainKey, string keyHash, string prefix) = XApiKeyHelper.Generate(apiKeyConfiguration.ApiKeyPrefix); var expireAt = DateTime.UtcNow.Add(expiration.Value); // @@ -112,7 +113,7 @@ namespace xIds.Providers // // Add Item to Database ... result = await AddApiKeyAsync( - item: item, + item: result, userInfo: userInfo, cancellationToken: cancellationToken ); @@ -303,7 +304,7 @@ namespace xIds.Providers { XException.NotAllowed.Throw(); } - + // var result = await FindManyApiKeyAsync( userInfo: userInfo, @@ -323,18 +324,158 @@ namespace xIds.Providers /// /// /// + /// /// /// /// - public async Task ValidateApiKey( + public async Task ValidateApiKey( string apiKey, string clientIP, + string requiredScope = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // - + var result = new XApiKeyValidationResult(); + + // + // Validate ... + var isValid = + !apiKey.IsNullOrEmpty() && + !clientIP.IsNullOrEmpty(); + if (!isValid) + { + return result; + } + + // + // Detect Specified Api Key ... + var apiKeyHash = XApiKeyHelper.ComputeHash(apiKey); + var keyModel = await dbContext.ApiKeys + .FirstOrDefaultAsync( + ak => ak.KeyHash == apiKeyHash, + cancellationToken: cancellationToken + ); + isValid = !keyModel.IsNullOrDefault(); + if (!isValid) + { + // + result.Errors.Add("ApiKey not found ..."); + return result; + } + + // + // Here we Have to Parse Allowed Ips and Scopes ... + var allowedIps = (keyModel.AllowedIPs ?? string.Empty) + .ParseListString(); + var allowedScopes = (keyModel.AllowedScopes ?? string.Empty) + .FromJSON>(); + result.Scopes = [..allowedScopes]; + + // + // Check Application is Exists and Active ... + var application = await dbContext.Applications + .FirstOrDefaultAsync(x => x.Id == keyModel.ApplicationId); + if (!application.IsNullOrDefault()) + { + result.OwnerId = application.OwnerId; + result.ApplicationId = application.Id; + } + + // + // Check Application is Exists and Active or not ... + var isApplicationActive = + // + // Application Exists ... + !application.IsNullOrDefault() && + // + // Check Application is Active ... + application.IsActive + // + ; + if (!isApplicationActive) + { + result.Errors.Add("Related Application is Inactive ..."); + } + + // + // Check Key is Active or not ... + var isKeyActive = + // + // Check Api Key is Active ... + // Not Revoked ... + // Not Expired ... + keyModel.IsActive() + // + ; + if (!isKeyActive) + { + // + if (keyModel.IsExpired()) + { + result.Errors.Add("ApiKey is Expired ..."); + } + + // + if (keyModel.IsRevoked()) + { + result.Errors.Add("ApiKey is Revoked ..."); + } + } + + // + // Checking Rate Limit ... + var isRateLimitPassed = CheckRateLimit(keyModel.Id, keyModel.RateLimitPerMinute); + if (!isRateLimitPassed) + { + result.Errors.Add("ApiKey Rate Limit Reached ..."); + } + + // + // Audit Log ... + + // + // Check Scope and IP is Valid or not ... + var isIpValid = + !allowedIps.HasChild() || + allowedIps.Contains(clientIP); + var isScopeValid = + !allowedScopes.HasChild() || + requiredScope.IsNullOrEmpty() || + allowedScopes.Contains(requiredScope); + var isScopeIpValid = + isIpValid && + isScopeValid + ; + if (!isScopeIpValid) + { + // + if (!isIpValid) + { + result.Errors.Add("Client IP not Allowed to Use Resource ..."); + } + + // + if (!isScopeValid) + { + result.Errors.Add("Required Scoped is Invalid ..."); + } + } + + // + isValid = + isKeyActive && + isScopeIpValid && + isRateLimitPassed && + isApplicationActive; + if (isValid) + { + result.Errors.Clear(); + } + + // + return result; } #endregion } diff --git a/Providers/XApplicationProvider/XApplicationProvider+Enricher.cs b/Providers/XApplicationProvider/XApplicationProvider+Enricher.cs index 6447457..187d8e0 100644 --- a/Providers/XApplicationProvider/XApplicationProvider+Enricher.cs +++ b/Providers/XApplicationProvider/XApplicationProvider+Enricher.cs @@ -1,10 +1,11 @@ -using System; -using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; +using xCommons.Extensions; using xIdentityModels.Dtos; +using System.Threading.Tasks; using xIdentityModels.Models; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; namespace xIds.Providers { @@ -22,7 +23,17 @@ namespace xIds.Providers XApiKey source, CancellationToken cancellationToken = default ) - { } + { + // + var result = source.MapConvert(); + result = await EnrichAsync( + source: result, + cancellationToken: cancellationToken + ); + + // + return result; + } /// /// Enrich Api Key ... @@ -34,7 +45,36 @@ namespace xIds.Providers XApiKeyDto source, CancellationToken cancellationToken = default ) - { } + { + // + if ( + !source.IsNullOrDefault() && + !source.Application.IsNullOrDefault() + ) + { + // + var application = await dbContext.Applications + .FirstOrDefaultAsync( + x => x.Id == source.ApplicationId, + cancellationToken: cancellationToken + ); + if (!application.IsNullOrDefault()) + { + // + var dto = await EnrichAsync( + source: application, + cancellationToken: cancellationToken + ); + if (!dto.IsNullOrDefault()) + { + source.Application = dto; + } + } + } + + // + return source; + } /// /// Enrich Api Key Usage ... @@ -46,7 +86,17 @@ namespace xIds.Providers XApiKeyUsage source, CancellationToken cancellationToken = default ) - { } + { + // + var result = source.MapConvert(); + result = await EnrichAsync( + source: result, + cancellationToken: cancellationToken + ); + + // + return result; + } /// /// Enrich Application ... @@ -58,7 +108,17 @@ namespace xIds.Providers XApplication source, CancellationToken cancellationToken = default ) - { } + { + // + var result = source.MapConvert(); + result = await EnrichAsync( + source: result, + cancellationToken: cancellationToken + ); + + // + return result; + } /// /// Enrich Api Key Usage ... @@ -70,7 +130,12 @@ namespace xIds.Providers XApiKeyUsageDto source, CancellationToken cancellationToken = default ) - { } + { + // + // There is not any Additional Enrichment ... + var result = await Task.FromResult(source); + return result; + } /// /// Enrich Application ... @@ -82,7 +147,24 @@ namespace xIds.Providers XApplicationDto source, CancellationToken cancellationToken = default ) - { } + { + // + if ( + !source.IsNullOrDefault() && + !source.OwnerId.IsNullOrEmpty() + ) + { + // + var owner = await GetOwner(source.OwnerId); + if (!owner.IsNullOrDefault()) + { + source.Owner = owner; + } + } + + // + return source; + } /// /// Enrich a Collection of Api Key(s) ... @@ -94,7 +176,17 @@ namespace xIds.Providers IEnumerable sources, CancellationToken cancellationToken = default ) - { } + { + // + var tasks = sources + .Select(s => EnrichAsync(s, cancellationToken)); + + // + var result = await Task.WhenAll(tasks); + + // + return result; + } /// /// Enrich a Collection of Api Key(s) ... @@ -106,7 +198,17 @@ namespace xIds.Providers IEnumerable sources, CancellationToken cancellationToken = default ) - { } + { + // + var tasks = sources + .Select(s => EnrichAsync(s, cancellationToken)); + + // + var result = await Task.WhenAll(tasks); + + // + return result; + } /// /// Enrich a Collection of Api Key Usage(s) ... @@ -118,7 +220,17 @@ namespace xIds.Providers IEnumerable sources, CancellationToken cancellationToken = default ) - { } + { + // + var tasks = sources + .Select(s => EnrichAsync(s, cancellationToken)); + + // + var result = await Task.WhenAll(tasks); + + // + return result; + } /// /// Enrich a Collection of Application(s) ... @@ -130,7 +242,17 @@ namespace xIds.Providers IEnumerable sources, CancellationToken cancellationToken = default ) - { } + { + // + var tasks = sources + .Select(s => EnrichAsync(s, cancellationToken)); + + // + var result = await Task.WhenAll(tasks); + + // + return result; + } /// /// Enrich a Collection of Api Key Usage(s) ... @@ -142,7 +264,17 @@ namespace xIds.Providers IEnumerable sources, CancellationToken cancellationToken = default ) - { } + { + // + var tasks = sources + .Select(s => EnrichAsync(s, cancellationToken)); + + // + var result = await Task.WhenAll(tasks); + + // + return result; + } /// /// Enrich a Collection of Application(s) ... @@ -154,7 +286,17 @@ namespace xIds.Providers IEnumerable sources, CancellationToken cancellationToken = default ) - { } + { + // + var tasks = sources + .Select(s => EnrichAsync(s, cancellationToken)); + + // + var result = await Task.WhenAll(tasks); + + // + return result; + } #endregion } } \ No newline at end of file diff --git a/Startup.cs b/Startup.cs index 3c14006..000389d 100644 --- a/Startup.cs +++ b/Startup.cs @@ -1,20 +1,20 @@ using System; +using xIds.DI; using System.IO; +using xIds.Constants; +using xIds.Providers; +using Newtonsoft.Json; +using xIds.Interfaces; using System.Reflection; +using xStorageService.DI; +using xCommons.Extensions; +using xCommons.Configurations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using xCommons.Configurations; -using xCommons.Extensions; -using xStorageService.DI; -using xIds.Constants; -using xIds.DI; -using xIds.Interfaces; -using xIds.Providers; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace xIds { @@ -57,6 +57,10 @@ namespace xIds // services.AddInMemoryXIdentityServer (Configuration); services.AddEfSupportXIdentityServer(Configuration); + // + // Services Application and Api Key Management Services ... + services.AddXApplicationProvider(Configuration); + // services.AddControllers() .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); @@ -71,7 +75,7 @@ namespace xIds // // Register Security Provider ... - services.AddSingleton(); + services.AddSingleton(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger) diff --git a/appsettings.Development.json b/appsettings.Development.json index 267897f..de9781c 100644 --- a/appsettings.Development.json +++ b/appsettings.Development.json @@ -37,6 +37,7 @@ "MaxFileSize": 41943040 }, "ApiKeyConfiguration": { + "ApiPrefix": "xapp", "DefaultRateLimit": 60, "EnableAuditLog": false, "MaxKeysPerApplication": "10", diff --git a/xIds.csproj b/xIds.csproj index 913d192..1b4f73b 100644 --- a/xIds.csproj +++ b/xIds.csproj @@ -36,6 +36,7 @@ +