我在为谷歌使用DotNetOpenAuth和MVC4问题实现自定义OAuth2Client。
我已经得到的地步,我可以成功地使授权请求谷歌端点https://accounts.google.com/o/oauth2/auth
和谷歌询问用户是否允许我到他们的帐户应用访问。 所有好为止。 当用户点击“确定”,谷歌然后调用我的回调URL预期。
问题是,当我呼吁OAuthWebSecurity类VerifyAuthentication方法(Microsoft.Web.WebPages.OAuth)
var authenticationResult = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
它总是返回与AuthenticationResult IsSuccessful = false
和Provider = ""
我看着这个代码,并OAuthWebSecurity类试图从获得供应商名称
Request.QueryString["__provider__"]
但谷歌没有将这些信息发送回的查询字符串。 其他的供应商我已经实现(LinkedIn)的发送提供者的名称后面,这一切工作得很好。
我不知道我从这点可以做,除了抛弃Microsoft.Web.WebPages.OAuth类,只是使用DotNetOpenAuth没有他们,但我希望有人可能有另一种解决方案,我可以试试...
我已经广泛搜索,但似乎无法找到任何帮助......我发现这真的很难,甚至随便找人做同样的事情,这真的让我感到惊讶的例子。
任何帮助非常感谢!
更新:马特·约翰逊提到下面,他已经打包到一个解决方案,你可以从GitHub获得: https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2
正如他指出:DNOA和OAuthWebSecurity用于ASP.Net MVC 4只随附的OpenID提供商的谷歌。 这是一个OAuth2用户端,您可以使用来代替。
重要 - 如果你正在使用ASP.Net MVC 5,这个包是不适用的。 您应该改用Microsoft.Owin.Security.Google。 (它还附带在VS 2013年5 MVC入门模板)
我说服了这到底由捕捉请求时,它有,并且做我自己检查,看它来自哪个供应商。 谷歌允许你发送一个参数到名为“状态”的的OAuth要求,他们只需通过直接回到你的时候,他们做出回调,所以我用这个传递供应商名称,谷歌,我在检查该不存在的"__provider__"
。
是这样的:
public String GetProviderNameFromQueryString(NameValueCollection queryString)
{
var result = queryString["__provider__"];
if (String.IsNullOrWhiteSpace(result))
{
result = queryString["state"];
}
return result;
}
我则实现了自定义OAuth2Client谷歌,和我手动呼吁该VerifyAuthentication方法我自己,绕过微软的包装材料。
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(注:它需要一些整理我还没有得到全面整理把程式码但它不工作,虽然!):
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);
}
您可以在供应商的查询参数添加到您的回调网址的结尾。 例如https://mywebsite.com/Account/ExternalLoginCallback? 供应商 =谷歌
该你会得到它,你不需要周围的工作。