Force re-authentication using OAuthWebSecurity with Facebook - c#

My website is using facebook as it's oauth provider. Users will be able to buy things through my site so I want to force them to authenticate even if they already have an active session with facebook.
I found this link in facebook's api documentation that discusses reauthentication but I can't get it to work with my mvc app. Anyone know if this is possible?
var extra = new Dictionary<string, object>();
extra.Add("auth_type", "reauthenticate");
OAuthWebSecurity.RegisterFacebookClient(
appId: "**********",
appSecret: "**********************",
displayName: "",
extraData: extra);

Found the solution. I had to create my own client instead of using the default one provided by OAuthWebSecurity.RegisterFacebookClient
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
namespace Namespace.Helpers
{
public class MyFacebookClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
{
private const string AuthorizationEP = "https://www.facebook.com/dialog/oauth";
private const string TokenEP = "https://graph.facebook.com/oauth/access_token";
private readonly string _appId;
private readonly string _appSecret;
public MyFacebookClient(string appId, string appSecret)
: base("facebook")
{
this._appId = appId;
this._appSecret = appSecret;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
return new Uri(
AuthorizationEP
+ "?client_id=" + this._appId
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&scope=email,user_about_me"
+ "&display=page"
+ "&auth_type=reauthenticate"
);
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
WebClient client = new WebClient();
string content = client.DownloadString(
"https://graph.facebook.com/me?access_token=" + accessToken
);
dynamic data = Json.Decode(content);
return new Dictionary<string, string> {
{
"id",
data.id
},
{
"name",
data.name
},
{
"photo",
"https://graph.facebook.com/" + data.id + "/picture"
},
{
"email",
data.email
}
};
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(content);
if (nameValueCollection != null)
{
string result = nameValueCollection["access_token"];
return result;
}
return null;
}
}
}
and then in AuthConfig.cs...
OAuthWebSecurity.RegisterClient(
new MyFacebookClient(
appId: "xxxxxxxxxx",
appSecret: "xxxxxxxxxxxxxxxx"),
"facebook", null
);

As a note to those that happen upon this if your Facebook Authentication stopped working when v2.3 became the lowest version you have access to (non versioned calls get the lowest version an app has access to). The API now returns JSON and not name value pairs so you have to update the QueryAccessToken method shown above by #Ben Tidman
Below is the updated method
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
dynamic json = System.Web.Helpers.Json.Decode(content);
if (json != null)
{
string result = json.access_token;
return result;
}
return null;
}

There's one issue in using the MyFacebookClient implementation.
Probably someone tryin to implement it came across the error:
The given key was not present in the dictionary
attempting to call the ExternalLoginCallback method in ActionController.
The error raises when the method
OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
is called.
In order to get it working the method VerifyAuthentication has to be overridden.
In particular the
public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl);
overload of the abstract class OAuth2Client.
If you use the following:
public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.OriginalString;
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(QueryAccessToken(returnPageUrl, code));
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
AuthenticationResult result = new AuthenticationResult(true, ProviderName, userData["id"], userData["name"], userData);
userData.Remove("id");
userData.Remove("name");
return result;
}
}
finally you get the method called in the right way and no excpetion is thrown.

Related

Retrieving data from an API

The first part of the program is to retrieve the employee user ID (or signature) from an API URL once the name has been entered. (Which I have done)
The second part, the user will enter a specific "to" and "from" date.
Using the signature obtained from the first part and the dates that the user enters, the program should pass this information to an API address and obtain information accordingly.
My question is that I am not sure how to pass the obtained signature to the new API address + the "to" and "from" date.
The first part of the program to retrieve the signature (works perfectly):
namespace TimeSheets_Try_11.Controllers
{
class WebAPI
{
public string Getsignature(string name)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var cookies = FullWebBrowserCookie.GetCookieInternal(new Uri(StaticStrings.UrlIora), false);
WebClient wc = new WebClient();
wc.Encoding = System.Text.Encoding.UTF8;
wc.Headers.Add("Cookie:" + cookies);
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
wc.UseDefaultCredentials = true;
string uri = "";
uri = StaticStrings.UrlIora + name;
var response = wc.DownloadString(uri);
var status = JsonConvert.DeserializeObject<List<Employeename>>(response);
string signame = status.Select(js => js.signature).First();
return signame;
}
The second part that I have written so far:
public string[] GetTime(double fromDate, double toDate, string username)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var cookies = FullWebBrowserCookie.GetCookieInternal(new Uri(StaticStrings.UrlNcert), false);
WebClient wc = new WebClient();
wc.Encoding = System.Text.Encoding.UTF8;
wc.Headers.Add("Cookie:" + cookies);
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
wc.UseDefaultCredentials = true;
string url = "";
url = StaticStrings.UrlNcert + username + "&fromDate=" + fromDate + "&toDate=" + toDate;
var respons = wc.DownloadString(url);
OracleHour ndata = JsonConvert.DeserializeObject<OracleHour>(respons);
var Get_Odnum = ndata.orderNumber;
var Dt_Work = ndata.dateOfWork;
var hrType = ndata.hourType;
var hr = ndata.hours;
var des = ndata.description;
var surname = ndata.surveyor;
string[] myncertdata = { Get_Odnum, Dt_Work.ToString(), hrType, hr.ToString(), des, surname };
return myncertdata;
}
}
}
The API strings:
namespace TimeSheets_Try_11.Controllers
{
class StaticStrings
{
public static string UrlIora = "https://iora.dnvgl.com/api/dictionary/employee/";
public static string UrlNcert = "https://cmcservices.dnvgl.com/Finance/api/oracleReportingCost?user=VERIT" + #"\";
}
}
For example if we are using the name "Jane Dow" from the date 9/22/20 - 9/29/20, the api strings will be
UrlIora = "https://iora.dnvgl.com/api/dictionary/employee/Jane
UrlNcert = "https://cmcservices.dnvgl.com/Finance/api/oracleReportingCost?user=VERIT\JDOW&fromDate=2020-09-22&toDate=2020-09-29"
Trivial way - change UrlNcert to url without query at first:
class StaticStrings
{
public static string UrlIora = "https://iora.dnvgl.com/api/dictionary/employee/";
public static string UrlNcert = "https://cmcservices.dnvgl.com/Finance/api/oracleReportingCost";
}
Then in your api call get values for username, fromDate and toDate and use string interpolation.
var url = $"{StaticStrings.UrlNcert}?user={username}&fromDate={fromDate:yyyy-MM-dd}&toDate={toDate:yyyy-MM-dd}";
If you want more complex way, check UriBuilder

How do you use the public access token with Unity webrequest?

I'm trying to get a JSON file from a website and I either get a 403 forbidden or a lot of HTML that doesn't include any useful data.
I might be doing it completely wrong because I have little to no understanding of WebRequest/networking and HTML.
public Text debugText;
public string websiteUrl;
IEnumerator GetRequest(string uri)
{
//UnityWebRequest.Get(uri)
var dictionary = new Dictionary<string, string>();
dictionary.Add("Authorization", "Bearer");
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("Authorization", "Bearer yQrAQWWxTZ2jyKg9vLOU ");
yield return webRequest.SendWebRequest();
string[] pages = uri.Split('/');
int page = pages.Length - 1;
Debug.Log("Headers: " + webRequest.GetResponseHeaders());
if (webRequest.isNetworkError)
{
Debug.Log(pages[page] + ": Error: " + webRequest.error);
}
else
{
string jsonResult = webRequest.downloadHandler.text;
Debug.Log(jsonResult);
//Debug.Log(jsonResult);
debugText.text = jsonResult;
}
}
}

Pinterest API Error 405 POST Request

I'm having an issue with sending a POST request for Pinterest in a UWP app I'm working on. I already have the access code from a previous WebAuthenticationBroker function. I've tried using the WebAuthenticationBroker with the UseHttpPost option under options with authenticating async, but, as I've provided my if functions, it returns ERROR. I just get a "message": "405: Method Not Allowed" and "type" : "http". I've looked all over, I've even tried using an HttpClient and PostAsync(), but I couldn't get it to get the access token. Any advise to what I'm doing wrong?
private void OutputToken(string TokenUri)
{
int tokenString = TokenUri.IndexOf('=');
string TheTokenUri = TokenUri.Substring(tokenString + 54);
PinterestReturnedTokenText.Text = TheTokenUri;
outputToken = TheTokenUri;
}
private async void auth()
{
try
{
string CallbackUrl = "https://localhost/";
string PinterestUrl = "https://api.pinterest.com/v1/oauth/token?grant_type=authorization_code&client_id=" + PinterestClientID + "&client_secret=" + PinterestClientSecret + "&code=" + outputCode;
Uri StartUri = new Uri(PinterestUrl);
Uri EndUri = new Uri(CallbackUrl);
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseHttpPost, StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
OutputToken(WebAuthenticationResult.ResponseData.ToString());
await GetPinterestNameAsync(WebAuthenticationResult.ResponseData.ToString());
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
OutputToken("HTTP Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseErrorDetail.ToString());
}
else
{
OutputToken("Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseStatus.ToString());
}
}
catch (Exception Error)
{
PinterestReturnedTokenText.Text = "ERROR";
}
}

How to update status on twitter using C# and LINQ to Twitter library

I am writing an Metrol Style App to update status on my Twitter. I use LINQ to Twitter library. But I don't understand why my app throws exception 401 Unauthorized. Here is my code:
private void UpdateStatus()
{
// configure the OAuth object
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = "ConsumerKey",
ConsumerSecret = "ConsumerSecret",
OAuthToken = "TwitterAccessToken",
AccessToken = "TwitterAccessTokenSecret"
}
};
using (var twitterCtx = new TwitterContext(auth, "https://api.twitter.com/1/", "https://search.twitter.com/"))
{
var tweet = twitterCtx.UpdateStatus("Hi everybody!"); // error here
viewTextBlock.Text = String.Empty;
viewTextBlock.Text = viewTextBlock.Text + "Status returned: " +
"(" + tweet.StatusID + ")" +
tweet.User.Name + ", " +
tweet.Text + "\n";
}
}
I just posted a blog entry on using OAuth in Windows 8 with LINQ to Twitter:
http://geekswithblogs.net/WinAZ/archive/2012/07/02/using-linq-to-twitter-oauth-with-windows-8.aspx
I also included a 401 FAQ in the LINQ to Twitter docs here:
http://linqtotwitter.codeplex.com/wikipage?title=LINQ%20to%20Twitter%20FAQ&referringTitle=Documentation
You can implement it using the Twitterizer assembly.
Firstly you can create a token which can be used to access Twitter and then using that particular token you can update TwitterStatus (Twitterizer.Core.TwitterObject.TwitterStatus).
Sample code is as follows.
public void CreateCachedAccessToken(string requestToken)
{
string ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
OAuthTokenResponse responseToken = OAuthUtility.GetAccessToken(ConsumerKey, ConsumerSecret, requestToken);
//Cache the UserId
Session["GetCachedUserId"] = responseToken.UserId;
OAuthTokens accessToken = new OAuthTokens();
accessToken.AccessToken = responseToken.Token;
accessToken.AccessTokenSecret = responseToken.TokenSecret;
accessToken.ConsumerKey = ConsumerKey;
accessToken.ConsumerSecret = ConsumerSecret;
Session["AccessToken"] = accessToken;
}
To update the TwitterStatus you can do as follows.
public OAuthTokens GetCachedAccessToken()
{
if (Session["AccessToken"] != null)
{
return (OAuthTokens)(Session["AccessToken"]);
}
else
{
return null;
}
}
TwitterStatus.Update(GetCachedAccessToken(), txtTweet.Trim());
The below mentioned method can be used to implement sign in.
protected string GetTwitterAuthorizationUrl()
{
string ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
OAuthTokenResponse reqToken = OAuthUtility.GetRequestToken(ConsumerKey, ConsumerSecret);
return "https://twitter.com/oauth/authorize?oauth_token=" + reqToken.Token;
}
Hope this helps.
If there are any clarifications please raise. Thanks

Adding Facebook log in button - can't retrieve email address on server side

I am trying to add a simple log in with Facebook button to my ASP.NET (C#) website. All I need is on the server side to retrieve the Facebook user's email address once they have logged in.
I was trying to use this example but it seems that the cookie "fbs_appid" is no longer used and instead there is one called "fbsr_appid".
How can I change the sample to use the different cookie? Alternately does anyone have a working example of retrieving the logged in Facebook user's email address.
I know there is an SDK I can use but I want to keep things simple. The above example would be perfect if it worked.
I managed to get the information needed using the fbsr cookie. I created the following class which does all of the work to confirm the user logged in with Facebook and then retrieves the user's details:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Script.Serialization;
namespace HarlequinShared
{
public class FacebookLogin
{
protected static string _appId = null;
protected static string AppId
{
get
{
if (_appId == null)
_appId = ConfigurationManager.AppSettings["FacebookAppId"] ?? null;
return _appId;
}
}
protected static string _appSecret = null;
protected static string AppSecret
{
get
{
if (_appSecret == null)
_appSecret = ConfigurationManager.AppSettings["FacebookAppSecret"] ?? null;
return _appSecret;
}
}
public static FacebookUser CheckLogin()
{
string fbsr = HttpContext.Current.Request.Cookies["fbsr_" + AppId].Value;
int separator = fbsr.IndexOf(".");
if (separator == -1)
{
return null;
}
string encodedSig = fbsr.Substring(0, separator);
string payload = fbsr.Substring(separator + 1);
string sig = Base64Decode(encodedSig);
var serializer = new JavaScriptSerializer();
Dictionary<string, string> data = serializer.Deserialize<Dictionary<string, string>>(Base64Decode(payload));
if (data["algorithm"].ToUpper() != "HMAC-SHA256")
{
return null;
}
HMACSHA256 crypt = new HMACSHA256(Encoding.ASCII.GetBytes(AppSecret));
crypt.ComputeHash(Encoding.UTF8.GetBytes(payload));
string expectedSig = Encoding.UTF8.GetString(crypt.Hash);
if (sig != expectedSig)
{
return null;
}
string accessTokenResponse = FileGetContents("https://graph.facebook.com/oauth/access_token?client_id=" + AppId + "&redirect_uri=&client_secret=" + AppSecret + "&code=" + data["code"]);
NameValueCollection options = HttpUtility.ParseQueryString(accessTokenResponse);
string userResponse = FileGetContents("https://graph.facebook.com/me?access_token=" + options["access_token"]);
userResponse = Regex.Replace(userResponse, #"\\u([\dA-Fa-f]{4})", v => ((char)Convert.ToInt32(v.Groups[1].Value, 16)).ToString());
FacebookUser user = new FacebookUser();
Regex getValues = new Regex("(?<=\"email\":\")(.+?)(?=\")");
Match infoMatch = getValues.Match(userResponse);
user.Email = infoMatch.Value;
getValues = new Regex("(?<=\"first_name\":\")(.+?)(?=\")");
infoMatch = getValues.Match(userResponse);
user.FirstName = infoMatch.Value;
getValues = new Regex("(?<=\"last_name\":\")(.+?)(?=\")");
infoMatch = getValues.Match(userResponse);
user.LastName = infoMatch.Value;
return user;
}
protected static string FileGetContents(string url)
{
string result;
WebResponse response;
WebRequest request = HttpWebRequest.Create(url);
response = request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
result = sr.ReadToEnd();
sr.Close();
}
return result;
}
protected static string Base64Decode(string input)
{
UTF8Encoding encoding = new UTF8Encoding();
string encoded = input.Replace("=", string.Empty).Replace('-', '+').Replace('_', '/');
var decoded = Convert.FromBase64String(encoded.PadRight(encoded.Length + (4 - encoded.Length % 4) % 4, '='));
var result = encoding.GetString(decoded);
return result;
}
}
public class FacebookUser
{
public string UID { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
And then I can use this in my login page:
FacebookUser user = FacebookLogin.CheckLogin();
if (user != null)
{
Response.Write("<p>" + user.Email);
Response.Write("<p>" + user.FirstName);
Response.Write("<p>" + user.LastName);
}
This is further explained here.
I believe that this method is secure and does the job as simply as possible. Please comment if there is any concerns.
This is not at all how you should be doing things, especially if you are trying to do it on the server side. You should not use the cookie.
The different sdks make things simpler, they (especially the official ones) stay up to date with facebook changes, unlike the example you provided that just stopped working when the cookie name was changed.
I'm not a C# developer, and never tried to use facebook from that environment so I can't tell you exactly how to do it with C#, but this is the main concept:
When you send the user for authentication (assumed Server Side Flow) you need to ask the user for permissions for anything but basic and public user information.
The email field is filed under "extended permission" and you need to ask for it specifically, something like this:
https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_url=YOUR_REDIRECT_URI&scope=email
(You can read more about permissions here)
Once the user authorized your app and granted you with the permissions, you can then ask for that data.
You can do it in the server side by asking for the "/me" graph path (or just the email: "me?fields=email").
You can also do it in the client side with the javascript sdk:
FB.api("/me", function(response) {
console.log(response);
});

Categories

Resources