How to handle a Bearer token in .NET 7 when a RSA security key is needed?

There is a existing .NET Framework application that creates Bearer token. Another existing .NET Framework code application is able to recognize the token for authentication and authorization. RSA security keys are used so it is not as simple as my previous work on this topic.

My task is to create a .NET 7 application that is also able to recognize the same token. I did not succeed. That is why I isolated the problem in one .NET 7 application. Hopefully, someone can fix my extension method in such a way that this is going to work.

Let me show you my extension method:

    public static IServiceCollection AddCustomJwtAuthentication(this IServiceCollection services, AuthConfiguration authConfiguration)
    {
        // Initialize RSA parameters for public key
        var rsa = new RSACryptoServiceProvider(2048);
        rsa.FromXmlString(authConfiguration.OAuthAccessTokenSigningKeyPublic);
        var rsaPublicKey = new RsaSecurityKey(rsa.ExportParameters(false));

        var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authConfiguration.OAuthTokenEncryptionKey));

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    AuthenticationType = "Bearer",
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = authConfiguration.OAuthIssuer,
                    ValidAudience = authConfiguration.OAuthAudience,
                    IssuerSigningKey = rsaPublicKey, // Used for verifying the token's signature
                    TokenDecryptionKey = symmetricSecurityKey, // Used for decrypting the token's payload
                    ValidAlgorithms = new[]
                    {
                        SecurityAlgorithms.RsaSha512Signature, // For signature verification
                        SecurityAlgorithms.Aes256KW, // For key wrapping during decryption
                        SecurityAlgorithms.Aes128CbcHmacSha256 // For content encryption/decryption
                    }
                };

                options.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        Console.WriteLine($"OnAuthenticationFailed {context.Exception}");
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Append("Token-Expired", $"{true}");
                        }
                        return Task.CompletedTask;
                    },
                    OnTokenValidated = context =>
                    {
                        Console.WriteLine($"OnTokenValidated: with SecurityToken valid to {context.SecurityToken.ValidTo}");
                        return Task.CompletedTask;
                    },
                    OnChallenge = context =>
                    {
                        Console.WriteLine($"OnChallenge: {context.Error} - {context.ErrorDescription}");
                        return Task.CompletedTask;
                    }
                };
            });

        return services;
    }

I’ll show my tokencreator too:

public class TokenCreator
{
    private const int KeySize = 2048;
    private readonly string _issuer;
    private readonly string _audience;
    private readonly SymmetricSecurityKey _encryptionKey;
    private readonly EncryptingCredentials _encryptingCredentials;
    private SigningCredentials _signingCredentials;

    private TokenCreator(string encryptionKey, string issuer, string audience)
    {
        if (string.IsNullOrWhiteSpace(encryptionKey)) throw new ArgumentNullException(nameof(encryptionKey));
        _issuer = issuer;
        _audience = audience;
        _encryptionKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(encryptionKey));
        _encryptingCredentials = new EncryptingCredentials(
            _encryptionKey,
            SecurityAlgorithms.Aes256KW,
            SecurityAlgorithms.Aes128CbcHmacSha256);
    }

    public TokenCreator(AuthConfiguration authConfiguration) : this (authConfiguration.OAuthTokenEncryptionKey,
        authConfiguration.OAuthIssuer, authConfiguration.OAuthAudience)
    {
        SetSigningCredentials(authConfiguration.OAuthAccessTokenSigningKeyPrivate);
    }


    private void SetSigningCredentials(string privateSigningKeyXml)
    {
        if (string.IsNullOrWhiteSpace(privateSigningKeyXml)) throw new ArgumentNullException(nameof(privateSigningKeyXml));
        using (var rsa = new RSACryptoServiceProvider(KeySize))
        {
            rsa.FromXmlString(privateSigningKeyXml);
            _signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa.ExportParameters(true)), SecurityAlgorithms.RsaSha512Signature);
        }
    }

    public virtual string CreateToken(DateTime expires, DateTime issued)
    {
        // Define claims
        var claims = new List<Claim>
        {
            new(ClaimTypes.Name, "John Doe"),
            new(ClaimTypes.Email, "[email protected]"),
            new(ClaimTypes.Role, "Administrator"),
            new("CustomClaimType", "CustomClaimValue")
        };
        var identity = new ClaimsIdentity(claims, "Bearer");
        var handler = new JwtSecurityTokenHandler();
        var token = handler.CreateJwtSecurityToken(_issuer, _audience, identity, null, expires, issued, _signingCredentials, _encryptingCredentials);
        var jwt = handler.WriteToken(token);
        return jwt;
    }
}

I’ll also share my program class:

    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();
            
            var authConfiguration = builder.Configuration.GetSection(nameof(AuthConfiguration)).Get<AuthConfiguration>()!;
            builder.Services.AddCustomJwtAuthentication(authConfiguration);

            var tokenCreator = new TokenCreator(authConfiguration);
            var token = tokenCreator.CreateToken(DateTime.UtcNow.AddHours(1), DateTime.UtcNow);
            Console.WriteLine("Token is coming");
            Console.WriteLine(token);
            Console.WriteLine("End of token");

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication(); 
            app.UseAuthorization();


            app.MapControllers();

            app.Run();
        }
    }

As this web api generates a token for me, I can directly test with VSCode REST Client:

GET https://localhost:7134/api/Authentication/verifyAuthentication
Authorization: Bearer SUPERLONGTOKEN

This to call my controller method:

    [Route("api/[controller]")]
    [ApiController]
    public class AuthenticationController : ControllerBase
    {
        [HttpGet("verifyAuthentication")]
        [Authorize]
        public IActionResult VerifyAuthentication()
        {
            return Ok("Auth succeeded");
        }
    }

This gives the following output:

HTTP/1.1 401 Unauthorized
Content-Length: 0
Connection: close
Date: Tue, 11 Jun 2024 12:26:12 GMT
Server: Kestrel
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"

Token generation succeeds so this is not my concern. To be able to work with it, I need to write an extension method using the same keys as my .NET Framework code uses. And apparently, I did not do that correctly as I do not get a 200 returned. So what needs to be changed to handle a Bearer token in .NET 7 when a RsaSecurityKey is needed?

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật