的OAuth2和DotNetOpenAuth - 谷歌实现自定义客户端 [英] OAuth2 and DotNetOpenAuth - implementing Google custom client

查看:181
本文介绍了的OAuth2和DotNetOpenAuth - 谷歌实现自定义客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在实现自定义OAuth2Client使用DotNetOpenAuth和MVC4谷歌的问题。

I'm having an issue implementing custom OAuth2Client for google using DotNetOpenAuth and MVC4.

我已经得到的地步,我可以成功地使授权请求谷歌端点
https://accounts.google.com/o/oauth2/auth

I've got to the point where I can successfully make the authorization request to the google endpoint https://accounts.google.com/o/oauth2/auth

和谷歌询问用户是否允许我对他们的帐户应用访问。所有好为止。当用户点击确定,谷歌然后调用预期我的回调URL。

and Google asks if the user will allow my application access to their account. All good so far. When the user clicks 'OK', google then calls my callback URL as expected.

问题是,当我称之为VerifyAuthentication方法上OAuthWebSecurity类(Microsoft.Web.WebPages.OAuth)

The problem is when I call the VerifyAuthentication method on the OAuthWebSecurity class (Microsoft.Web.WebPages.OAuth)

var authenticationResult = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

它总是返回与AuthenticationResult IsSuccessful = FALSE 提供商=

我已经看了到code这一点,和OAuthWebSecurity类尝试获得

I've looked into the code for this, and the OAuthWebSecurity class tries to get the Provider name from

Request.QueryString["__provider__"]

但谷歌不会将这些信息发送回查询字符串。其他的供应商我已经实现(LinkedIn)发送提供者的名称后面,这一切工作得很好。

but Google is not sending this information back in the querystring. The other provider I have implemented (LinkedIn) is sending the provider name back and it all works just fine.

我不知道我从这点可以做,除了抛弃Microsoft.Web.WebPages.OAuth类,只是使用DotNetOpenAuth没有他们,但我希望有人可能有另一种解决方案,我可以试试...

I'm not sure what I can do from this point, apart from abandon the Microsoft.Web.WebPages.OAuth classes and just use DotNetOpenAuth without them, but I was hoping someone might have another solution I can try...

我已经广泛搜查,但似乎无法找到任何事情来帮助...我发现它真的很难,甚至随便找人做同样的事情,这真的让我感到惊讶的例子。

I've searched extensively, but can't seem to find anything to help ... I've found it really difficult even just to find examples of people doing the same thing, which has really surprised me.

任何帮助非常AP preciated!

Any help much appreciated!

推荐答案

更​​新:马特·约翰逊提到下面,他已经打包到一个解决方案,你可以从GitHub获得:的 https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2

Update: As Matt Johnson mentions below he has packaged up a solution to this which you can get from GitHub: https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2

正如他指出:
DNOA和OAuthWebSecurity用于ASP.Net MVC 4只随附一个OpenID提供商谷歌。这是一个OAuth2用户端,您可以使用来代替。

As he notes: DNOA and OAuthWebSecurity for ASP.Net MVC 4 ship with only an OpenId provider for Google. This is an OAuth2 client that you can use instead.

重要 - 如果你正在使用ASP.Net MVC 5,这个包不适用。您应该改用Microsoft.Owin.Security.Google。 (它还附带在VS 2013的MVC 5入门模板。)

IMPORTANT - If you are using ASP.Net MVC 5, this package is not applicable. You should use Microsoft.Owin.Security.Google instead. (It also ships with the MVC 5 starter templates in VS 2013.)

我得到了这一轮最终通过捕获请求时,它进来,并做我自己检查,看它来自哪个供应商。谷歌允许你发送一个参数名为'国'的OAuth请求,他们简单地传递直接回到你的时候,他们做出的回调,所以我用这个来传递提供者的名称为谷歌,我在检查该没有的__ __提供商

I got round this in the end by catching the request when it comes in, and doing my own check to see which provider it has come from. Google allow you to send a parameter to the OAuth request called 'state', which they simply pass straight back to you when they make the callback, so I'm using this to pass the provider name for google, and I check for this in the absence of the "__provider__".

是这样的:

 public String GetProviderNameFromQueryString(NameValueCollection queryString)
    {
        var result = queryString["__provider__"];

        if (String.IsNullOrWhiteSpace(result))
        {
            result = queryString["state"];
        }

        return result;
    }

我然后实施了谷歌定制的OAuth2Client,我手动调用上的VerifyAuthentication自己的方法,绕过微软的包装材料。

I've then implemented a custom OAuth2Client for Google, and I manually call the VerifyAuthentication method on that myself, bypassing the Microsoft wrapper stuff.

 if (provider is GoogleCustomClient)
        {
            authenticationResult = ((GoogleCustomClient)provider).VerifyAuthentication(context, new Uri(String.Format("{0}/oauth/ExternalLoginCallback", context.Request.Url.GetLeftPart(UriPartial.Authority).ToString())));
        }
        else
        {
            authenticationResult = OAuthWebSecurity.VerifyAuthentication(returnUrl);
        } 

这也让我使用Microsoft包装保持的东西,我已经有到位的其他供应商。

This has allowed me to keep the stuff I already had in place for the other providers using the Microsoft wrappers.

由于所要求的@ 1010100 1001010,这是我为谷歌定制OAuth2Client(注:它需要一些整理我还没有得到全面YET整理THE code UP它的工作,虽然!):

As requested by @1010100 1001010, here is my custom OAuth2Client for Google (NOTE: IT NEEDS SOME TIDYING! I HAVEN'T GOT ROUND TO TIDYING THE CODE UP YET. It does work though) :

public class GoogleCustomClient : OAuth2Client
{
    ILogger _logger;

    #region Constants and Fields

    /// <summary>
    /// The authorization endpoint.
    /// </summary>
    private const string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/auth";

    /// <summary>
    /// The token endpoint.
    /// </summary>
    private const string TokenEndpoint = "https://accounts.google.com/o/oauth2/token";

    /// <summary>
    /// The _app id.
    /// </summary>
    private readonly string _clientId;

    /// <summary>
    /// The _app secret.
    /// </summary>
    private readonly string _clientSecret;

    #endregion


    public GoogleCustomClient(string clientId, string clientSecret)
        : base("Google")
    {
        if (string.IsNullOrWhiteSpace(clientId)) throw new ArgumentNullException("clientId");
        if (string.IsNullOrWhiteSpace(clientSecret)) throw new ArgumentNullException("clientSecret");

        _logger = ObjectFactory.GetInstance<ILogger>();

        this._clientId = clientId;
        this._clientSecret = clientSecret;
    }

    protected override Uri GetServiceLoginUrl(Uri returnUrl)
    {
        StringBuilder serviceUrl = new StringBuilder();

        serviceUrl.AppendFormat("{0}?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile", AuthorizationEndpoint);
        serviceUrl.Append("&state=google");
        serviceUrl.AppendFormat("&redirect_uri={0}", returnUrl.ToString());
        serviceUrl.Append("&response_type=code");
        serviceUrl.AppendFormat("&client_id={0}", _clientId);

        return new Uri(serviceUrl.ToString());

    }

    protected override IDictionary<string, string> GetUserData(string accessToken)
    {
        RestClient client = new RestClient("https://www.googleapis.com");
        var request = new RestRequest(String.Format("/oauth2/v1/userinfo?access_token={0}", accessToken), Method.GET);
        IDictionary<String, String> extraData = new Dictionary<String, String>();

        var response = client.Execute(request);
        if (null != response.ErrorException)
        {
            return null;
        }
        else
        {
            try
            {
                var json = JObject.Parse(response.Content);

                string firstName = (string)json["given_name"];
                string lastName = (string)json["family_name"];
                string emailAddress = (string)json["email"];
                string id = (string)json["id"];

                extraData = new Dictionary<String, String>
                {
                    {"accesstoken", accessToken}, 
                    {"name", String.Format("{0} {1}", firstName, lastName)},
                    {"firstname", firstName},
                    {"lastname", lastName},
                    {"email", emailAddress},
                    {"id", id}                                           
                };
            }
            catch(Exception ex)
            {
                _logger.Error("Error requesting OAuth user data from Google", ex);
                return null;
            }
            return extraData;
        }

    }

    protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
    {
        StringBuilder postData = new StringBuilder();
        postData.AppendFormat("client_id={0}", this._clientId);
        postData.AppendFormat("&redirect_uri={0}", HttpUtility.UrlEncode(returnUrl.ToString()));
        postData.AppendFormat("&client_secret={0}", this._clientSecret);
        postData.AppendFormat("&grant_type={0}", "authorization_code");
        postData.AppendFormat("&code={0}", authorizationCode);


        string response = "";
        string accessToken = "";

        var webRequest = (HttpWebRequest)WebRequest.Create(TokenEndpoint);

        webRequest.Method = "POST";
        webRequest.ContentType = "application/x-www-form-urlencoded";

        try
        {

            using (Stream s = webRequest.GetRequestStream())
            {
                using (StreamWriter sw = new StreamWriter(s))
                    sw.Write(postData.ToString());
            }

            using (WebResponse webResponse = webRequest.GetResponse())
            {
                using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
                {
                    response = reader.ReadToEnd();
                }
            }

            var json = JObject.Parse(response);
            accessToken = (string)json["access_token"];
        }
        catch(Exception ex)
        {
            _logger.Error("Error requesting OAuth access token from Google", ex);
            return null;
        }

        return accessToken;

    }

    public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
    {

        string code = context.Request.QueryString["code"];
        if (string.IsNullOrEmpty(code))
        {
            return AuthenticationResult.Failed;
        }

        string accessToken = this.QueryAccessToken(returnPageUrl, code);
        if (accessToken == null)
        {
            return AuthenticationResult.Failed;
        }

        IDictionary<string, string> userData = this.GetUserData(accessToken);
        if (userData == null)
        {
            return AuthenticationResult.Failed;
        }

        string id = userData["id"];
        string name;

        // Some oAuth providers do not return value for the 'username' attribute. 
        // In that case, try the 'name' attribute. If it's still unavailable, fall back to 'id'
        if (!userData.TryGetValue("username", out name) && !userData.TryGetValue("name", out name))
        {
            name = id;
        }

        // add the access token to the user data dictionary just in case page developers want to use it
        userData["accesstoken"] = accessToken;

        return new AuthenticationResult(
            isSuccessful: true, provider: this.ProviderName, providerUserId: id, userName: name, extraData: userData);
    }

这篇关于的OAuth2和DotNetOpenAuth - 谷歌实现自定义客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆