Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ProjectVG.Api/ApiServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static IServiceCollection AddDevelopmentCors(this IServiceCollection serv
services.AddCors(options => {
options.AddPolicy("AllowAll",
policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()
.WithExposedHeaders("X-Access-Token", "X-Refresh-Token", "X-Expires-In", "X-UID"));
.WithExposedHeaders("X-Access-Credit", "X-Refresh-Credit", "X-Expires-In", "X-UID"));
});

return services;
Expand Down
33 changes: 17 additions & 16 deletions ProjectVG.Api/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using ProjectVG.Application.Services.Auth;
using ProjectVG.Common.Constants;
using ProjectVG.Common.Exceptions;

namespace ProjectVG.Api.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
[Route("api/v1/auth")]
public class AuthController : ControllerBase
{
private readonly IAuthService _authService;
Expand All @@ -14,14 +16,16 @@ public AuthController(IAuthService authService)
_authService = authService;
}

/// <summary>
/// Access Token 갱신
/// </summary>
[HttpPost("refresh")]
public async Task<IActionResult> RefreshToken()
{
var refreshToken = GetRefreshTokenFromHeader();
var result = await _authService.RefreshTokenAsync(refreshToken);

return Ok(new
{
var result = await _authService.RefreshAccessTokenAsync(refreshToken);

return Ok(new {
success = true,
tokens = result.Tokens,
user = result.User
Expand All @@ -33,9 +37,8 @@ public async Task<IActionResult> Logout()
{
var refreshToken = GetRefreshTokenFromHeader();
var success = await _authService.LogoutAsync(refreshToken);

return Ok(new
{

return Ok(new {
success = success,
message = success ? "Logout successful" : "Logout failed"
});
Expand All @@ -44,15 +47,13 @@ public async Task<IActionResult> Logout()
[HttpPost("guest-login")]
public async Task<IActionResult> GuestLogin([FromBody] string guestId)
{
if (string.IsNullOrEmpty(guestId))
{
if (string.IsNullOrEmpty(guestId)) {
throw new ValidationException(ErrorCode.GUEST_ID_INVALID);
}

var result = await _authService.LoginWithOAuthAsync("guest", guestId);

return Ok(new
{
var result = await _authService.GuestLoginAsync(guestId);

return Ok(new {
success = true,
tokens = result.Tokens,
user = result.User
Expand All @@ -61,7 +62,7 @@ public async Task<IActionResult> GuestLogin([FromBody] string guestId)

private string GetRefreshTokenFromHeader()
{
return Request.Headers["X-Refresh-Token"].FirstOrDefault() ?? string.Empty;
return Request.Headers["X-Refresh-Credit"].FirstOrDefault() ?? string.Empty;
}
Comment on lines 63 to 66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

헤더 명 변경 영향 점검: X-Refresh-Credit

클라이언트/게이트웨이 모두 X-Refresh-Credit로 업데이트되었는지 확인 필요합니다. 미반영 시 새 토큰 발급·로그아웃이 전부 실패합니다.

점검 스크립트:


🏁 Script executed:

#!/bin/bash
# 클라이언트/문서에서 이전 헤더명 사용 여부 검색
rg -n 'X-Refresh-Token|X-Access-Token' -g '!**/bin/**' -g '!**/obj/**' -C2

Length of output: 1618


헤더 명 변경 일괄 반영 필요: X-Access-Credit / X-Refresh-Credit

  • test-clients/oauth2-client/oauth2-test-client.html (363–364):
    • tokenResponse.headers.get('X-Access-Token')X-Access-Credit
    • tokenResponse.headers.get('X-Refresh-Token')X-Refresh-Credit
  • docs/unity_auth_guide.md (65–66, 210–211):
    • X-Access-TokenX-Access-Credit
    • X-Refresh-TokenX-Refresh-Credit

미적용 시 토큰 발급 및 로그아웃 기능 전부 실패합니다.

🤖 Prompt for AI Agents
In ProjectVG.Api/Controllers/AuthController.cs around lines 63 to 66, the
project-wide header names were changed to X-Access-Credit and X-Refresh-Credit
but some code and docs still use the old X-Access-Token / X-Refresh-Token;
ensure this controller's header getters use the new names (verify or add a
GetAccessTokenFromHeader returning Request.Headers["X-Access-Credit"]) and
replace any remaining usages of X-Access-Token or X-Refresh-Token in this file;
also update test-clients/oauth2-client/oauth2-test-client.html and
docs/unity_auth_guide.md at the specified lines to use X-Access-Credit and
X-Refresh-Credit so token issuance and logout work correctly.

}
}
}
73 changes: 65 additions & 8 deletions ProjectVG.Api/Controllers/CharacterController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using ProjectVG.Application.Models.Character;
using ProjectVG.Api.Models.Character.Request;
using ProjectVG.Api.Models.Character.Response;
using ProjectVG.Api.Filters;
using System.Security.Claims;

namespace ProjectVG.Api.Controllers
{
Expand All @@ -19,6 +21,16 @@ public CharacterController(ICharacterService characterService, ILogger<Character
_logger = logger;
}

private Guid? GetCurrentUserId()
{
var userIdClaim = User.FindFirst("user_id")?.Value;
if (Guid.TryParse(userIdClaim, out var userId))
{
return userId;
}
return null;
}

[HttpGet]
public async Task<ActionResult<IEnumerable<CharacterResponse>>> GetAllCharacters()
{
Expand All @@ -35,20 +47,42 @@ public async Task<ActionResult<CharacterResponse>> GetCharacterById(Guid id)
return Ok(response);
}

[HttpPost]
public async Task<ActionResult<CharacterResponse>> CreateCharacter([FromBody] CreateCharacterRequest request)
[HttpPost("individual")]
[JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> CreateCharacterWithFields([FromBody] CreateCharacterWithFieldsRequest request)
{
var userId = GetCurrentUserId();
var command = request.ToCommand(userId);
var characterDto = await _characterService.CreateCharacterWithFieldsAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return CreatedAtAction(nameof(GetCharacterById), new { id = response.Id }, response);
}

[HttpPost("systemprompt")]
[JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> CreateCharacterWithSystemPrompt([FromBody] CreateCharacterWithSystemPromptRequest request)
{
var command = request.ToCreateCharacterCommand();
var characterDto = await _characterService.CreateCharacterAsync(command);
var userId = GetCurrentUserId();
var command = request.ToCommand(userId);
var characterDto = await _characterService.CreateCharacterWithSystemPromptAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return CreatedAtAction(nameof(GetCharacterById), new { id = response.Id }, response);
}

[HttpPut("{id}")]
public async Task<ActionResult<CharacterResponse>> UpdateCharacter(Guid id, [FromBody] UpdateCharacterRequest request)
[HttpPut("{id}/individual")]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request)
{
var command = request.ToCommand(id);
var characterDto = await _characterService.UpdateCharacterToIndividualAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return Ok(response);
}
Comment on lines +72 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

인증 없는 캐릭터 업데이트 보안 문제

UpdateCharacterToIndividual 엔드포인트에 [JwtAuthentication] 속성이 없어 인증되지 않은 사용자가 캐릭터를 수정할 수 있습니다. 캐릭터 생성은 인증이 필요한데 수정은 그렇지 않은 것은 일관성이 없습니다.

[HttpPut("{id}/individual")]
+ [JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request)

또한 현재 사용자가 해당 캐릭터의 소유자인지 확인하는 로직도 필요할 수 있습니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[HttpPut("{id}/individual")]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request)
{
var command = request.ToCommand(id);
var characterDto = await _characterService.UpdateCharacterToIndividualAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return Ok(response);
}
[HttpPut("{id}/individual")]
[JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToIndividual(Guid id, [FromBody] UpdateCharacterToIndividualRequest request)
{
var command = request.ToCommand(id);
var characterDto = await _characterService.UpdateCharacterToIndividualAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return Ok(response);
}
🤖 Prompt for AI Agents
In ProjectVG.Api/Controllers/CharacterController.cs around lines 72-79, the
UpdateCharacterToIndividual endpoint lacks authentication and authorization
checks; add the same [JwtAuthentication] attribute used on protected endpoints
to require a valid user, then retrieve the caller's user id from the JWT claims
and validate ownership before performing the update (call a service method or
add a service check like EnsureUserOwnsCharacterAsync(command.Id, userId)); if
the caller is not the owner return Forbid/Unauthorized (403/401) accordingly,
and only proceed to call _characterService.UpdateCharacterToIndividualAsync when
authentication and ownership checks pass.


[HttpPut("{id}/systemprompt")]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
{
var command = request.ToUpdateCharacterCommand();
var characterDto = await _characterService.UpdateCharacterAsync(id, command);
var command = request.ToCommand(id);
var characterDto = await _characterService.UpdateCharacterToSystemPromptAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return Ok(response);
}
Comment on lines +81 to 88
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

인증 없는 캐릭터 업데이트 보안 문제

UpdateCharacterToSystemPrompt 엔드포인트도 마찬가지로 인증이 필요합니다.

[HttpPut("{id}/systemprompt")]
+ [JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[HttpPut("{id}/systemprompt")]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
{
var command = request.ToUpdateCharacterCommand();
var characterDto = await _characterService.UpdateCharacterAsync(id, command);
var command = request.ToCommand(id);
var characterDto = await _characterService.UpdateCharacterToSystemPromptAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return Ok(response);
}
[HttpPut("{id}/systemprompt")]
[JwtAuthentication]
public async Task<ActionResult<CharacterResponse>> UpdateCharacterToSystemPrompt(Guid id, [FromBody] UpdateCharacterToSystemPromptRequest request)
{
var command = request.ToCommand(id);
var characterDto = await _characterService.UpdateCharacterToSystemPromptAsync(command);
var response = CharacterResponse.ToResponseDto(characterDto);
return Ok(response);
}
🤖 Prompt for AI Agents
In ProjectVG.Api/Controllers/CharacterController.cs around lines 81-88, the
UpdateCharacterToSystemPrompt endpoint is missing authentication and
authorization checks; add the appropriate [Authorize] attribute to the
controller or this action, extract the authenticated user's ID from the
HttpContext (or inject the user service), and verify that the user is allowed to
update the specified character (ownership or role check) before calling
_characterService.UpdateCharacterToSystemPromptAsync; if the check fails return
Unauthorized or Forbid. Ensure the service call uses the validated user context
(or pass userId) to prevent unauthorized updates.

Expand All @@ -59,5 +93,28 @@ public async Task<ActionResult> DeleteCharacter(Guid id)
await _characterService.DeleteCharacterAsync(id);
return NoContent();
}

[HttpGet("my")]
[JwtAuthentication]
public async Task<ActionResult<IEnumerable<CharacterResponse>>> GetMyCharacters([FromQuery] string orderBy = "latest")
{
var userId = GetCurrentUserId();
if (!userId.HasValue)
{
return Unauthorized();
}

var characterDtos = await _characterService.GetMyCharactersAsync(userId.Value, orderBy);
var responses = characterDtos.Select(CharacterResponse.ToResponseDto);
return Ok(responses);
}

[HttpGet("public")]
public async Task<ActionResult<IEnumerable<CharacterResponse>>> GetPublicCharacters([FromQuery] string orderBy = "latest")
{
var characterDtos = await _characterService.GetPublicCharactersAsync(orderBy);
var responses = characterDtos.Select(CharacterResponse.ToResponseDto);
return Ok(responses);
}
}
}
Loading