315 lines
13 KiB
C#
315 lines
13 KiB
C#
using System.Text;
|
|
using Asp.Versioning;
|
|
using JwtRoleAuthentication.Services;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Caching.Distributed;
|
|
using Microsoft.Extensions.Options;
|
|
using ServerCommon;
|
|
using ServerCommon.UGQ.Models;
|
|
using StackExchange.Redis;
|
|
using Swashbuckle.AspNetCore.Annotations;
|
|
using UGQApiServer.Auth;
|
|
using UGQApiServer.Models;
|
|
using UGQApiServer.Settings;
|
|
using UGQDataAccess.Service;
|
|
using UGQDatabase.Models;
|
|
using ServerCore; using ServerBase;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using UGQDataAccess.Logs;
|
|
using ServerCommon.BusinessLogDomain;
|
|
|
|
|
|
namespace UGQApiServer.Controllers
|
|
{
|
|
[ApiVersion("1.0")]
|
|
[Route("api/v{version:apiVersion}/[controller]")]
|
|
[ApiController]
|
|
public class AccountController : ControllerBase
|
|
{
|
|
AccountService _accountService;
|
|
TokenService _tokenService;
|
|
WebPortalTokenAuth _webPortalTokenAuth;
|
|
DynamoDbClient _dynamoDbClient;
|
|
IConnectionMultiplexer _redis;
|
|
JWTSettings _jwtSettings;
|
|
|
|
public AccountController(
|
|
AccountService accountService,
|
|
TokenService tokenService,
|
|
WebPortalTokenAuth webPortalTokenAuth,
|
|
DynamoDbClient dynamoDbClient,
|
|
IConnectionMultiplexer redis,
|
|
IOptions<JWTSettings> settings)
|
|
{
|
|
_accountService = accountService;
|
|
_tokenService = tokenService;
|
|
_webPortalTokenAuth = webPortalTokenAuth;
|
|
_dynamoDbClient = dynamoDbClient;
|
|
_redis = redis;
|
|
_jwtSettings = settings.Value;
|
|
}
|
|
|
|
string? userGuidFromToken()
|
|
{
|
|
return User.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
|
}
|
|
|
|
// #if DEBUG
|
|
/// <summary>
|
|
/// 게임DB와 연동된 버전 개발용 로그인
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 계정이 없으면 POST /api/v1/Admin/add-gamedb-account 로 테스트 계정을 만드십시오.
|
|
/// </remarks>
|
|
[HttpPost]
|
|
[Route("dev-login-v2")]
|
|
[Produces("application/json")]
|
|
[DevelopmentOnly]
|
|
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(LoginResponse))]
|
|
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
|
public async Task<IResult> loginDev([FromBody] DevLoginRequest request)
|
|
{
|
|
(Result result, UserByAccountIdResult? gameAccount) =
|
|
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, request.LoginAccountId, "");
|
|
if (result.isFail())
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqRequireAccount));
|
|
|
|
if (gameAccount == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
|
|
|
AccountEntity accountEntity = await _accountService.getOrInsertAccount(
|
|
gameAccount.UserGuid, gameAccount.Nickname, request.LoginAccountId);
|
|
|
|
var accessToken = _tokenService.CreateToken(gameAccount.UserGuid, gameAccount.Nickname);
|
|
var refreshToken = _tokenService.GenerateRefreshToken();
|
|
DateTime refreshTokenExpryTime = DateTime.Now.AddDays(7);
|
|
|
|
if (await _accountService.saveRefreshToken(accountEntity.UserGuid, refreshToken, refreshTokenExpryTime) == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
|
|
|
{
|
|
var expiry = TimeSpan.FromMinutes(_jwtSettings.TokenValidityInMinutes);
|
|
await _redis.GetDatabase().StringSetAsync($"ugq_login:{accountEntity.UserGuid}", "online", expiry);
|
|
}
|
|
|
|
List<ILogInvoker> business_logs = [
|
|
new UgqApiLoginBusinessLog(UgqApiLoginType.DevelopmentApi),
|
|
];
|
|
var log_action = new LogActionEx(LogActionType.UgqApiLogin);
|
|
UgqApiBusinessLogger.collectLogs(log_action, accountEntity, business_logs);
|
|
|
|
return Results.Ok(new LoginResponse
|
|
{
|
|
UserGuid = accountEntity.UserGuid,
|
|
Nickname = accountEntity.Nickname,
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken,
|
|
});
|
|
}
|
|
// #endif
|
|
|
|
/// <summary>
|
|
/// 로그인
|
|
/// </summary>
|
|
[HttpPost]
|
|
[Route("login")]
|
|
[Produces("application/json")]
|
|
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(LoginResponse))]
|
|
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
|
public async Task<IResult> login([FromBody] LoginRequest request)
|
|
{
|
|
WebPortalToken? webPortalToken = null;
|
|
|
|
{
|
|
Result result2 = AuthHelper.verifySsoAccountAuthJWT(request.WebPortalToken,
|
|
_webPortalTokenAuth.WebPortalTokenSecret,
|
|
out var account_id, out var account_type, out var access_token);
|
|
if (result2.isFail())
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
|
|
|
webPortalToken = new WebPortalToken
|
|
{
|
|
AccountId = account_id,
|
|
AccountType = account_type,
|
|
AccessToken = access_token,
|
|
};
|
|
|
|
Log.getLogger().info($"webPortalToken. AccountId: {webPortalToken.AccountId}, AccountType: {webPortalToken.AccountType}");
|
|
}
|
|
|
|
if (webPortalToken == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
|
|
|
var errorCode = await _webPortalTokenAuth.mysqlAuth(webPortalToken);
|
|
if (errorCode != ServerErrorCode.Success)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
|
|
|
(Result result, UserByAccountIdResult? gameAccount) =
|
|
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, webPortalToken.AccountId, "");
|
|
if (result.isFail())
|
|
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
|
|
|
if (gameAccount == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
|
|
|
AccountEntity accountEntity = await _accountService.getOrInsertAccount(
|
|
gameAccount.UserGuid, gameAccount.Nickname, webPortalToken.AccountId);
|
|
|
|
var accessToken = _tokenService.CreateToken(gameAccount.UserGuid, gameAccount.Nickname);
|
|
var refreshToken = _tokenService.GenerateRefreshToken();
|
|
DateTime refreshTokenExpryTime = DateTime.Now.AddDays(7);
|
|
|
|
if (await _accountService.saveRefreshToken(accountEntity.UserGuid, refreshToken, refreshTokenExpryTime) == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
|
|
|
{
|
|
var expiry = TimeSpan.FromMinutes(_jwtSettings.TokenValidityInMinutes);
|
|
await _redis.GetDatabase().StringSetAsync($"ugq_login:{accountEntity.UserGuid}", "online", expiry);
|
|
}
|
|
|
|
List<ILogInvoker> business_logs = [
|
|
new UgqApiLoginBusinessLog(UgqApiLoginType.NormalApi),
|
|
];
|
|
var log_action = new LogActionEx(LogActionType.UgqApiLogin);
|
|
UgqApiBusinessLogger.collectLogs(log_action, accountEntity, business_logs);
|
|
|
|
return Results.Ok(new LoginResponse
|
|
{
|
|
UserGuid = accountEntity.UserGuid,
|
|
Nickname = accountEntity.Nickname,
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// img 로그인
|
|
/// </summary>
|
|
[HttpPost]
|
|
[Route("login-igm")]
|
|
[Produces("application/json")]
|
|
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(IgmLoginResponse))]
|
|
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
|
public async Task<IResult> login_igm([FromBody] LoginRequest request)
|
|
{
|
|
WebPortalToken? webPortalToken = null;
|
|
|
|
{
|
|
Result result2 = AuthHelper.verifySsoAccountAuthJWT(request.WebPortalToken,
|
|
_webPortalTokenAuth.WebPortalTokenSecret,
|
|
out var account_id, out var account_type, out var access_token);
|
|
if (result2.isFail())
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
|
|
|
webPortalToken = new WebPortalToken
|
|
{
|
|
AccountId = account_id,
|
|
AccountType = account_type,
|
|
AccessToken = access_token,
|
|
};
|
|
|
|
Log.getLogger().info($"webPortalToken. igm - AccountId: {webPortalToken.AccountId}, AccountType: {webPortalToken.AccountType}");
|
|
}
|
|
|
|
if (webPortalToken == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
|
|
|
var errorCode = await _webPortalTokenAuth.mysqlAuth_igm(webPortalToken);
|
|
if (errorCode != ServerErrorCode.Success)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
|
|
|
(Result result, UserByAccountIdResult? gameAccount) =
|
|
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, webPortalToken.AccountId, "");
|
|
if (result.isFail())
|
|
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
|
|
|
if (gameAccount == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
|
|
|
var accessToken = _tokenService.CreateToken(gameAccount.UserGuid, gameAccount.Nickname);
|
|
|
|
List<ILogInvoker> business_logs = [
|
|
new UgqApiLoginBusinessLog(UgqApiLoginType.NormalApi),
|
|
];
|
|
var log_action = new LogActionEx(LogActionType.igmApiLogin);
|
|
UgqApiBusinessLogger.collectLogs(log_action, gameAccount.UserGuid, gameAccount.Nickname, business_logs);
|
|
|
|
return Results.Ok(new IgmLoginResponse
|
|
{
|
|
UserGuid = gameAccount.UserGuid,
|
|
Nickname = gameAccount.Nickname,
|
|
AccessToken = accessToken,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 토큰 리프레쉬
|
|
/// </summary>
|
|
[HttpPost]
|
|
[Route("refresh")]
|
|
[Produces("application/json")]
|
|
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(RefreshTokenResponse))]
|
|
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
|
public async Task<IResult> Refresh([FromBody] RefreshTokenRequest request)
|
|
{
|
|
var principal = _tokenService.GetPrincipalFromExpiredToken(request.AccessToken);
|
|
|
|
var userGuid = principal.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
|
if (userGuid == null)
|
|
return Results.Unauthorized();
|
|
|
|
var accountEntity = await _accountService.getAccount(userGuid);
|
|
if (accountEntity is null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
|
|
|
if (accountEntity.RefreshToken != request.RefreshToken || accountEntity.RefreshTokenExpiryTime <= DateTime.Now)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidToken));
|
|
|
|
var accessToken = _tokenService.CreateToken(userGuid, accountEntity.Nickname);
|
|
var refreshToken = _tokenService.GenerateRefreshToken();
|
|
|
|
if (await _accountService.saveRefreshToken(accountEntity.UserGuid, refreshToken, null) == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
|
|
|
{
|
|
var expiry = TimeSpan.FromMinutes(_jwtSettings.TokenValidityInMinutes);
|
|
await _redis.GetDatabase().StringSetAsync($"ugq_login:{accountEntity.UserGuid}", "online", expiry);
|
|
}
|
|
|
|
return Results.Ok(new RefreshTokenResponse
|
|
{
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken,
|
|
});
|
|
}
|
|
|
|
[Authorize]
|
|
[HttpPost]
|
|
[Route("logout")]
|
|
[Produces("application/json")]
|
|
[SwaggerResponse(StatusCodes.Status200OK)]
|
|
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
|
public async Task<IResult> Logout()
|
|
{
|
|
var userGuid = userGuidFromToken();
|
|
if (userGuid == null)
|
|
return Results.Unauthorized();
|
|
|
|
var accountEntity = await _accountService.getAccount(userGuid);
|
|
if (accountEntity is null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
|
|
|
if (await _accountService.deleteRefreshToken(userGuid) == null)
|
|
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
|
|
|
List<ILogInvoker> business_logs = [
|
|
new UgqApiLogoutBusinessLog(),
|
|
];
|
|
var log_action = new LogActionEx(LogActionType.UgqApiLogout);
|
|
UgqApiBusinessLogger.collectLogs(log_action, accountEntity, business_logs);
|
|
|
|
return Results.Ok();
|
|
}
|
|
}
|
|
}
|