wpf - 如何将用户信息从 Google OAuth 传递到 WPF CefSharp 浏览器中的请求网站
问题描述
所以这就是问题所在。我有一个正在使用 CefSharp 开发的浏览器,我正在尝试让浏览器正确地进行身份验证并将 UserInfo 传递回请求登录的网站。以下是他们在网站上单击的按钮:
正如现在所写的那样,我在实现 ILifeSpanHandler 接口的 PopUpHandler 类中捕获了这个登录事件。我专门在 OnBeforePopup 方法中捕获它,并运行我从互联网上下载的默认 OAuth 类。我稍微修改了 OAuth 类以实现自定义 cefsharp 浏览器弹出窗口,以让用户输入他们的凭据。
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName,
WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo,
IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
foreach (var allowedSite in Helper.AllowedPopups)
{
if (targetUrl.Contains(allowedSite))
{
if (targetUrl.Contains("login") && targetUrl.Contains("oauth"))
{
OAuth that = new OAuth();
that.AuthorizeWithGoogle();
newBrowser = null;
return true;
}
Helper.OpenNewTab(targetUrl, true);
newBrowser = null;
return true;
}
}
string askingURL = Helper.GetWebsiteName(targetUrl);
AllowPopUpNotifier allowThis = new AllowPopUpNotifier(askingURL);
if (allowThis.ShowDialog() == true)
{
Helper.AllowedPopups.Add(askingURL);
if (targetUrl.Contains("login") && targetUrl.Contains("oauth"))
{
var newPopUpWindow = new CustomPopUpWindow(targetUrl);
newPopUpWindow.Show();
newBrowser = null;
return true;
}
Helper.OpenNewTab(targetUrl, true);
newBrowser = null;
return true;
}
else
{
newBrowser = null;
return true;
}
}
然后,我实现的 OAuth 类如下所示:
public class OAuth
{
//client configuration;
const string clientID = "581786658708-elflankerquo1a6vsckabbhn25hclla0.apps.googleusercontent.com";
const string clientSecret = "3f6NggMbPtrmIBpgx-MK2xXK";
//const string clientID = "1020802206989-hsvquqebqgrj8kj4b387lcnosos4manc.apps.googleusercontent.com";
//const string clientSecret = "5k_uICJwwq25oV6-BM22310V";
const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
const string tokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
const string userInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
//ref http://stackoverflow.com/a/3978040
public static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
public async Task<UserCredential> getUserCredential()
{
UserCredential credential;
string[] scopes = new string[] { }; // user basic profile
//Read client id and client secret from Web config file
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "1020802206989-hsvquqebqgrj8kj4b387lcnosos4manc.apps.googleusercontent.com",
ClientSecret = "3f6NggMbPtrmIBpgx-MK2xXK"
}, scopes,
"user", CancellationToken.None, new FileDataStore("Auth.Api.Store"));
return credential;
}
public async void AuthorizeWithGoogle()
{
//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.
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
Debug.WriteLine("redirect URI: " + redirectURI);
//Creates an HttpListener to listen for requests on that redirect URI.
var http = new HttpListener();
http.Prefixes.Add(redirectURI);
Debug.WriteLine("Listening..");
http.Start();
//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,
Uri.EscapeDataString(redirectURI),
clientID,
state,
code_challenge,
code_challenge_method);
//Opens request in the browser.
//Process.Start(authorizationRequest);
//var newBrowser = new WebBrowser();
var that = new CustomPopUpWindow(authorizationRequest);
//that.browser.Address = authorizationRequest;
//newBrowser.Source = new Uri(authorizationRequest, UriKind.RelativeOrAbsolute);
//newBrowser.Load(authorizationRequest);
//that.Content = newBrowser;
that.Show();
//Waits for the OAuth authorization response.
var context = await http.GetContextAsync();
// Brings this app back to the foreground.
var mainWindow = Helper.GetMainWindow();
Application.Current.Dispatcher.Invoke(() =>
{
mainWindow.Activate();
});
//Sends an HTTP response to the browser.
var response = context.Response;
string responseString = string.Format("<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please return to the app.</body></html>");
var buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
responseOutput.Close();
http.Stop();
Console.WriteLine("HTTP server stopped.");
});
//Checks for errors.
if (context.Request.QueryString.Get("error") != null)
{
Debug.WriteLine(String.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
if (context.Request.QueryString.Get("code") == null
|| context.Request.QueryString.Get("state") == null)
{
Debug.WriteLine("Malformed authorization response. " + context.Request.QueryString);
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
//extracts the code
var code = context.Request.QueryString.Get("code");
var incoming_state = context.Request.QueryString.Get("state");
//Compares the receieved state to the expected value, to ensure that
//this app made the request which resulted in authorization.
if (incoming_state != state)
{
Debug.WriteLine(String.Format("Received request with invalid state ({0})", incoming_state));
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
Debug.WriteLine("Authorization code: " + code);
//Starts the code exchange at the Token Endpoint.
performCodeExchange(code, code_verifier, redirectURI);
}
async void performCodeExchange(string code, string code_verifier, string redirectURI)
{
Debug.WriteLine("Exchanging code for tokens...");
//builds the request
string tokenRequestURI = "https://www.googleapis.com/oauth2/v4/token";
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
code,
System.Uri.EscapeDataString(redirectURI),
clientID,
code_verifier,
clientSecret
);
//sends the request
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestURI);
tokenRequest.Method = "POST";
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
byte[] _byteVersion = Encoding.ASCII.GetBytes(tokenRequestBody);
tokenRequest.ContentLength = _byteVersion.Length;
Stream stream = tokenRequest.GetRequestStream();
await stream.WriteAsync(_byteVersion, 0, _byteVersion.Length);
stream.Close();
try
{
//gets the response
WebResponse tokenResponse = await tokenRequest.GetResponseAsync();
using (StreamReader reader = new StreamReader(tokenResponse.GetResponseStream()))
{
//reads response body
string responseText = await reader.ReadToEndAsync();
Debug.WriteLine(responseText);
//converts to dictionary
Dictionary<string, string> tokenEndpointDecoded = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);
string access_token = tokenEndpointDecoded["access_token"];
userinfoCall(access_token);
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
Debug.WriteLine("HTTP: " + response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
//reads response body
string responseText = await reader.ReadToEndAsync();
Debug.WriteLine(responseText);
Helper.GoogleAuthorized = false;
CustomMessageBox.Show("Google login not authorized. Check your login credentials and try again.");
return;
}
}
}
}
}
async void userinfoCall(string access_token)
{
Debug.WriteLine("Making API Call to Userinfo...");
//builds the request
string userinfoRequestURI = "https://www.googleapis.com/oauth2/v3/userinfo";
//sends the request
HttpWebRequest userinfoRequest = (HttpWebRequest)WebRequest.Create(userinfoRequestURI);
userinfoRequest.Method = "GET";
userinfoRequest.Headers.Add(string.Format("Authorization: Bearer {0}", access_token));
userinfoRequest.ContentType = "application/x-www-form-urlencoded";
userinfoRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
//gets the response
WebResponse userinfoResponse = await userinfoRequest.GetResponseAsync();
using (StreamReader userinfoResponseReader = new StreamReader(userinfoResponse.GetResponseStream()))
{
//reads response body
string userinfoResponseText = await userinfoResponseReader.ReadToEndAsync();
Debug.WriteLine(userinfoResponseText);
}
}
/// <summary>
/// Appends the given string to the on-screen log, and the debug console.
/// </summary>
/// <param name = "output" > string to be appended</param>
public void output(string output)
{
//textBoxOutput.Text = textBoxOutput.Text + output + Environment.NewLine;
Console.WriteLine(output);
}
/// <summary>
/// Returns URI-safe data with a given input length.
/// </summary>
/// <param name = "length" > Input length(nb.output will be longer)</param>
/// <returns></returns>
public static string randomDataBase64url(uint length)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[length];
rng.GetBytes(bytes);
return base64urlencodeNoPadding(bytes);
}
/// <summary>
/// Returns the SHA256 hash of the input string.
/// </summary>
/// <param name = "inputStirng" ></ param >
/// < returns ></ returns >
public static byte[] sha256(string inputStirng)
{
byte[] bytes = Encoding.ASCII.GetBytes(inputStirng);
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(bytes);
}
/// <summary>
/// Base64url no-padding encodes the given input buffer.
/// </summary>
/// <param name = "buffer" ></ param >
/// < returns ></ returns >
public static string base64urlencodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
//Converts base64 to base64url.
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
//Strips padding.
base64 = base64.Replace("=", "");
return base64;
}
}
}
这让我得到了 UserInfo,并将其写入“Debug.WriteLine(userinfoResponseText);” OAuth 类中的行。但是,我不知道如何将其重定向回最初调用弹出窗口的网站。有人可以帮忙吗?我迷路了。
解决方案
推荐阅读
- ios - 设置视图控制器的视图属性和该视图的 tableview 子视图的掩码会导致两个看起来不同的掩码
- sql - 使用查询从变量中提取数字字符串
- android - 滑动以从 recyclerView 中删除或编辑项目
- python - 尝试加载 Librosa 的示例文件时找不到文件错误
- laravel - 使用工厂 laravel 存储假货
- volttron - 连接到联邦时如何在下游存储数据?
- javascript - 条件渲染标记 (JSX) 与 CSS `display: none` - 哪个更好?
- java - Pixelart 的抗锯齿算法
- c++ - 如何将此 gdi+ 示例正确链接到库文件?
- bazel - bazel 升级后构建中断