Asp.net mvc 4 OAuth2和DotNetOpenAuth-实现Google自定义客户端
我在使用DotNetOpenAuth和MVC4为google实现自定义OAuth2Client时遇到问题 我已经到了可以成功向google端点发出授权请求的地步 谷歌询问用户是否允许我的应用程序访问他们的帐户。到目前为止一切都很好。当用户单击“OK”时,google会按预期调用我的回调URL 问题是当我在OAuthWebSecurity类(Microsoft.Web.WebPages.OAuth)上调用VerifyAuthentication方法时 它总是返回带有Asp.net mvc 4 OAuth2和DotNetOpenAuth-实现Google自定义客户端,asp.net-mvc-4,oauth-2.0,dotnetopenauth,Asp.net Mvc 4,Oauth 2.0,Dotnetopenauth,我在使用DotNetOpenAuth和MVC4为google实现自定义OAuth2Client时遇到问题 我已经到了可以成功向google端点发出授权请求的地步 谷歌询问用户是否允许我的应用程序访问他们的帐户。到目前为止一切都很好。当用户单击“OK”时,google会按预期调用我的回调URL 问题是当我在OAuthWebSecurity类(Microsoft.Web.WebPages.OAuth)上调用VerifyAuthentication方法时 它总是返回带有issucessful=fal
issucessful=false和Provider=“”
我已经研究了这方面的代码,OAuthWebSecurity类试图从中获取提供者名称
Request.QueryString["__provider__"]
但谷歌并没有将这些信息发送回查询字符串。我实现的另一个提供商(LinkedIn)正在发回提供商名称,一切正常
我不确定从这一点上我能做什么,除了放弃Microsoft.Web.WebPages.OAuth类,只使用没有它们的DotNetOpenAuth,但我希望有人能有另一个解决方案,我可以尝试
我搜索了很多,但似乎找不到任何帮助。。。我发现很难找到人们做同样事情的例子,这让我很惊讶
非常感谢任何帮助 更新:正如Matt Johnson在下面提到的,他已经打包了一个解决方案,您可以从GitHub获得:
正如他指出的那样:
用于ASP.NETMVC4的DNOA和OAuthWebSecurity只附带一个用于Google的OpenId提供程序。这是一个OAuth2客户机,您可以改用它
重要信息-如果您使用的是ASP.NETMVC5,则此软件包不适用。您应该改用Microsoft.Owin.Security.Google。(在VS 2013中,它还附带MVC 5初学者模板。)
我最终绕过了这个问题,在请求传入时捕获它,并自己检查它来自哪个提供商。Google允许您向OAuth请求发送一个名为“state”的参数,当他们进行回调时,会直接将该参数传递给您,因此我使用该参数传递Google的提供者名称,并在没有“\uuuu provider\uuuu”
的情况下检查该参数
大概是这样的:
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 wrappers的提供商保留我已经拥有的东西
根据@1010100 1001010的请求,这里是我为谷歌定制的OAuth2Client(注意:它需要一些整理!我还没有时间整理代码。不过它确实可以工作):
公共类GoogleCustomClient:OAuth2Client
{
ILogger\u记录器;
#区域常数和字段
///
///授权端点。
///
私有常量字符串授权端点=”https://accounts.google.com/o/oauth2/auth";
///
///令牌终结点。
///
私有常量字符串标记端点=”https://accounts.google.com/o/oauth2/token";
///
///_应用程序id。
///
私有只读字符串\u clientId;
///
///应用程序的秘密。
///
私有只读字符串_clientSecret;
#端区
公共GoogleCustomClient(字符串clientId、字符串clientSecret)
:base(“谷歌”)
{
if(string.IsNullOrWhiteSpace(clientId))抛出新的ArgumentNullException(“clientId”);
if(string.IsNullOrWhiteSpace(clientSecret))抛出新的ArgumentNullException(“clientSecret”);
_logger=ObjectFactory.GetInstance();
这个._clientId=clientId;
这个._clientSecret=clientSecret;
}
受保护的覆盖Uri GetServiceLoginUrl(Uri返回URL)
{
StringBuilder serviceUrl=新的StringBuilder();
serviceUrl.AppendFormat(“{0}?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile”,AuthorizationEndpoint);
Append(“&state=google”);
AppendFormat(“&redirect_uri={0}”,returnUrl.ToString());
Append(“&response_type=code”);
AppendFormat(“&client_id={0}”,_clientId);
返回新的Uri(serviceUrl.ToString());
}
受保护的重写IDictionary GetUserData(字符串accessToken)
{
RestClient=新的RestClient(“https://www.googleapis.com");
var request=new RestRequest(String.Format(“/oauth2/v1/userinfo?access_token={0}”,accessToken),Method.GET);
IDictionary extraData=新字典();
var response=client.Execute(请求);
if(null!=response.ErrorException)
{
返回null;
}
其他的
{
尝试
{
var json=JObject.Parse(response.Content);
string firstName=(string)json[“给定名称”];
string lastName=(string)json[“family_name”];
字符串emailAddress=(字符串)json[“email”];
字符串id=(字符串)json[“id”];
extraData=新字典
{
{“accesstoken”,accesstoken},
{“name”,String.Format(“{0}{1}”,firstName,lastName)},
{“firstname”,firstname},
{“lastname”,lastname},
{“email”,emailAddress},
{“id”,id}
};
}
捕获(例外情况除外)
{
_logger.Error(“从Google请求OAuth用户数据时出错”,例如);
返回null;
}
返回外部数据;
}
}
受保护的覆盖字符串QueryAccessToken(Uri返回URL,字符串授权代码)
{
StringBuild
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);
}
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);
}