admin管理员组

文章数量:1335895

When using PasswordSignInAsync from a SignInManager, the result is a SignInResult which tells me if the sign in succeeded or failed but doesn't actually containt the details like the access token. The token and other info is stored in AccessTokenResponse, which appears to just be stored in the context. My question is, how can I intercept the AccessTokenResponse in order to map it to a custom class? Other than that, this implementation is working well.

When calling PasswordSignInAsync from an API method, it seems like the AccessTokenResponse is already embedded in the response without me doing anything, so I'm unsure how I would access it.

Controller Method (using Mediatr)

[HttpPost("Login")]
[AllowAnonymous]
public async Task<Results<Ok<AccessTokenResponse>, EmptyHttpResult, ProblemHttpResult>> Login(LoginUserCommand command)
{
    var response = await Mediator.Send(command);
    if (!response.Success)
    {
        return TypedResults.Problem(response.Message);
    }
    return TypedResults.Empty;
}

Handler

public LoginUserHandler(UserManager<PublicUser> userManager, SignInManager<PublicUser> signInManager)
{
    _userManager = userManager;
    signInManager.AuthenticationScheme = IdentityConstants.BearerScheme;
    _signInManager = signInManager;
}

public async Task<BaseResponse> Handle(LoginUserCommand request, CancellationToken cancellationToken)
{
    var response = new BaseResponse();

    var user = await _userManager.FindByNameAsync(request.Username);
    if (user == null)
    {
        response.Success = false;
        response.Message = "Login Failed";
        return response;
    }

    var result = await _signInManager.PasswordSignInAsync(user, request.Password, false, true);

    if (!result.Succeeded)
    {
        response.Success = false;
        response.Message = "Login Failed";
    }

    return response;
}

I'm hoping to map this to a custom class so I can strongly type the API response in the Swagger definition.

When using PasswordSignInAsync from a SignInManager, the result is a SignInResult which tells me if the sign in succeeded or failed but doesn't actually containt the details like the access token. The token and other info is stored in AccessTokenResponse, which appears to just be stored in the context. My question is, how can I intercept the AccessTokenResponse in order to map it to a custom class? Other than that, this implementation is working well.

When calling PasswordSignInAsync from an API method, it seems like the AccessTokenResponse is already embedded in the response without me doing anything, so I'm unsure how I would access it.

Controller Method (using Mediatr)

[HttpPost("Login")]
[AllowAnonymous]
public async Task<Results<Ok<AccessTokenResponse>, EmptyHttpResult, ProblemHttpResult>> Login(LoginUserCommand command)
{
    var response = await Mediator.Send(command);
    if (!response.Success)
    {
        return TypedResults.Problem(response.Message);
    }
    return TypedResults.Empty;
}

Handler

public LoginUserHandler(UserManager<PublicUser> userManager, SignInManager<PublicUser> signInManager)
{
    _userManager = userManager;
    signInManager.AuthenticationScheme = IdentityConstants.BearerScheme;
    _signInManager = signInManager;
}

public async Task<BaseResponse> Handle(LoginUserCommand request, CancellationToken cancellationToken)
{
    var response = new BaseResponse();

    var user = await _userManager.FindByNameAsync(request.Username);
    if (user == null)
    {
        response.Success = false;
        response.Message = "Login Failed";
        return response;
    }

    var result = await _signInManager.PasswordSignInAsync(user, request.Password, false, true);

    if (!result.Succeeded)
    {
        response.Success = false;
        response.Message = "Login Failed";
    }

    return response;
}

I'm hoping to map this to a custom class so I can strongly type the API response in the Swagger definition.

Share Improve this question edited Nov 20, 2024 at 20:54 SpicyCatGames 1,7461 gold badge18 silver badges34 bronze badges asked Nov 19, 2024 at 21:31 Matt MacDonaldMatt MacDonald 112 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

We can choose to generate a JWT token ourselves, use JwtSecurityTokenHandler to generate an access token, and return it to the client. This method does not rely on the externally stored AccessTokenResponse, so that we can directly control the entire login process.

    [HttpPost("Login2")]
    public async Task<IActionResult> Login2([FromBody] LoginModel model)
    {
        var user = await _userManager.FindByNameAsync(model.Username);
        if (user == null)
        {
            return Unauthorized("Invalid Username or Password.");
        }

        var result = await _signInManager.PasswordSignInAsync(user, model.Password, false, false);
        if (!result.Succeeded)
        {
            return Unauthorized("Invalid Username or Password.");
        }

        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes("Your_Secret_Key_Here_Your_Secret_Key_Here");
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(ClaimTypes.NameIdentifier, user.Id),
                new Claim(ClaimTypes.Name, user.UserName)
            }),
            Expires = DateTime.UtcNow.AddHours(1),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);

        var customResponse = new CustomResponse
        {
            AccessToken = jwtToken,
            ExpiresIn = 3600 // 1 hour
        };

        return Ok(customResponse);
    }
}

internal class CustomResponse
{
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }
    public long ExpiresIn { get; set; }
}

本文标签: