Files
caliverse_server/UGQApiServer/Controllers/AccountController.cs
2025-05-01 07:20:41 +09:00

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