115 lines
4.5 KiB
C#
115 lines
4.5 KiB
C#
using Microsoft.Web.WebView2.Core;
|
|
using System.Windows;
|
|
using WebView2 = Microsoft.Web.WebView2.Wpf.WebView2;
|
|
|
|
namespace BaronSoftware.SSO
|
|
{
|
|
/// <summary>
|
|
/// 인증 엔드포인트를 WebView2로 로드하고, redirect_uri로의 이동을 가로채
|
|
/// authorization code가 담긴 콜백 URL을 반환하는 로그인 창.
|
|
/// redirect_uri로 실제 페이지가 로드되기 전에 NavigationStarting에서 취소하므로,
|
|
/// localhost에 실제 서버를 띄울 필요가 없습니다.
|
|
/// </summary>
|
|
internal partial class LoginWindow : Window
|
|
{
|
|
private readonly string _authorizeUrl;
|
|
private readonly string _redirectUri;
|
|
private readonly TaskCompletionSource<string> _tcs =
|
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
internal LoginWindow(string authorizeUrl, string redirectUri)
|
|
{
|
|
InitializeComponent();
|
|
_authorizeUrl = authorizeUrl;
|
|
_redirectUri = redirectUri;
|
|
|
|
Loaded += async (s, e) =>
|
|
{
|
|
try
|
|
{
|
|
#if DEBUG
|
|
await webview.EnsureCoreWebView2Async();
|
|
webview.CoreWebView2.NavigationStarting += OnNavigationStarting;
|
|
webview.CoreWebView2.Navigate(_authorizeUrl);
|
|
#else
|
|
|
|
// 1. 웹뷰 환경 설정 객체 생성
|
|
var environment = await CoreWebView2Environment.CreateAsync(null, null, null);
|
|
|
|
// 2. 컨트롤러 옵션 생성 및 InPrivate 모드 활성화
|
|
var options = environment.CreateCoreWebView2ControllerOptions();
|
|
options.IsInPrivateModeEnabled = true; // 핵심 설정
|
|
|
|
// 3. 인프라이빗 옵션을 적용하여 WebView2 초기화
|
|
await webview.EnsureCoreWebView2Async(environment, options);
|
|
|
|
webview.CoreWebView2.NavigationStarting += OnNavigationStarting;
|
|
webview.CoreWebView2.Navigate(_authorizeUrl);
|
|
#endif
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_tcs.TrySetException(ex);
|
|
Dispatcher.BeginInvoke(new Action(Close));
|
|
}
|
|
};
|
|
|
|
Closed += (_, _) => _tcs.TrySetException(new OperationCanceledException());
|
|
}
|
|
|
|
private void OnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs e)
|
|
{
|
|
if (e.Uri.StartsWith(_redirectUri, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
e.Cancel = true; // 실제 localhost 로딩 차단
|
|
_tcs.TrySetResult(e.Uri);
|
|
Dispatcher.BeginInvoke(new Action(Close));
|
|
}
|
|
}
|
|
|
|
/// <summary>창을 띄우고 콜백 URL(code 포함)을 비동기로 반환.</summary>
|
|
internal Task<string> ShowAndGetRedirectAsync()
|
|
{
|
|
Show();
|
|
return _tcs.Task;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 로그인 WebView가 사용하는 프로필의 모든 쿠키(=SSO 세션 쿠키)를 삭제합니다.
|
|
/// 화면에 보이지 않는 오프스크린 WebView2를 잠깐 띄워 동일 프로필의 쿠키를 비웁니다.
|
|
/// 이후 다음 로그인 시 세션이 없어 로그인 폼이 다시 표시됩니다.
|
|
/// </summary>
|
|
internal static async Task ClearSessionCookiesAsync()
|
|
{
|
|
var holder = new Window
|
|
{
|
|
Width = 1,
|
|
Height = 1,
|
|
Left = -32000,
|
|
Top = -32000,
|
|
WindowStyle = WindowStyle.None,
|
|
ShowInTaskbar = false,
|
|
ShowActivated = false,
|
|
Title = string.Empty,
|
|
};
|
|
var web = new WebView2();
|
|
holder.Content = web;
|
|
holder.Show(); // WebView2 초기화에 필요한 HWND 확보(화면 밖)
|
|
try
|
|
{
|
|
await web.EnsureCoreWebView2Async();
|
|
// DeleteAllCookies()는 즉시 반환이라, 디스크 반영 전에 Dispose되면 쿠키가 남아
|
|
// 다음 로그인이 SSO로 조용히 통과될 수 있다. 완료까지 await하는 ClearBrowsingDataAsync로
|
|
// SSO 세션 쿠키/사이트 데이터를 확실히 제거한다.
|
|
await web.CoreWebView2.Profile.ClearBrowsingDataAsync(
|
|
CoreWebView2BrowsingDataKinds.AllSite);
|
|
}
|
|
finally
|
|
{
|
|
web.Dispose();
|
|
holder.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|