asp.net-mvc-4 oauth-2.0 dotnetopenauth

asp.net mvc 4 - OAuth2 y DotNetOpenAuth: implementación del cliente personalizado de Google



asp.net-mvc-4 oauth-2.0 (2)

Puede agregar un parámetro de consulta de proveedor al final de su url de devolución de llamada. por ejemplo, https://mywebsite.com/Account/ExternalLoginCallback? proveedor = google

Lo obtendrás y no necesitas el trabajo.

Tengo un problema al implementar OAuth2Client personalizado para google usando DotNetOpenAuth y MVC4.

Llegué al punto en el que puedo realizar correctamente la solicitud de autorización al punto final de Google https://accounts.google.com/o/oauth2/auth

y Google pregunta si el usuario permitirá que mi aplicación acceda a su cuenta. Todo bien hasta ahora. Cuando el usuario hace clic en ''Aceptar'', google llama a mi URL de devolución de llamada como se esperaba.

El problema es cuando llamo al método VerifyAuthentication en la clase OAuthWebSecurity (Microsoft.Web.WebPages.OAuth)

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

Siempre devuelve un AuthenticationResult con IsSuccessful = false y Provider = ""

He buscado en el código para esto, y la clase OAuthWebSecurity intenta obtener el nombre del proveedor de

Request.QueryString["__provider__"]

pero Google no está enviando esta información a la cadena de consulta. El otro proveedor que implementé (LinkedIn) está devolviendo el nombre del proveedor y todo funciona bien.

No estoy seguro de lo que puedo hacer a partir de este punto, además de abandonar las clases de Microsoft.Web.WebPages.OAuth y usar DotNetOpenAuth sin ellas, pero esperaba que alguien pudiera tener otra solución que pudiera probar ...

He buscado mucho, pero parece que no puedo encontrar nada que me ayude ... Me ha resultado realmente difícil incluso encontrar ejemplos de gente que hace lo mismo, lo que realmente me ha sorprendido.

Cualquier ayuda muy apreciada!


Actualización: Como Matt Johnson menciona a continuación, ha preparado una solución para esto que puede obtener de GitHub: https://github.com/mj1856/DotNetOpenAuth.GoogleOAuth2

Como señala: DNOA y OAuthWebSecurity para ASP.Net MVC 4 se envían solo con un proveedor OpenId para Google. Este es un cliente OAuth2 que puede usar en su lugar.

IMPORTANTE: si está utilizando ASP.Net MVC 5, este paquete no es aplicable. Deberías usar Microsoft.Owin.Security.Google en su lugar. (También se envía con las plantillas iniciales de MVC 5 en VS 2013).

Al final lo solucioné captando la solicitud cuando llegaba, y haciendo mi propio cheque para ver de qué proveedor provenía. Google te permite enviar un parámetro a la solicitud de OAuth llamada ''estado'', que simplemente te devuelve cuando hacen la devolución de llamada, así que estoy usando esto para pasar el nombre del proveedor para google, y verifico esto en la ausencia del "__provider__" .

algo como esto:

public String GetProviderNameFromQueryString(NameValueCollection queryString) { var result = queryString["__provider__"]; if (String.IsNullOrWhiteSpace(result)) { result = queryString["state"]; } return result; }

Luego implementé un OAuth2Client personalizado para Google, y yo mismo llamé manualmente el método VerifyAuthentication sobre eso, pasando por alto las cosas de Microsoft wrapper.

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

Esto me ha permitido mantener las cosas que ya tenía para los otros proveedores que usan los contenedores de Microsoft.

Tal como lo solicitó @ 1010100 1001010, este es mi OAuth2Client personalizado para Google (NOTA: ¡NECESITA ALGUNA PREPARACIÓN! NO HE PODIDO REDONDEAR EL CÓDIGO PARA ARRIBA. Sin embargo, sí funciona):

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