AcaMate_API/Program/Services/V1/KakaoService.cs
Seonkyu.kim 65962c01c2 [] 카카오 로그인 구현
1. 인가코드 받기
2. 리다이렉트 -> 인가 코드를 엑세스 토큰으로
3. User.Me -> snsID 받아오기
4. DB 에서 snsID 조회 까지 완료
2025-05-29 17:53:50 +09:00

157 lines
5.9 KiB
C#

using System.Text.Json;
using System.Net.Http.Headers;
using Back.Program.Services.V1.Interfaces;
namespace Back.Program.Services.V1;
public class KakaoService: IKakaoService
{
private readonly HttpClient _httpClient;
private const string KAKAO_API_BASE_URL = "https://kapi.kakao.com";
private const string KAKAO_AUTH_BASE_URL = "https://kauth.kakao.com";
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _redirectUri;
public KakaoService(HttpClient httpClient, IConfiguration configuration)
{
_httpClient = httpClient;
_clientId = configuration["Kakao:ClientId"] ?? throw new InvalidOperationException("Kakao:ClientId not configured");
_redirectUri = configuration["Kakao:RedirectUri"] ?? throw new InvalidOperationException("Kakao:RedirectUri not configured");
_clientSecret = configuration["Kakao:ClientSecret"] ?? throw new InvalidOperationException("Kakao:ClientSecret not configured");
}
private void SetHeaders(string accessToken)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
private async Task<string> Call(HttpMethod method, string url, HttpContent? content = null)
{
try
{
var request = new HttpRequestMessage(method, url);
if (content != null)
{
request.Content = content;
}
var response = await _httpClient.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
return JsonSerializer.Serialize(new { error = $"HTTP {(int)response.StatusCode}: {responseContent}" });
}
return responseContent;
}
catch (Exception ex)
{
return JsonSerializer.Serialize(new { error = ex.Message });
}
}
/// <summary>
/// 인가 받은 코드로 토큰 받기를 실행하고 이 토큰으로 사용자 정보 가져오기를 할 수 있다.
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public async Task<string> GetAccessToken(string code)
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("client_id", _clientId),
new KeyValuePair<string, string>("redirect_uri", _redirectUri),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("client_secret", _clientSecret)
});
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var response = await Call(HttpMethod.Post, $"{KAKAO_AUTH_BASE_URL}/oauth/token", content);
var responseData = JsonSerializer.Deserialize<JsonElement>(response);
if (responseData.TryGetProperty("error", out var error))
{
return response;
}
if (!responseData.TryGetProperty("access_token", out var accessToken))
{
return JsonSerializer.Serialize(new { error = "Access token is missing from response" });
}
return JsonSerializer.Serialize(new { access_token = accessToken.GetString() });
}
/// <summary>
/// 인가코드를 받는다 이 인가코드를 사용해 토큰 받기를 요청 할 수 있다.
/// 이게 리다이렉트에 포함되어있음
/// </summary>
/// <param name="scope"></param>
/// <returns></returns>
public Task<string> GetAuthorizationUrl(string scope)
{
var authUrl = $"{KAKAO_AUTH_BASE_URL}/oauth/authorize?client_id={_clientId}&redirect_uri={_redirectUri}&response_type=code";
if (!string.IsNullOrEmpty(scope))
{
authUrl += $"&scope={scope}";
}
return Task.FromResult(authUrl);
}
public async Task<(bool Success, string Response)> Redirect(string code)
{
if (string.IsNullOrEmpty(code))
return (false, JsonSerializer.Serialize(new { error = "Authorization code not found" }));
var response = await GetAccessToken(code);
var responseData = JsonSerializer.Deserialize<JsonElement>(response);
if (responseData.TryGetProperty("error", out var error))
{
return (false, response);
}
var accessToken = responseData.GetProperty("access_token").GetString();
if (string.IsNullOrEmpty(accessToken))
{
return (false, response);
}
return (true, accessToken);
}
public async Task<(bool Success, string Response)> Logout(string accessToken)
{
if (string.IsNullOrEmpty(accessToken))
return (false, JsonSerializer.Serialize(new { error = "Not logged in" }));
SetHeaders(accessToken);
var response = await Call(HttpMethod.Post, $"{KAKAO_API_BASE_URL}/v1/user/logout");
return (true, response);
}
public async Task<(bool Success, string Response)> Unlink(string accessToken)
{
if (string.IsNullOrEmpty(accessToken))
return (false, JsonSerializer.Serialize(new { error = "Not logged in" }));
SetHeaders(accessToken);
var response = await Call(HttpMethod.Post, $"{KAKAO_API_BASE_URL}/v1/user/unlink");
return (true, response);
}
public async Task<(bool Success, string Response)> UserMe(string accessToken)
{
if (string.IsNullOrEmpty(accessToken))
return (false, JsonSerializer.Serialize(new { error = "Not logged in" }));
SetHeaders(accessToken);
var response = await Call(HttpMethod.Get, $"{KAKAO_API_BASE_URL}/v2/user/me");
return (true, response);
}
}