[我不了解Facebook,但是通常(我在Google OAuth2和Azure AD以及Azure AD B2C上都有经验),身份验证提供程序允许您使用custom URI方案进行身份验证回调,类似于badcompany://auth
为了获得身份验证令牌,我最终实现了以下方案(所有代码均提供无担保,请勿随意复制。)
1。启动应用程序时注册URI处理程序
您可以通过在Windows注册表中的HKEY_CURRENT_USER/Software/Classes
中创建一个密钥来注册URI处理程序(因此不需要管理特权)>
- 密钥名称等于URI前缀,在我们的情况下为
badcompany
- 键包含一个名为
URL Protocol
的空字符串值> - 键包含图标的子键
DefaultIcon
(实际上我不知道这是否必要),我使用了当前可执行文件的路径 - 有一个子项
shell/open/command
,其默认值确定试图打开URI时要执行的命令的路径,请注意*,"%1"
是将URI传递给可执行文件所必需的。
this.EnsureKeyExists(Registry.CurrentUser, "Software/Classes/badcompany", "URL:BadCo Applications");
this.SetValue(Registry.CurrentUser, "Software/Classes/badcompany", "URL Protocol", string.Empty);
this.EnsureKeyExists(Registry.CurrentUser, "Software/Classes/badcompany/DefaultIcon", $"{location},1");
this.EnsureKeyExists(Registry.CurrentUser, "Software/Classes/badcompany/shell/open/command", $"\"{location}\" \"%1\"");
private void SetValue(RegistryKey rootKey, string keys, string valueName, string value)
{
var key = this.EnsureKeyExists(rootKey, keys);
key.SetValue(valueName, value);
}
private RegistryKey EnsureKeyExists(RegistryKey rootKey, string keys, string defaultValue = null)
{
if (rootKey == null)
{
throw new Exception("Root key is (null)");
}
var currentKey = rootKey;
foreach (var key in keys.Split('/'))
{
currentKey = currentKey.OpenSubKey(key, RegistryKeyPermissionCheck.ReadWriteSubTree)
?? currentKey.CreateSubKey(key, RegistryKeyPermissionCheck.ReadWriteSubTree);
if (currentKey == null)
{
throw new Exception("Could not get or create key");
}
}
if (defaultValue != null)
{
currentKey.SetValue(string.Empty, defaultValue);
}
return currentKey;
}
2。打开用于IPC的管道
由于必须将消息从程序的一个实例传递到另一个实例,因此必须打开可用于该目的的命名管道。
我在后台Task
中循环调用了此代码
private async Task<string> ReceiveTextFromPipe(CancellationToken cancellationToken)
{
string receivedText;
PipeSecurity ps = new PipeSecurity();
System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(System.Security.Principal.WellKnownSidType.WorldSid, null);
PipeAccessRule par = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
ps.AddAccessRule(par);
using (var pipeStream = new NamedPipeServerStream(this._pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 4096, 4096, ps))
{
await pipeStream.WaitForConnectionAsync(cancellationToken);
using (var streamReader = new StreamReader(pipeStream))
{
receivedText = await streamReader.ReadToEndAsync();
}
}
return receivedText;
}
3。确保仅启动应用程序once
可以使用Mutex
获取。
Mutex
当另一个实例获取了互斥锁时,该应用程序没有完全启动,但是
4。使用身份验证重定向URI调用的句柄- 如果是唯一(第一个)实例,它可能会处理身份验证重定向URI本身
使用令牌进行请求如果是另外一个实例第一个实例现在执行1。]下的步骤
关闭第二个实例URI通过]发送到第一个实例>
internal class SingleInstanceChecker
{
private static Mutex Mutex { get; set; }
public static async Task EnsureIsSingleInstance(string id, Action onIsSingleInstance, Func<Task> onIsSecondaryInstance)
{
SingleInstanceChecker.Mutex = new Mutex(true, id, out var isOnlyInstance);
if (!isOnlyInstance)
{
await onIsSecondaryInstance();
Application.Current.Shutdown(0);
}
else
{
onIsSingleInstance();
}
}
}
添加到保罗的出色答案:
using (var client = new NamedPipeClientStream(this._pipeName))
{
try
{
var millisecondsTimeout = 2000;
await client.ConnectAsync(millisecondsTimeout);
}
catch (Exception)
{
onSendFailed();
return;
}
if (!client.IsConnected)
{
onSendFailed();
}
using (StreamWriter writer = new StreamWriter(client))
{
writer.Write(stringToSend);
writer.Flush();
}
}
值得一看-他们将为您做的一件事是建议对本机应用程序使用授权码流(PKCE)- 我的偏好与Paul的偏好相同-使用自定义URI方案-可用性更好,我认为
话虽如此,环回解决方案应该在没有管理员权限的情况下可以用于大于1024的端口如果有帮助,我的博客上有一些关于此的内容-包括一个Nodejs / Electron示例,您可以从Identity Model Libraries运行以查看完成的解决方案是什么样。