Comments (7)
public class FacebookClient2017 : OAuth2Client
{
/// <summary>
/// The authorization endpoint.
/// </summary>
private const string AuthorizationEndpoint = "https://www.facebook.com/v2.8/dialog/oauth";
/// <summary>
/// The token endpoint.
/// </summary>
private const string TokenEndpoint = "https://graph.facebook.com/v2.8/oauth/access_token";
/// <summary>
/// The user info endpoint.
/// </summary>
private const string UserInfoEndpoint = "https://graph.facebook.com/v2.8/me";
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
public FacebookClient2017(string appId, string appSecret) : this(appId, appSecret, new[] { "email" }) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
public FacebookClient2017(string appId, string appSecret, params string[] requestedScopes) : base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException("appId");
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException("appSecret");
if (requestedScopes == null)
throw new ArgumentNullException("requestedScopes");
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
}
public override void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
string redirectUrl = this.GetServiceLoginUrl(returnUrl).AbsoluteUri;
context.Response.Redirect(redirectUrl, endResponse: true);
}
public new AuthenticationResult VerifyAuthentication(HttpContextBase context)
{
throw new NoNullAllowedException();
}
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);
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(AuthorizationEndpoint, new NameValueCollection
{
{ "client_id", _appId },
{ "scope", string.Join(" ", _requestedScopes) },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
{ "state", state },
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));
return data;
}
}
}
public string QueryAccessTokenByCode(Uri returnUrl, string authorizationCode)
{
return this.QueryAccessToken(returnUrl, authorizationCode);
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(TokenEndpoint, new NameValueCollection
{
{ "code", authorizationCode },
{ "client_id", _appId },
{ "client_secret", _appSecret },
{ "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
});
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
string accessToken = null;
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);
using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
{
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = desirializedJsonObject["access_token"].ToString();
}
return accessToken;
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = String.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) { Query = qs };
return builder.Uri;
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
}
public class OAuthConfig
{
public static void RegisterAuth()
{
OAuthWebSecurity.RegisterClient(new FacebookClient2017("appId", "appSecret"));
}
}
public ActionResult LoginCallback(string error)
{
FacebookClient2017.RewriteRequest();
string returnUrl = this.Request.QueryString["ReturnUrl"];
// after that continue as usual
result = OAuthWebSecurity.VerifyAuthentication(this.Url.Action("LoginCallback", new { ReturnUrl = returnUrl }));
from dotnetopenauth.
sort of critical,
all sites with Facebook auth no longer working!
from dotnetopenauth.
Having the same issue here.
Anybody has a quick solution?
from dotnetopenauth.
When will this modified code merge a release module?
from dotnetopenauth.
I am facing the same issue, hope this code will be merged soon too.
@adoconnection I am trying to translate your code to vb.net but I am facing an issue with
public FacebookClient2017(string appId, string appSecret) : this(appId, appSecret, new[] { "email" }) { }
Any idea how to translate it in vb.net ? Telerik's converter gives me
Public Sub New(appId As String, appSecret As String) Me.New(appId, appSecret, New () {"email"}) End Sub
But this doesn't compile.
from dotnetopenauth.
Thanks for the solution, worked for me !
from dotnetopenauth.
Just a heads up for anyone who comes across this thread like I did and discover that it's not quite working with the current version of the Facebook API (i.e. v3.2 as of this comment). Using the scope
parameter did not return an email address for me so I had to resort to using the fields
query param when getting the User's data.
That's not to say the above solution was not awesome. It got me 90% there so thank you for that!
Below is the slightly refactored class I came up with. Hopefully this prevents someone else from spinning their wheels trying to figure out why email wouldn't come back :-) :
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;
namespace iLSNetwork.Shared.Services.UserAccount
{
public class FacebookClient2018 : OAuth2Client
{
/// <summary>
/// The app id.
/// </summary>
private readonly string _appId;
/// <summary>
/// The app secret.
/// </summary>
private readonly string _appSecret;
/// <summary>
/// The requested scopes.
/// </summary>
private readonly string[] _requestedScopes;
/// <summary>
/// The requested fields to return from the user profile.
/// </summary>
private readonly string[] _userProfileFields;
/// <summary>
/// The authorization endpoint.
/// </summary>
private readonly string _authorizationEndpoint;
/// <summary>
/// The token endpoint.
/// </summary>
private readonly string _tokenEndpoint;
/// <summary>
/// The user info endpoint.
/// </summary>
private readonly string _userInfoEndpoint;
/// <summary>
/// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="facebookVersion">Version of the Facebook graph api being accessed. This should come in the format of "vX.Y" (i.e. "v3.2"). Default is "v3.2"</param>
public FacebookClient2018(string appId, string appSecret, string facebookVersion = "v3.2") : this(appId, appSecret, new[] {"email"}, new[] {"email", "name"}, facebookVersion) { }
/// <summary>
/// Creates a new Facebook OAuth2 client.
/// </summary>
/// <param name="appId">The Facebook App Id</param>
/// <param name="appSecret">The Facebook App Secret</param>
/// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
/// <param name="userProfileFields">One or more requested default user profile fields from facebook. These are limited to "first_name", "last_name", "middle_name", "name", "name_format", "picture", "short_name"</param>
/// <param name="facebookVersion">Version of the Facebook graph api being accessed. This should come in the format of "vX.Y" (i.e. "v3.2"). Default is "v3.2"</param>
public FacebookClient2018(string appId, string appSecret, string[] requestedScopes, string[] userProfileFields, string facebookVersion = "v3.2") : base("facebook")
{
if (string.IsNullOrWhiteSpace(appId))
throw new ArgumentNullException(nameof(appId));
if (string.IsNullOrWhiteSpace(appSecret))
throw new ArgumentNullException(nameof(appSecret));
if (requestedScopes == null)
throw new ArgumentNullException(nameof(requestedScopes));
if (requestedScopes.Length == 0)
throw new ArgumentException("One or more scopes must be requested.", nameof(requestedScopes));
_appId = appId;
_appSecret = appSecret;
_requestedScopes = requestedScopes;
_userProfileFields = userProfileFields ?? new string[0];
var facebookVersion1 = facebookVersion;
_authorizationEndpoint = $"https://www.facebook.com/{facebookVersion1}/dialog/oauth";
_tokenEndpoint = $"https://graph.facebook.com/{facebookVersion1}/oauth/access_token";
_userInfoEndpoint = $"https://graph.facebook.com/{facebookVersion1}/me";
}
public override void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
var redirectUrl = GetServiceLoginUrl(returnUrl).AbsoluteUri;
context.Response.Redirect(redirectUrl, endResponse: true);
}
public new AuthenticationResult VerifyAuthentication(HttpContextBase context)
{
throw new NoNullAllowedException();
}
public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
{
var code = context.Request.QueryString["code"];
if (string.IsNullOrEmpty(code))
{
return AuthenticationResult.Failed;
}
var accessToken = QueryAccessToken(returnPageUrl, code);
if (accessToken == null)
{
return AuthenticationResult.Failed;
}
var userData = GetUserData(accessToken);
if (userData == null)
{
return AuthenticationResult.Failed;
}
var 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: ProviderName, providerUserId: id, userName: name, extraData: userData);
}
public string QueryAccessTokenByCode(Uri returnUrl, string authorizationCode)
{
return QueryAccessToken(returnUrl, authorizationCode);
}
/// <summary>
/// Facebook works best when return data be packed into a "state" parameter.
/// This should be called before verifying the request, so that the url is rewritten to support this.
/// </summary>
public static void RewriteRequest()
{
var ctx = HttpContext.Current;
var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
if (stateString == null || !stateString.Contains("__provider__=facebook"))
return;
var q = HttpUtility.ParseQueryString(stateString);
q.Add(ctx.Request.QueryString);
q.Remove("state");
ctx.RewritePath(ctx.Request.Path + "?" + q);
}
private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
{
var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
var qs = string.Join("&", keyValuePairs);
var builder = new UriBuilder(baseUri) {Query = qs};
return builder.Uri;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);
return BuildUri(_authorizationEndpoint, new NameValueCollection
{
{"client_id", _appId},
{"scope", $"{string.Join(" ", _requestedScopes)}"},
{"redirect_uri", returnUrl.GetLeftPart(UriPartial.Path)},
{"state", state}
});
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
var uri = BuildUri(_userInfoEndpoint, new NameValueCollection {{"access_token", accessToken}, {"fields", string.Join(",", _userProfileFields)}});
var webRequest = (HttpWebRequest) WebRequest.Create(uri);
using (var webResponse = webRequest.GetResponse())
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return null;
using (var textReader = new StreamReader(stream))
{
var json = textReader.ReadToEnd();
var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());
data.Add("picture", $"https://graph.facebook.com/{data["id"]}/picture");
return data;
}
}
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
var uri = BuildUri(_tokenEndpoint, new NameValueCollection
{
{"code", authorizationCode},
{"client_id", _appId},
{"client_secret", _appSecret},
{"redirect_uri", returnUrl.GetLeftPart(UriPartial.Path)}
});
var webRequest = (HttpWebRequest) WebRequest.Create(uri);
string accessToken;
var response = (HttpWebResponse) webRequest.GetResponse();
// handle response from FB
// this will not be a url with params like the first request to get the 'code'
var rEncoding = Encoding.GetEncoding(response.CharacterSet ?? throw new InvalidOperationException("Response from getting the facebook access token was invalid."));
using (var sr = new StreamReader(response.GetResponseStream() ?? throw new InvalidOperationException("Response from getting the facebook access token was invalid"), rEncoding))
{
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));
var deserializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
accessToken = deserializedJsonObject["access_token"].ToString();
}
return accessToken;
}
}
}
from dotnetopenauth.
Related Issues (20)
- No source/symbols for the latest stable version
- Pass additional field parameters in oAuth request header
- <reporting enabled="true"/> leaves other IIS applications unable to use IsolatedStorage HOT 1
- Using StandardAccessTokenAnalyzer with the symmetric ICryptoKeyStore in stable 4.3.4 NuGet package
- Missing Credit HOT 2
- DotNetOpenAuth locally work but publish on server not work HOT 2
- Facebook messaged me that the graph api 2.0 version this library uses will not be supported come August 2016 HOT 4
- Really not an issue
- New error: The OpenID Provider issued an assertion for an Identifier whose discovery information did not match.
- OpenIdWebRingSsoRelyingParty canot CreateRequest
- THIS REPOSITORY IS DEAD ! HOT 6
- How to use code to set whitelistHosts?
- Audience parameter
- The invalid URI: URI string is too long HOT 1
- SSO doesn't for web accelerator between SSO sites
- Web request to failed. Remote party has closed the transport stream.
- Facebook strict OAuth redirection URIs HOT 1
- Access tokens are too long HOT 3
- do refresh_token has a lifetime?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dotnetopenauth.