using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Domain.Entities.Members; /// /// 회원별 OAuth 토큰 저장 (Google OAuth 2.0 등) /// YouTube API 호출 시 Access Token 사용 /// public class MemberOAuthToken { [ForeignKey(nameof(MemberID))] public virtual Member? Member { get; private set; } [Key] public long ID { get; private set; } public int MemberID { get; private set; } public string Provider { get; private set; } = "Google"; /// /// provider 측 고유 사용자 ID (카카오 id, 네이버 response.id). 로그인 시 backfill. /// 소셜 연결 끊기(disconnect) webhook 이 Provider + ProviderUserKey 로 연동행을 찾는다. /// public string? ProviderUserKey { get; private set; } public string AccessTokenEnc { get; private set; } = default!; public string? RefreshTokenEnc { get; private set; } public DateTime ExpiresAt { get; private set; } public string Scopes { get; private set; } = string.Empty; public DateTime CreatedAt { get; private set; } = DateTime.UtcNow; public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow; private MemberOAuthToken() { } public static MemberOAuthToken Create( int memberID, string provider, string accessTokenEnc, string? refreshTokenEnc, DateTime expiresAt, string scopes, string? providerUserKey = null ) { return new MemberOAuthToken { MemberID = memberID, Provider = provider, ProviderUserKey = providerUserKey, AccessTokenEnc = accessTokenEnc, RefreshTokenEnc = refreshTokenEnc, ExpiresAt = expiresAt, Scopes = scopes }; } public void UpdateTokens(string accessTokenEnc, string? refreshTokenEnc, DateTime expiresAt, string scopes, string? providerUserKey = null) { AccessTokenEnc = accessTokenEnc; if (refreshTokenEnc is not null) { RefreshTokenEnc = refreshTokenEnc; } if (!string.IsNullOrEmpty(providerUserKey)) { ProviderUserKey = providerUserKey; } ExpiresAt = expiresAt; Scopes = scopes; UpdatedAt = DateTime.UtcNow; } public bool IsExpired => DateTime.UtcNow >= ExpiresAt; public bool HasRefreshToken => !string.IsNullOrEmpty(RefreshTokenEnc); }