asp.net-mvc-4 – OAuth2和DotNetOpenAuth – 实现Google自定义客户端

我在使用DotNetopenAuth和MVC4实现自定义oauth2client for google的问题.

我已经到了这一点,我可以成功地向google终端发出授权请求
https://accounts.google.com/o/oauth2/auth

并且Google询问用户是否允许我的应用访问其帐户.到目前为止都不错当用户点击“确定”后,Google会按预期的方式调用回调URL.

问题是当我在OAuthWebSecurity类(Microsoft.Web.WebPages.OAuth)上调用VerifyAuthentication方法时,

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

它总是返回一个AuthenticationResult与IsSuccessful = false和Provider =“”

我已经查看了这个代码,OAuthWebSecurity类尝试从中获取提供者名称

Request.QueryString["__provider__"]

但是Google并没有在查询字符串中发送此信息.我已经实施的另一个提供商(LinkedIn)正在发送提供商名称,它一切正常.

我不知道我可以做什么,除了放弃Microsoft.Web.WebPages.OAuth类,只是使用DotNetopenAuth没有他们,但我希望有人可能有另一个解决方案,我可以尝试…

我已经广泛搜索,但似乎找不到任何帮助…我发现真的很难,只是找到人做同样的事情的例子,这真的让我感到惊讶.

任何帮助非常感谢!

解决方法

更新:正如Matt Johnson提到的,他已经打包了一个可以从GitHub: https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2获得的解决方

他说:
针对ASP.Net MVC 4的DNOA和OAuthWebSecurity仅提供给Google的OpenId提供程序.这是一个您可以使用的OAuth2客户端.

重要 – 如果您使用ASP.Net MVC 5,此包不适用.您应该使用Microsoft.Owin.Security.Google. (它也附带VS 2013中的MVC 5启动器模板)

最后我得到了这个结果,通过捕获请求进来,并做我自己的检查,看看它是哪个提供者. Google允许您将参数发送到名为“state”的OAuth请求,当它们进行回调时,它们将直接传递给您,因此我使用此参数传递google的提供程序名称,并且我检查没有“__provider__”.

这样的:

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

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

        return result;
    }

然后,我为Google实现了一个自定义oauth2client,我自己手动调用VerifyAuthentication方法,绕过了Microsoft的包装器.

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包装器的东西.

根据@ 1010100 1001010的要求,这里是我自定义oauth2client for Google(注意:需要一些TIDYING!我没有办法处理代码上升,它的工作虽然):

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);
    }

相关文章

### 创建一个gRPC服务项目(grpc服务端)和一个 webapi项目(...
一、SiganlR 使用的协议类型 1.websocket即时通讯协议 2.Ser...
.Net 6 WebApi 项目 在Linux系统上 打包成Docker镜像,发布为...
一、 PD简介PowerDesigner 是一个集所有现代建模技术于一身的...
一、存储过程 存储过程就像数据库中运行的方法(函数) 优点:...
一、Ueditor的下载 1、百度编辑器下载地址:http://ueditor....