Поток кода авторизации с Identitity4 и OidcClient
Для настольного приложения Winforms я буду использовать поток кода авторизации с PKCE. В качестве поставщика удостоверений я использую IdentityServer, а в качестве клиентской библиотеки - OicdClient. Следующим шагом я должен решить, какой браузер использовать для входа пользователя:
SystemBrowser говорит о простой / понятной реализации потока. Для Extended WebBrowser говорится, что у какого-то пользователя может не быть SystemBrowser. Но WebBrowser - это более старая версия IE? и можно ли его использовать для безопасной аутентификации?
Тем не менее я попробовал образец "Extended WebBrowser" и не смог интегрировать его в свою среду прототипа с собственным сервером IS4. Поэтому мне нужна некоторая ясность с потоком кода и перенаправлением. Я уже реализовал этот поток кода авторизации с чистыми классами.Net, но использование OicdClient немного сбивает меня с толку (вначале как черный ящик).
Мой вопрос: как перенаправление работает с этими библиотеками, и кто отвечает за перенаправление и кто отвечает за получение перенаправления с кодом (для обмена на токен доступа)?
Поток кода состоит из следующих шагов (без таких деталей, как clientID, PKCE ...):
- Отправить запрос кода в IS4
- Ответ IS4 со страницей входа (отображается в браузере)
- После успешного входа в систему IS4 отправляет URL-адрес перенаправления с кодом
- HttpListener получает это перенаправление с кодом и
- Отправьте запрос в IS4 с кодом для получения токена доступа
С OidcClient и в автоматическом режиме:
var options = new OidcClientOptions
{
Authority = "https://demo.identityserver.io",
ClientId = "native",
RedirectUri = redirectUri,
Scope = "openid profile api",
Browser = new SystemBrowser()
};
var client = new OidcClient(options);
var result = await client.LoginAsync();
Для меня здесь много волшебства. Только вызов LoginAsync() заставляет его работать...
Важным моментом, по-видимому, является свойство Browser опций с интерфейсом IBrowser и его реализация этого метода:
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken)
{
using (var listener = new LoopbackHttpListener(Port, _path))
{
OpenBrowser(options.StartUrl);
try
{
var result = await listener.WaitForCallbackAsync();
if (String.IsNullOrWhiteSpace(result))
{
return new BrowserResult { ResultType = BrowserResultType.UnknownError, Error = "Empty response." };
}
return new BrowserResult { Response = result, ResultType = BrowserResultType.Success };
}
catch (TaskCanceledException ex)
{ ....}
}
}
если я попытаюсь сопоставить шаги потока:
- Страница входа: OpenBrowser(options.StartUrl);
- Редирект будет делать IS4? SystemBrowser из образца этого не делает.
- Получите код: await listener.WaitForCallbackAsync();
1 и 5, вероятно, выполняются OicdClient. Этот пример довольно ясен, необходимо подтвердить, что перенаправление выполняется IS4.
Реализация в другом примере Extended WebBrowser
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
using (var form = _formFactory.Invoke())
using (var browser = new ExtendedWebBrowser()
{
Dock = DockStyle.Fill
})
{
var signal = new SemaphoreSlim(0, 1);
var result = new BrowserResult
{
ResultType = BrowserResultType.UserCancel
};
form.FormClosed += (o, e) =>
{
signal.Release();
};
browser.NavigateError += (o, e) =>
{
e.Cancel = true;
if (e.Url.StartsWith(options.EndUrl))
{
result.ResultType = BrowserResultType.Success;
result.Response = e.Url;
}
else
{
result.ResultType = BrowserResultType.HttpError;
result.Error = e.StatusCode.ToString();
}
signal.Release();
};
browser.BeforeNavigate2 += (o, e) =>
{
var b = e.Url.StartsWith(options.EndUrl);
if (b)
{
e.Cancel = true;
result.ResultType = BrowserResultType.Success;
result.Response = e.Url;
signal.Release();
}
};
form.Controls.Add(browser);
browser.Show();
System.Threading.Timer timer = null;
form.Show();
browser.Navigate(options.StartUrl);
await signal.WaitAsync();
if (timer != null) timer.Change(Timeout.Infinite, Timeout.Infinite);
form.Hide();
browser.Hide();
return result;
}
}
- Сделано: browser.Navigate(options.StartUrl);
- Перенаправление через IS4
- Получение кода в обработчике события: NavigateError???
Здесь что-то не так? На IS4 вызывается AccountController.Login, который вызывает / соединяет / авторизует / выполняет обратный вызов? с помощью redirect_uri. Но это не касается BeforeNavigate2. вместо этого появляется событие NavigateError, где установлен результат:
result.ResultType = BrowserResultType.Success;
result.Response = e.Url;
1 ответ
В настоящее время рекомендуется использовать веб-браузер пользователя по умолчанию, а не встраивать компонент браузера. Что касается того, как это реализовать - поскольку вы не можете перехватывать события навигации браузера с помощью этого подхода, вам необходимо реализовать HTTP-прослушиватель, который может принимать запрос POST от вашегоidentityserver4
реализация.
Прочтите это: https://auth0.com/blog/oauth-2-best-practices-for-native-apps/
И этот RFC: https://tools.ietf.org/html/rfc8252