AspNetCoreRateLimit icon indicating copy to clipboard operation
AspNetCoreRateLimit copied to clipboard

[Question] How does one limit via a berrer token method

Open davidbuckleyni opened this issue 4 years ago • 5 comments

I have a web api that authenticates using berrer token is their an example how to limit the user thottle via that method

davidbuckleyni avatar Jan 01 '21 20:01 davidbuckleyni

You can do something like this:

using AspNetCoreRateLimit;
using Example.Core.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Example.HttpRateLimiter
{
    public class CustomClientHeaderResolveContributor : IClientResolveContributor
    {
        public const string JWT_KEY = "Jwt:Key";
        public CustomClientHeaderResolveContributor()
        {
        }

        private static ClaimsPrincipal GetPrincipalFromExpiredToken(string token, IConfiguration configuration)
        {
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration[JWT_KEY])),
                ValidateLifetime = true
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken securityToken);
            if (securityToken is not JwtSecurityToken jwtSecurityToken || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
                throw new SecurityTokenException("Invalid token");

            return principal;
        }

        private static string ResolveClient(HttpContext httpContext)
        {
            var configuration = httpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
            if(httpContext.Request.Headers.TryGetValue("Authorization", out var authValue))
            {
                var strAuthValue = authValue.ToString();
                if(strAuthValue.StartsWith("Bearer "))
                {
                    var token = strAuthValue["Bearer ".Length..];
                    var user = GetPrincipalFromExpiredToken(token, configuration);
                    var userId = user.GetUserId<string>();
                    return userId;
                }
            }

            var ip = httpContext.Connection.RemoteIpAddress?.ToString();
            return ip;
        }

        public Task<string> ResolveClientAsync(HttpContext httpContext)
        {
            return Task.FromResult<string>(ResolveClient(httpContext));
        }
    }
}

I have my own extension for GetUserId.

XzaR90 avatar Jan 02 '21 23:01 XzaR90

@XzaR90

please,how to make your class CustomClientHeaderResolveContributor be used in asp.net core( code in startup.cs).^V^

ensleep avatar Jan 19 '21 06:01 ensleep

I had to download the repo because the NuGet version is little outdated.

I made a fork and use it as a git submodule in my project.

I come back in 1 hour or so to post the startup part.

    public static class ServiceCollectionRateLimiterExtension
    {
		public static void AddHttpRateLimiter(this IServiceCollection services, IConfiguration configuration)
        {
            //load general configuration from appsettings.json
            services.Configure<ClientRateLimitOptions>(configuration.GetSection("ClientRateLimiting"));
            //load client rules from appsettings.json
            services.Configure<ClientRateLimitPolicies>(configuration.GetSection("ClientRateLimitPolicies"));
            // inject counter and rules stores
            services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>();
            services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
            // configuration (resolvers, counter key builders)
            services.AddSingleton<IRateLimitConfiguration, CustomRateLimitConfiguration>();
        }
	}

XzaR90 avatar Jan 19 '21 06:01 XzaR90

@XzaR90, please could you provide an example of how to implement CustomRateLimitConfiguration : IRateLimitConfiguration?

OutstandingBillNeal avatar Mar 28 '22 21:03 OutstandingBillNeal

@OutstandingBillNeal I am using the latest version of the nuget package now.

    public class CustomRateLimitConfiguration : RateLimitConfiguration
    {
        public CustomRateLimitConfiguration(IOptions<IpRateLimitOptions> ipOptions, IOptions<ClientRateLimitOptions> clientOptions) : base(ipOptions, clientOptions)
        {
        }

        public override void RegisterResolvers()
        {
            ClientResolvers.Add(new CustomClientHeaderResolveContributor());
        }
    }
        public static void AddHttpRateLimiter(this IServiceCollection services, IConfiguration configuration)
        {
            // load general configuration from appsettings.json
            services.Configure<ClientRateLimitOptions>(opt => configuration.GetSection("ClientRateLimiting").Bind(opt));
            // load client rules from appsettings.json
            services.Configure<ClientRateLimitPolicies>(opt => configuration.GetSection("ClientRateLimitPolicies").Bind(opt));


            services.AddDistributedRateLimiting<AsyncKeyLockProcessingStrategy>();


            // configuration (resolvers, counter key builders)
            services.AddSingleton<IRateLimitConfiguration, CustomRateLimitConfiguration>();
        }
    public static class ClaimsPrincipalExtension
    {
        public static T GetUserId<T>(this ClaimsPrincipal principal)
        {
            if (principal == null)
            {
                throw new ArgumentNullException(nameof(principal));
            }

            var nameIdentifier = principal.FindFirstValue(ClaimTypes.NameIdentifier);
            if (typeof(T) == typeof(Guid))
            {
                if (Guid.TryParse(nameIdentifier, out Guid result))
                {
                    return (T)(object)result;
                }

                return default;
            }

            return nameIdentifier != null ? (T)Convert.ChangeType(nameIdentifier, typeof(T)) : default;
        }

        public static string GetUserName(this ClaimsPrincipal principal)
        {
            if (principal == null)
            {
                throw new ArgumentNullException(nameof(principal));
            }

            return principal.FindFirstValue(ClaimTypes.Name);
        }

        public static string GetUserEmail(this ClaimsPrincipal principal)
        {
            if (principal == null)
            {
                throw new ArgumentNullException(nameof(principal));
            }

            return principal.FindFirstValue(ClaimTypes.Email);
        }
    }

I hope it helps!

XzaR90 avatar Mar 29 '22 10:03 XzaR90