简体   繁体   中英

ASP.NET Core 6 basic authentification problem with Postman post method

I have this return error in Postman when calling post method but project works for get method. Project is a simple Web API with custom basic auth with X-UserID and Request body check with hmac hash:

 {
        "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
        "title": "One or more validation errors occurred.",
        "status": 400,
        "traceId": "00-e3ed0f946eea3d3837b9fb1ab1a90264-c22b6030e59a8653-00",
        "errors": {
            "$": [
                "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
            ],
            "user": [
                "The user field is required."
            ]
        }
    }

This is the code for the handler:

using System.Net.Http.Headers;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

namespace test25_08.Authentication;

public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly ApplicationDbContext _context;

    public BasicAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger,
        UrlEncoder encoder, ISystemClock clock, ApplicationDbContext context) : base(options, logger, encoder, clock)
    {
        _context = context;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        string method = Request.Method;

        if (!Request.Headers.ContainsKey("X-UserId"))
        {
            return AuthenticateResult.Fail("No header found");
        }

        var headerValue = AuthenticationHeaderValue.Parse(Request.Headers["X-UserId"]);

        var bytes = Convert.FromBase64String(headerValue.Parameter);

        var credentials = Encoding.UTF8.GetString(bytes);

        if (!string.IsNullOrEmpty(credentials))
        {
            var strings = credentials.Split(":");

            var userId = strings[0];
            var password = strings[1];

            var user = _context.Users
                .FirstOrDefault(item =>
                    item.Id == Convert.ToInt32(userId) && item.Password == password
                );

            if (user == null)
            {
                return AuthenticateResult.Fail("No user found");
            }

            if (method.Equals("POST"))
            {
                string secret = "secret";
                var headerValue2 = AuthenticationHeaderValue.Parse(Request.Headers["X-Digest"]);

                if (!Request.Headers.ContainsKey("X-Digest"))
                {
                    return AuthenticateResult.Fail("No header found");
                }


                Request.EnableBuffering();

                Request.Body.Position = 0;

                string requestBody = await new StreamReader(Request.Body).ReadToEndAsync();
                string replace = Regex.Replace(requestBody, @"\s+", "");

                if (!headerValue2.Parameter!.Equals(HashString(replace, secret)))
                {
                    return AuthenticateResult.Fail("Request body doesn't match");
                }
            }


            var claim = new[] { new Claim(ClaimTypes.Name, userId) };
            var identity = new ClaimsIdentity(claim, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);
        }

        return AuthenticateResult.Fail("UnAuthorized");
    }

    static string HashString(string stringToHash, string hachKey)
    {
        UTF8Encoding myEncoder = new UTF8Encoding();
        byte[] key = myEncoder.GetBytes(hachKey);
        byte[] text = myEncoder.GetBytes(stringToHash);
        HMACSHA1 myHmacsha1 = new HMACSHA1(key);
        byte[] hashCode = myHmacsha1.ComputeHash(text);
        string hash = BitConverter.ToString(hashCode).Replace("-", "");
        return hash.ToLower();
    }
}

This is configuration in Program.cs :

using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using test25_08;
using test25_08.Authentication;
using test25_08.Service;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

// builder.Services.AddIdentity<User, IdentityRole>()
//     .AddEntityFrameworkStores<ApplicationDbContext>()
//     .AddDefaultTokenProviders();

builder.Services.AddScoped<IWalletService, WalletService>();

builder.Services
    .AddAuthentication("BasicAuthHandler").AddScheme<AuthenticationSchemeOptions,BasicAuthHandler>("BasicAuthHandler", null);

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();

Can anyone find reason why this issue in Postman?

Updated

If uncheck X-Digest from Postman and commet if (method.Equals("POST")){...} in handler everything works well.

curl --location --request POST 'https://localhost:44358/api/v1/Users' \
--header 'X-UserId: basic MTphYWE=' \
--header 'X-Digest: basic 707cc304a905d573cd196e2ace1eb565e3c04a82' \
--header 'Content-Type: application/json' \
--data-raw '{
  "id": 0,
  "userName": "string",
  "password": "string",
  "fullName": "string",
  "passportNumber": "string",
  "borNDate": "2022-09-07T03:14:00.985Z",
  "isAuthenticated": true
}'

Alhamdulillah, I find solution. Problem was after reading Request body we lose data in Request body.I find Request.Body.Peeker Nuget package with Request.PeekBody() or Request.PeekBodyAsync() you can read body as string without losing data Link to source

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM