0

I can't get the simple Web Api authorized request via JWT in NET 7 minimal API from a MAUI client app (see below).

Therefore I thought of a workaround solution which looks like this:

  1. from the client side I also send the user data (user and password) for each request:
UserAuthentication user = new UserAuthentication();
user.UserName = "user1";
user.Email = "user1@email.com";
user.Password = "321";
HttpClient httpClient = new HttpClient();
var response = await

httpClient.PostAsJsonAsync("https://api.mywebsite.com/api/data", user);
  1. in the minimal api, I then check the user details at the endpoint and then send the data back if the login matches.
    app.MapPost("/api/data",
        [AllowAnonymous] (User user) =>.
        {
            if (user.UserName == "user1" && user.Password == "321")
            {
                return Results.Ok(list_data);
            }
            return Results.Unauthorized();
        });

Two remarks:

  • in the release version both user and password should be encrypted
  • of course the user data comes from the DB, I hard-coded the user here for testing.

Can anyone confirm that I can implement an authorized request like this (i.e. without JWT)? Or have I missed something important here with my reasoning? The important thing is that it is at least as secure as with JWT.

Here again examples that do not work in MAUI Client (are already in other posts of mine). I'm starting to think there's a problem here at Microsoft, but don't know where best to report the problem (did it at ASP-NET-CORE, got rejected https://github.com/dotnet/AspNetCore.Docs/issues/27929 ).

minimal API (NET 7):

app.MapGet("/secret2", [Authorize] () => $"Hello You. This is a secret!!!");

MAUI CLient:

        HttpClient httpClient = new HttpClient();
        var requestMessage = new HttpRequestMessage
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri(@"https://api.mysite.com/secret2")
        };
        requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
        var response = await httpClient.SendAsync(requestMessage);

Although the same JWT works via Postman, here in MAUI I get an Unauthorized message 401 when requesting. I already posted details about this (also network protocol), see also github link.

Thanks

EDIT

According to Tiny Wang, the client code works. I then assume that there is an error in the JWT generation (wondering how the generated token then worked via Postman).

Here is the completely code from Web Api (for the generation and request of JWT, as well as endpoint for a request via JWT).).

using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;

namespace WebApplication1
{
    public class User
    {
        public string UserName { get; set; } = "";
        public string Email { get; set; } = "";
        public string Password { get; set; } = "";
        public string AddInfo { get; set; } = "";
    }

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

            builder.Services.AddAuthorization();
            builder.Services.AddAuthentication().AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"))
                };
            });

            builder.Services.AddEndpointsApiExplorer();

            var app = builder.Build();

            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.MapGet("/api/test", [AllowAnonymous] () => "Hello you!");

            app.MapGet("/secret2", [Authorize] () => $"Hello You. This is a secret!!!");

            app.MapPost("/security/createToken",
                [AllowAnonymous] (User user) =>
                {
                    if (user.UserName == "user" && user.Password == "123")
                    {
                        var claims = new[]
{
                            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                            new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
                            new Claim(JwtRegisteredClaimNames.GivenName, user.UserName),
                            new Claim(JwtRegisteredClaimNames.Email, "user@test.com"),
                            new Claim(ClaimTypes.Role, "Administrator"),
                            new Claim("Role1", "Administrator"),
                            new Claim("Role2", "Standard"),
                            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                        };

                        var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"));
                        var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);

                        var tokeOptions = new JwtSecurityToken(
                            issuer: "https://api.mysite.com:64591",
                            audience: "https://api.mysite.com:64591",
                            claims: claims,
                            expires: DateTime.Now.AddMinutes(50),
                            signingCredentials: signinCredentials
                        );
                        var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);

                        return Results.Ok(tokenString);
                    }
                    return Results.Unauthorized();
                });


            app.UseHttpsRedirection();

            app.Run();
        }
    }
}
  • i'm not so sure about your requirement... and in my opinion, using jwt can make the request authorized by the claims(token contained much information such as user role and token expire time), this makes your app can check if the token had enough permission to access this api or if the token is valid or expired. but what you shared in your code snippet didn't contain this part..Pls correct me if I misunderstood. – Tiny Wang Dec 19 '22 at 08:52
  • Your code works fine for me (highly doubt that MAUI is a factor here). Have you checked that you are sending a valid token with the request? Have you tried if the same code works from simple console app? – Guru Stron Dec 19 '22 at 11:04
  • @Tiny Wang: If I do it the way I explained, then there has to be a check of the user on every web api request (you have to catch and check the user and password in the program before answering the request to client). From my point of view a JWT is much bigger than sending only username and password on every client request. Besides, the information you get via JWT can also be read directly via an Api request and stored in the client. –  Dec 19 '22 at 12:43
  • @Guru Stron If the code works for you, then it could be that my token is not generated correctly in the web api? I wonder why it works via Postman then!? I edit my post and post the web api code for token generation. Maybe you can see a bug from this? Thanks –  Dec 19 '22 at 12:45
  • `JWT is much bigger than sending only username and password on every client request` yes it's true and I also think send username/password in each request looks not so good...because call api is doing an authorization but not authentication, and using token can make it suitable for more scenarios I think. Anyway, you can use the way you like when it meets your requirement. – Tiny Wang Dec 20 '22 at 01:48

1 Answers1

0

The cause of the problem was tricky, because even the many tutorials and posts in the form as they are given in Internet will not work. But if you copy generated token out (e.g. from debug mode) and use it in Postman, then everything will work nicely and this is something that confuses you a lot. Fortunately, there are still people who have incredible mind and can detect such inconsistencies. I wouldn't have seen this in 1000 years either :)

See: https://learn.microsoft.com/en-us/answers/questions/1133200/401-unauthorized-consuming-web-api-with-jwt-authen.html