로그인 STA 스레드화·SSO 로그아웃·UserInfo 역직렬화 수정 및 AuthTest 추가

- LoginWindow: 로그인/로그아웃/쿠키삭제를 전용 STA 스레드(자체 Dispatcher 펌프)에서 실행
  → SignIn/SignInAsync가 MTA/STA 어느 스레드에서 호출돼도 동작 (RunOnDedicatedUiThreadAsync, AuthenticateAsync)
- SsoClient: 로그인/로그아웃 창을 AuthenticateAsync로 호출, 크로스스레드 Owner 제거
- BaronSSO: SignIn/SignOut을 Task.Run(...).GetAwaiter().GetResult()로 정리, SignInAsync 데드락 주석 추가
- UserInfo: private set 프로퍼티에 [JsonInclude] 적용 → FromSsoFile 역직렬화 복원 정상화, LastAuthTime [JsonIgnore]
- AuthTest 샘플 프로젝트 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 19:20:12 +09:00
parent a15cab4bc9
commit dd2c4e4975
14 changed files with 277 additions and 45 deletions

View File

@@ -7,28 +7,53 @@ using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace BaronSoftware.SSO
{
public class UserInfo
{
// System.Text.Json은 기본적으로 private setter에 역직렬화하지 못한다.
// [JsonInclude]를 붙여야 FromSsoFile()의 Deserialize가 private set 프로퍼티를 복원한다.
[JsonInclude]
public string UUID { get; private set; }
[JsonInclude]
public string Name { get; private set; }
[JsonInclude]
public string Email { get; private set; }
[JsonInclude]
public string[] SubEmails { get; private set; }
[JsonInclude]
public string RefreshToken { get; private set; }
[JsonInclude]
public string TenantId { get; private set; }
/// <summary>'tenants' 클레임 안에 등장하는 모든 테넌트 id 목록(상위/조상 테넌트 포함, 중복 제거).</summary>
[JsonInclude]
public string[] AllTenantIds { get; private set; }
[JsonInclude]
public long LastAuthUnixTimeStamp { get; private set; }
// 계산형 프로퍼티(setter 없음) — 파일에 저장/복원할 필요 없으므로 직렬화 제외
[JsonIgnore]
public DateTime LastAuthTime => DateTimeOffset.FromUnixTimeSeconds(LastAuthUnixTimeStamp).LocalDateTime;
[JsonInclude]
public string IdToken { get; private set; }
[JsonInclude]
public string Raw { get; private set; }
[JsonInclude]
public string RawTokenResponse { get; private set; }
[JsonInclude]
public Dictionary<string, object> Claims { get; private set; }
internal static UserInfo FromJson(string rawjson, TokenResponse reposeToken)