使用Google凭据登录UWP C#app

我正在尝试登录我正在为一个客户开发的UWP应用程序,该客户端有一个使用G Suite的@.com电子邮件。 它不必访问任何用户数据,他们只是希望它作为身份validation,以便只有拥有公司电子邮件的人才能访问该应用程序。

如果他们可以在不使用网络浏览器的情况下从应用程序内登录,那将会很棒,如果它能记住它们就更好,这样他们就不必每次都登录。

我一直在关注OAuth 2.0和谷歌的其他几个解决方案,但无法真正理解使用哪个,更不用说如何使用。

我查看了这个答案,但使用您的应用程序发送证书文件似乎不是一个好主意。

所以基本上如果可以这样做,我需要从Google获得哪些(如果有的话)证书或凭证,以及如何处理它们以及通过我的C#代码登录?

编辑

该应用程序是100%客户端,没有服务器后端

看看谷歌的GitHub ,似乎.Net API还没有为UWP做好准备(但如果你遍历这些问题,你会发现他们正在研究它,所以正式版本准备就绪并且这个答案可能是个问题。会过时的)。

因为我认为将简单的accessToken (可选地刷新它)转换为基本的配置文件信息应该足以满足这种情况。 基于Google的可用样本我已经构建了一个小项目(GitHub上的源代码) ,可以帮助您。

首先,您必须在Google的开发者控制台中定义您的应用并获取ClientIDClientSecret 。 一旦你有了这个,你就可以进行编码。 要获取accessToken,我将使用WebAuthenticationBroker

 string authString = "https://accounts.google.com/o/oauth2/auth?client_id=" + ClientID; authString += "&scope=profile"; authString += $"&redirect_uri={RedirectURI}"; authString += $"&state={state}"; authString += $"&code_challenge={code_challenge}"; authString += $"&code_challenge_method={code_challenge_method}"; authString += "&response_type=code"; var receivedData = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, new Uri(authString), new Uri(ApprovalEndpoint)); switch (receivedData.ResponseStatus) { case WebAuthenticationStatus.Success: await GetAccessToken(receivedData.ResponseData.Substring(receivedData.ResponseData.IndexOf(' ') + 1), state, code_verifier); return true; case WebAuthenticationStatus.ErrorHttp: Debug.WriteLine($"HTTP error: {receivedData.ResponseErrorDetail}"); return false; case WebAuthenticationStatus.UserCancel: default: return false; } 

如果一切顺利且用户输入正确的凭据,您将不得不向Google要求令牌(我假设您只希望用户输入一次凭据)。 为此,您使用方法GetAccessToken

 // Parses URI params into a dictionary - ref: http://stackoverflow.com/a/11957114/72176 Dictionary queryStringParams = data.Split('&').ToDictionary(c => c.Split('=')[0], c => Uri.UnescapeDataString(c.Split('=')[1])); StringContent content = new StringContent($"code={queryStringParams["code"]}&client_secret={ClientSecret}&redirect_uri={Uri.EscapeDataString(RedirectURI)}&client_id={ClientID}&code_verifier={codeVerifier}&grant_type=authorization_code", Encoding.UTF8, "application/x-www-form-urlencoded"); HttpResponseMessage response = await httpClient.PostAsync(TokenEndpoint, content); string responseString = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { Debug.WriteLine("Authorization code exchange failed."); return; } JsonObject tokens = JsonObject.Parse(responseString); accessToken = tokens.GetNamedString("access_token"); foreach (var item in vault.RetrieveAll().Where((x) => x.Resource == TokenTypes.AccessToken.ToString() || x.Resource == TokenTypes.RefreshToken.ToString())) vault.Remove(item); vault.Add(new PasswordCredential(TokenTypes.AccessToken.ToString(), "MyApp", accessToken)); vault.Add(new PasswordCredential(TokenTypes.RefreshToken.ToString(), "MyApp", tokens.GetNamedString("refresh_token"))); TokenLastAccess = DateTimeOffset.UtcNow; 

一旦你有了令牌(为了安全起见我将它们保存在PasswordVault中),你可以稍后使用它们进行身份validation,而无需询问用户的凭据。 请注意, accessToken的生命周期有限,因此您使用refreshToken来获取新的:

 if (DateTimeOffset.UtcNow < TokenLastAccess.AddSeconds(3600)) { // is authorized - no need to Sign In return true; } else { string token = GetTokenFromVault(TokenTypes.RefreshToken); if (!string.IsNullOrWhiteSpace(token)) { StringContent content = new StringContent($"client_secret={ClientSecret}&refresh_token={token}&client_id={ClientID}&grant_type=refresh_token", Encoding.UTF8, "application/x-www-form-urlencoded"); HttpResponseMessage response = await httpClient.PostAsync(TokenEndpoint, content); string responseString = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { JsonObject tokens = JsonObject.Parse(responseString); accessToken = tokens.GetNamedString("access_token"); foreach (var item in vault.RetrieveAll().Where((x) => x.Resource == TokenTypes.AccessToken.ToString())) vault.Remove(item); vault.Add(new PasswordCredential(TokenTypes.AccessToken.ToString(), "MyApp", accessToken)); TokenLastAccess = DateTimeOffset.UtcNow; return true; } } } 

上面的代码只是一个示例(带有一些快捷方式),如上所述 – 一个工作版本,有一些error handling,你可以在我的GitHub上找到。 还请注意,我没有花太多时间在这上面,它肯定需要更多的工作来处理所有的情况和可能的问题。 虽然希望能帮助你开始。

Roamsz的答案很棒,但对我没有用,因为我发现了一些冲突,或者至少以最新版本17134作为目标,它不起作用。 这是问题,在他的Github示例中,他使用returnurl作为urn:ietf:wg:oauth:2.0:oob 。 这是url的类型,当您在google或firebase控制台中创建新的“创建OAuth客户端ID”时,您无法使用Web应用程序类型。 你必须使用“Ios”,如下所示。 因为Web应用程序需要http或https url作为返回URL。

来自谷歌文档

在此处输入图像描述

在此处输入图像描述

根据他的样本,他使用客户端密钥来获取访问令牌,如果您将Ios创建为类型,这是不可能的。 因为Android和Ios不使用客户端密钥。 这里有完美的描述

client_secret从API控制台获取的客户端密钥。 注册为Android,iOS或Chrome应用程序的客户端不需要此值。

所以你必须使用类型作为Ios,不需要客户端密码并且返回url是urn:ietf:wg:oauth:2.0:ooburn:ietf:wg:oauth:2.0:oob:auto difference是自动关闭浏览器并返回到应用程序。 另外,代码需要手动复制。 我更喜欢使用urn:ietf:wg:oauth:2.0:oob:auto

关于代码:请关注他的github代码 。 只需从访问令牌请求中删除客户端密钥。

编辑:看起来我是对的,即使官方样本在UWP版本15063之后无效,有人在他们的github上创建了一个问题

https://github.com/Microsoft/Windows-universal-samples/issues/642