首页 > 解决方案 > 如何将用户信息从 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 类中的行。但是,我不知道如何将其重定向回最初调用弹出窗口的网站。有人可以帮忙吗?我迷路了。

标签: wpfgoogle-oauthcefsharp

解决方案


推荐阅读