ASP.NET Core Identity에서 JWT Refresh Token 적용 및 테스트하기

  • 5 minutes to read

이 문서에서는 ASP.NET Core Identity와 JWT Refresh Token을 활용하여 인증 시스템을 구축하는 방법을 단계별로 설명합니다.
완성된 API를 .http 파일을 이용해 테스트하는 방법까지 포함하므로, 직접 따라하며 실습할 수 있습니다.


1. 프로젝트 환경 설정

1.1. 필요한 패키지 설치

ASP.NET Core에서 JWT 인증과 Identity를 사용하기 위해 NuGet 패키지를 설치해야 합니다.

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity

이 패키지들은 다음과 같은 역할을 합니다.

  • Microsoft.AspNetCore.Authentication.JwtBearer: JWT 기반 인증을 수행할 수 있도록 설정하는 패키지
  • Microsoft.AspNetCore.Identity.EntityFrameworkCore: Identity를 사용하여 사용자 계정 관리를 쉽게 구현하는 패키지
  • Microsoft.AspNetCore.Identity: ASP.NET Core Identity의 핵심 기능을 제공하는 패키지

2. Identity 및 JWT 설정

2.1. 사용자 엔터티(User Entity) 수정

IdentityUser를 확장하여 Refresh Token 관련 필드를 추가합니다.

using Microsoft.AspNetCore.Identity;
using System;

public class ApplicationUser : IdentityUser
{
    public string? RefreshToken { get; set; }
    public DateTime? RefreshTokenExpiryTime { get; set; }
}

위 코드에서 RefreshTokenRefreshTokenExpiryTime을 추가하여 사용자가 새로운 액세스 토큰을 받을 수 있도록 저장할 공간을 마련합니다.


2.2. JWT 설정 추가 (appsettings.json)

JWT 관련 설정을 appsettings.json에 추가합니다.

"JwtSettings": {
  "Secret": "{JwtSecret}",
  "Issuer": "https://your-api.com",
  "Audience": "https://your-client.com",
  "AccessTokenExpiration": 30,  
  "RefreshTokenExpiration": 7    
}

각 항목의 의미는 다음과 같습니다.

  • Secret: JWT 서명을 위한 보안 키. 반드시 강력한 값을 사용해야 하며, 환경 변수에서 관리하는 것이 좋습니다.
  • Issuer: JWT를 발급하는 서버의 도메인 또는 서비스 이름.
  • Audience: 이 토큰을 사용할 클라이언트(웹 애플리케이션, 모바일 앱 등).
  • AccessTokenExpiration: 액세스 토큰 만료 시간(분 단위).
  • RefreshTokenExpiration: 리프레시 토큰 만료 시간(일 단위).

환경 변수에서 Secret 값 가져오기 (Program.cs)

운영 환경에서는 .env 파일 또는 환경 변수에서 JwtSecret 값을 관리하는 것이 보안에 더 좋습니다.

var secretKey = Environment.GetEnvironmentVariable("JwtSecret") ?? configuration["JwtSettings:Secret"];

3. JWT 및 Identity 설정 (Program.cs)

JWT 및 ASP.NET Core Identity를 설정하여 사용자 인증과 토큰 발급을 수행할 준비를 합니다.

using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

var jwtSettings = configuration.GetSection("JwtSettings");
var key = Encoding.UTF8.GetBytes(jwtSettings["Secret"]);

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = jwtSettings["Issuer"],
        ValidAudience = jwtSettings["Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(key)
    };
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

4. JWT 및 Refresh Token 생성

4.1. JWT 및 Refresh Token 생성 서비스

JWT 및 Refresh Token을 생성하는 서비스를 추가합니다.

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

public class JwtService
{
    private readonly IConfiguration _configuration;

    public JwtService(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public string GenerateAccessToken(IEnumerable<Claim> claims)
    {
        var jwtSettings = _configuration.GetSection("JwtSettings");
        var key = Encoding.UTF8.GetBytes(jwtSettings["Secret"]);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(jwtSettings["AccessTokenExpiration"])),
            Issuer = jwtSettings["Issuer"],
            Audience = jwtSettings["Audience"],
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };

        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }

    public string GenerateRefreshToken()
    {
        return Convert.ToBase64String(RandomNumberGenerator.GetBytes(64));
    }
}

5. 인증 관련 API 구현 (AuthController.cs)

사용자 인증 및 토큰 관리를 위한 컨트롤러를 구현합니다.

[Route("api/auth")]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly JwtService _jwtService;

    public AuthController(UserManager<ApplicationUser> userManager, JwtService jwtService)
    {
        _userManager = userManager;
        _jwtService = jwtService;
    }

    [HttpPost("register")]
    public async Task<IActionResult> Register([FromBody] RegisterModel model)
    {
        var user = new ApplicationUser { UserName = model.Username, Email = model.Email };
        var result = await _userManager.CreateAsync(user, model.Password);
        if (!result.Succeeded)
            return BadRequest(result.Errors);

        return Ok(new { message = "User registered successfully" });
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginModel model)
    {
        var user = await _userManager.FindByNameAsync(model.Username);
        if (user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
            return Unauthorized();

        var accessToken = _jwtService.GenerateAccessToken(new[] { new Claim(ClaimTypes.Name, user.UserName) });
        var refreshToken = _jwtService.GenerateRefreshToken();

        user.RefreshToken = refreshToken;
        user.RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(7);
        await _userManager.UpdateAsync(user);

        return Ok(new { accessToken, refreshToken });
    }
}

6. .http 파일을 사용한 테스트

API가 정상적으로 동작하는지 확인하기 위해 .http 파일을 작성하여 요청을 테스트할 수 있습니다.

POST http://localhost:5000/api/auth/register
Content-Type: application/json

{
  "username": "testuser",
  "email": "test@example.com",
  "password": "Test@1234"
}

POST http://localhost:5000/api/auth/login
Content-Type: application/json

{
  "username": "testuser",
  "password": "Test@1234"
}

이제 JWT 및 Refresh Token을 이용한 인증 시스템이 완성되었습니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com