Handler.cs 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. using Application.Abstractions.Authentication;
  2. using Application.Abstractions.Messaging;
  3. using SharedKernel;
  4. using SharedKernel.Results;
  5. using Microsoft.Extensions.Options;
  6. namespace Application.Features.Api.Auth.SocialAuthorizeUrl;
  7. internal sealed class Handler(
  8. IEnumerable<ISocialAuthProvider> socialProviders,
  9. IOptions<AppSettings> options
  10. ) : IQueryHandler<Query, Result<Response>> {
  11. private readonly AppSettings.OAuthSection _oauth = options.Value.OAuth;
  12. public Task<Result<Response>> Handle(Query request, CancellationToken ct)
  13. {
  14. var providerName = request.Provider?.Trim().ToLowerInvariant() switch
  15. {
  16. "naver" => "Naver",
  17. "kakao" => "Kakao",
  18. _ => null
  19. };
  20. if (providerName is null)
  21. {
  22. return Task.FromResult(Result.Failure<Response>(Error.Problem("Auth.UnsupportedProvider", "지원하지 않는 소셜 로그인입니다.")));
  23. }
  24. // 네이버는 스펙상 필수, 카카오도 CSRF 방어 위해 항상 요구 (d5 §③)
  25. if (string.IsNullOrWhiteSpace(request.State))
  26. {
  27. return Task.FromResult(Result.Failure<Response>(Error.Problem("Auth.StateRequired", "state 값이 필요합니다.")));
  28. }
  29. var oauthConfig = providerName == "Naver" ? _oauth.Naver : _oauth.Kakao;
  30. if (string.IsNullOrWhiteSpace(oauthConfig.ClientId))
  31. {
  32. return Task.FromResult(Result.Failure<Response>(Error.Problem("Auth.SocialNotConfigured", $"{providerName} 로그인이 설정되지 않았습니다.")));
  33. }
  34. var redirectUri = string.IsNullOrWhiteSpace(request.RedirectUri) ? oauthConfig.RedirectUri : request.RedirectUri;
  35. if (string.IsNullOrWhiteSpace(redirectUri))
  36. {
  37. return Task.FromResult(Result.Failure<Response>(Error.Problem("Auth.RedirectUriRequired", "Redirect URI 가 필요합니다.")));
  38. }
  39. var provider = socialProviders.FirstOrDefault(c => c.Provider == providerName);
  40. if (provider is null)
  41. {
  42. return Task.FromResult(Result.Failure<Response>(Error.Problem("Auth.UnsupportedProvider", "지원하지 않는 소셜 로그인입니다.")));
  43. }
  44. var url = provider.GetAuthorizationUrl(oauthConfig.ClientId, redirectUri, request.State);
  45. return Task.FromResult(Result.Success(new Response(providerName, url)));
  46. }
  47. }