【问题标题】:Google OAuth 2.0 for desktop apps for Windows without Admin privileges适用于没有管理员权限的 Windows 桌面应用程序的 Google OAuth 2.0
【发布时间】:2017-03-29 13:23:49
【问题描述】:

我听说过 Google 的 OAuth 交互现代化计划,此处描述:https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html

然后我查看了此处找到的适用于 Windows 的示例桌面应用程序:https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp。

它非常简单并且可以正常工作,但是当我在没有提升权限的情况下(作为非管理员)启动 Visual Studio 时,我发现 HttpListener 由于以下错误而无法启动:“访问被拒绝”。

事实证明,如果没有管理员权限,在环回地址 (127.0.0.1) 处启动 HttpListener 是不可能的。但是尝试 localhost 而不是 127.0.0.1 会导致成功。

我发现有一个特定的命令可以让 HttpListener 从给定的地址(和端口)开始:

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

但它也只能以管理员权限执行,所以它不是一个选项。

仍然 localhost 似乎是最好的选择,但 OAuth 2.0 for Mobile & Desktop Apps 就本节声明如下:

有关环回 IP 地址的更多信息,请参阅 redirect_uri 参数定义。也可以使用 localhost 代替环回 IP,但这可能会导致客户端防火墙出现问题。大多数(但不是全部)防火墙允许环回通信。

这就是为什么我对使用 localhost 有点怀疑的原因。所以我想知道在这种情况下谷歌推荐的方式是什么,因为我不打算仅仅出于这个原因以管理员身份运行我们的应用程序。

有什么想法吗?

【问题讨论】:

  • 我对这个问题的答案也很感兴趣。

标签: google-oauth


【解决方案1】:

例如,您可以使用 TcpListener 代替 HttpListener。它不需要海拔来聆听。

以下是此示例的修改摘录: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp

// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";

// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);

// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
    authorizationEndpoint,
    System.Uri.EscapeDataString(redirectURI),
    clientID,
    state,
    code_challenge,
    code_challenge_method);

// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);

// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();

// Read response.
var response = ReadString(client);

// Brings this app back to the foreground.
this.Activate();

// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
    client.Dispose();
    listener.Stop();

    Console.WriteLine("HTTP server stopped.");
});

// TODO: Check the response here to get the authorization code and verify the code challenge

读写方法为:

private string ReadString(TcpClient client)
{
    var readBuffer = new byte[client.ReceiveBufferSize];
    string fullServerReply = null;

    using (var inStream = new MemoryStream())
    {
        var stream = client.GetStream();

        while (stream.DataAvailable)
        {
            var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            if (numberOfBytesRead <= 0)
                break;

            inStream.Write(readBuffer, 0, numberOfBytesRead);
        }

        fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
    }

    return fullServerReply;
}

private Task WriteStringAsync(TcpClient client, string str)
{
    return Task.Run(() =>
    {
        using (var writer = new StreamWriter(client.GetStream(), Encoding.UTF8))
        {
            writer.Write("HTTP/1.0 200 OK");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Type: text/html; charset=UTF-8");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Length: " + str.Length);
            writer.Write(Environment.NewLine);
            writer.Write(Environment.NewLine);
            writer.Write(str);
        }
    });
}

【讨论】:

    猜你喜欢
    • 2020-06-18
    • 2012-11-25
    • 1970-01-01
    • 2013-07-20
    • 2011-08-12
    • 1970-01-01
    • 2015-05-07
    • 2012-01-12
    • 1970-01-01
    相关资源
    最近更新 更多