使用 Identitity4 和 OidcClient 的授权代码流
Authorization code flow with Identitity4 and OidcClient
对于 Winforms 桌面应用程序,我将使用 PKCE 的授权代码流。作为身份提供者,我使用 IdentityServer and as client library OicdClient。
下一步我必须决定用户登录使用哪个浏览器:
对于SystemBrowser来说simple/clear流程的实现。
对于 Extended WebBrowser 来说,有些用户可能没有 SystemBrowser。但是 WebBrowser 是较旧的 IE 版本?是否允许用于安全身份验证?
尽管如此,我还是尝试了“Extended WebBrowser”示例,并无意中将其集成到我的带有自己的 IS4 服务器的原型环境中。因此,我需要清楚地了解代码流和重定向。
我已经用纯 .Net 类 实现了这个授权代码流,但是使用 OicdClient 让我有点困惑(一开始就像一个黑盒子)。
我的问题是重定向如何与这个库一起工作,谁负责重定向,谁负责接收带有代码的重定向(以交换访问令牌)?
代码流有以下步骤(没有 clientID、PKCE 等细节......):
- 向 IS4 发送代码请求
- 带有登录页面的 IS4 响应(在浏览器中显示)
- 成功登录后,IS4 发送代码
重定向URL
- HttpListener 接收此重定向代码和
- 使用接收访问令牌的代码向 IS4 发送请求
使用 OidcClient 并使用 Automatic Mode:
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 被称为
调用 /connect/authorize/callback?与 redirect_uri。
但这不会出现在 BeforeNavigate2 中。相反,NavigateError 事件出现在结果设置为:
的位置
result.ResultType = BrowserResultType.Success;
result.Response = e.Url;
当前的最佳做法是使用用户的默认网络浏览器,而不是嵌入浏览器组件。至于如何实现它 - 由于您无法使用这种方法拦截浏览器导航事件,因此您需要实现一个 HTTP 侦听器,该侦听器可以接受来自 identityserver4
实现的 POST 请求。
读一读:https://auth0.com/blog/oauth-2-best-practices-for-native-apps/
对于 Winforms 桌面应用程序,我将使用 PKCE 的授权代码流。作为身份提供者,我使用 IdentityServer and as client library OicdClient。 下一步我必须决定用户登录使用哪个浏览器:
对于SystemBrowser来说simple/clear流程的实现。 对于 Extended WebBrowser 来说,有些用户可能没有 SystemBrowser。但是 WebBrowser 是较旧的 IE 版本?是否允许用于安全身份验证?
尽管如此,我还是尝试了“Extended WebBrowser”示例,并无意中将其集成到我的带有自己的 IS4 服务器的原型环境中。因此,我需要清楚地了解代码流和重定向。 我已经用纯 .Net 类 实现了这个授权代码流,但是使用 OicdClient 让我有点困惑(一开始就像一个黑盒子)。
我的问题是重定向如何与这个库一起工作,谁负责重定向,谁负责接收带有代码的重定向(以交换访问令牌)?
代码流有以下步骤(没有 clientID、PKCE 等细节......):
- 向 IS4 发送代码请求
- 带有登录页面的 IS4 响应(在浏览器中显示)
- 成功登录后,IS4 发送代码 重定向URL
- HttpListener 接收此重定向代码和
- 使用接收访问令牌的代码向 IS4 发送请求
使用 OidcClient 并使用 Automatic Mode:
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 被称为 调用 /connect/authorize/callback?与 redirect_uri。 但这不会出现在 BeforeNavigate2 中。相反,NavigateError 事件出现在结果设置为:
的位置result.ResultType = BrowserResultType.Success;
result.Response = e.Url;
当前的最佳做法是使用用户的默认网络浏览器,而不是嵌入浏览器组件。至于如何实现它 - 由于您无法使用这种方法拦截浏览器导航事件,因此您需要实现一个 HTTP 侦听器,该侦听器可以接受来自 identityserver4
实现的 POST 请求。
读一读:https://auth0.com/blog/oauth-2-best-practices-for-native-apps/