Knocks/BackEnd/Knoks.Api/Controllers/TokenController.cs

168 lines
6.4 KiB
C#

using Knoks.Api.Authentication;
using Knoks.Api.Controllers.Base;
using Knoks.Api.Entities;
using Knoks.Core.Logic.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Threading.Tasks;
using static Knoks.Api.Authentication.IdentityConsts;
namespace Knoks.Api.Controllers
{
[Route("v1/[controller]")]
public class TokenController : ApiConsumerController
{
private readonly ILogger<TokenController> _logger;
private readonly IUserManager _userManager;
private readonly IOperatorManager _operatorManager;
private readonly JwtIssuerOptions _jwtOptions;
public TokenController(
ILogger<TokenController> logger,
IUserManager userManager,
IOperatorManager operatorManager,
JwtIssuerOptions jwtOptions)
{
_logger = logger;
_userManager = userManager;
_operatorManager = operatorManager;
_jwtOptions = jwtOptions;
ValidateOptions(_jwtOptions);
}
#region class methods
private static void ValidateOptions(JwtIssuerOptions options)
{
if (options == null)
throw new ArgumentNullException(nameof(options));
if (options.ValidFor <= TimeSpan.Zero)
throw new ArgumentException("Must be a non-zero TimeSpan.", nameof(JwtIssuerOptions.ValidFor));
if (options.SigningCredentials == null)
throw new ArgumentNullException(nameof(JwtIssuerOptions.SigningCredentials));
if (options.JtiGenerator == null)
throw new ArgumentNullException(nameof(JwtIssuerOptions.JtiGenerator));
}
private async Task<string> CreateToken(long? userId = null, int? operatorId = null)
{
return await CreateToken(
new Claim(ApiIdentifierClaim, ApiConsumer.ApiIdentifier.ToString("N")),
userId.HasValue ? new Claim(UserIdClaim, userId.ToString(), ClaimValueTypes.Integer64) : null,
operatorId.HasValue ? new Claim(OperatorIdClaim, operatorId.ToString(), ClaimValueTypes.Integer32) : null);
}
private async Task<string> RefreshToken()
{
return await CreateToken(
User.FindFirst(ApiIdentifierClaim),
User.FindFirst(UserIdClaim),
User.FindFirst(OperatorIdClaim));
}
private async Task<string> CreateToken(params Claim[] claims)
{
var claimList = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, _jwtOptions.IssuedAt.ToUnixEpochDate().ToString(), ClaimValueTypes.Integer64),
new Claim(JwtRegisteredClaimNames.Sub, Guid.NewGuid().ToString("N"))
};
claimList.AddRange(claims.Where(claim => claim != null));
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials,
claims: claimList));
}
private async Task<TokenResponse> GetTokenResult(UserCredentials userCredentials = null, OperatorCredentials operatorCredentials = null)
{
long? userId = null;
if (userCredentials != null)
{
userId = (await _userManager.AuthenticateUser(ApiConsumer.Id, userCredentials.Email, userCredentials.Password))?.UserId;
if (userId == null)
{
_logger.LogInformation($"Invalid username or password. Email = '{userCredentials.Email}'");
throw new ApiResponseException(ApiErrorType.InvalidUsernameOrPassword, $"Invalid username or password. Email = '{userCredentials.Email}'");
}
}
int? operatorId = null;
if (operatorCredentials != null)
{
operatorId = (await _operatorManager.AuthenticateOperator(ApiConsumer.Id, operatorCredentials.OperatorName, operatorCredentials.OperatorPassword))?.OperatorId;
if (operatorId == null)
{
_logger.LogInformation($"Invalid username or password. Username = '{operatorCredentials.OperatorName}'");
throw new ApiResponseException(ApiErrorType.InvalidUsernameOrPassword, $"Invalid username or password. Username = '{operatorCredentials.OperatorName}'");
}
}
return new TokenResponse
{
Token = await CreateToken(userId, operatorId),
Expiry = (int)_jwtOptions.ValidFor.TotalSeconds,
IsUser = userId != null,
IsOperator = operatorId != null
};
}
#endregion
[AllowAnonymous]
[HttpGet("{apiIdentifier}")]
public async Task<TokenResponse> GenerateToken()
{
return await GetTokenResult();
}
[AllowAnonymous]
[HttpPost("{apiIdentifier}")]
public async Task<TokenResponse> GenerateToken([FromBody]UserCredentials credentials)
{
return await GetTokenResult(userCredentials: credentials);
}
[AllowAnonymous]
[HttpPost("{apiIdentifier}/Operator")]
public async Task<TokenResponse> GenerateOperatorToken([FromBody]OperatorCredentials credentials)
{
return await GetTokenResult(operatorCredentials: credentials);
}
[HttpGet("refresh")]
public async Task<TokenResponse> Refresh()
{
return new TokenResponse
{
Token = await RefreshToken(),
Expiry = (int)_jwtOptions.ValidFor.TotalSeconds,
IsUser = User.FindFirst(UserIdClaim) != null,
IsOperator = User.FindFirst(OperatorIdClaim) != null
};
}
[HttpGet("heartbit")]
public ApiObject<bool> Heartbit()
{
return ApiObject(true);
}
}
}