I am having an issue with OAuth and Facebook. I am using MVC4 standard OAuth login. I am not having the issue locally but on the server this is proving to be a problem.
If I paste the following URL into the browser it works OK:
http://localhost:46260/Account/ExternalLoginCallback?ReturnUrl=%2FDashboard&__provider__=FacebookPro&__sid__=1234somesid456 // this is autogenerated
When I change the URL for the app in facebook to the current domain and paste this url in, I get re-directed to the Unsuccessful login page:
http://freersvp.mytakeawaysite.com:80/Account/ExternalLoginCallback?ReturnUrl=%2FDashboard&__provider__=Facebook+Pro&__sid__=1234someid456 // note this is autogenerated
N.B The above two url's are the redirect uri
The below URL is what is requested and is causing the exception:
URL
https://graph.facebook.com/oauth/access_token?client_id=52*********37&redirect_uri=http%3a%2f%2ffreersvp.mytakeawaysite.com%3a80%2fAccount%2fExternalLoginCallback%3fReturnUrl%3d%252FDashboard%26__provider__%3dFacebook%2bPro%26__sid__%3d3c92eb7e84304afc931ef0ea7b62f56a&client_secret=2123***********4256&code=AQAQIJsj-ondldllVYKdpxJaZouqrlg9sjTcfUxyWhAw8MXbD2DvsOSujg2m7E3s3cvNusCI0ZZoJAuGgu_FLkPyjYMQAkTWDVyHTcAoJD-tezyXgn0vhoFzX3FmuRBHYpyJEM-dk0KgF5ugsTHo9yGjBjrcfMDUGu9IxkKQ36k3gMrwocM1_l5t342Q2kIOHdt8pPcyrs--NzgNyZv48vSq7jkZwuQ95xRjUHG5J-ptcgq0l2BlqjzHDDuvIFH23lpMWHzzqdejdj5ejukz7t_Fnhx-mrpVdcRYhP3JeZ2UOTjAyKQmUB3rInooECcjq4c
Exception
{
"error": {
"message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
"type": "OAuthException",
"code": 100
}
}
The string token does come back with null in the GetUserData function in the below code:
I am using the FacebookScopedClient:
public class FacebookScopedClient : IAuthenticationClient
{
private string appId;
private string appSecret;
private string scope;
private const string baseUrl = "https://www.facebook.com/dialog/oauth?client_id=";
public const string graphApiToken = "https://graph.facebook.com/oauth/access_token?";
public const string graphApiMe = "https://graph.facebook.com/me?";
private static string GetHTML(string URL)
{
string connectionString = URL;
try
{
System.Net.HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(connectionString);
myRequest.Credentials = CredentialCache.DefaultCredentials;
//// Get the response
WebResponse webResponse = myRequest.GetResponse();
Stream respStream = webResponse.GetResponseStream();
////
StreamReader ioStream = new StreamReader(respStream);
string pageContent = ioStream.ReadToEnd();
//// Close streams
ioStream.Close();
respStream.Close();
return pageContent;
}
catch(Exception ex)
{
}
return null;
}
private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
{
SessionControl ctl = new SessionControl();
ctl.SaveParam("redirecturi", redirectURI, -3);
ctl.Dispose();
string token = GetHTML(graphApiToken + "client_id=" + appId + "&redirect_uri=" + HttpUtility.UrlEncode(redirectURI) + "&client_secret=" + appSecret + "&code=" + accessCode);
if(token == null || token == "")
{
return null;
}
string access_token = token.Substring(token.IndexOf("access_token="), token.IndexOf("&"));
string data = GetHTML(graphApiMe + "fields=id,name,email,username,gender,link&" + access_token);
try
{
}
catch { }
// this dictionary must contains
Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
userData.Add("accesstoken", access_token);
try
{
userData.Add("id", userData["id"]);
}
catch { }
return userData;
}
public FacebookScopedClient(string appId, string appSecret, string scope)
{
this.appId = appId;
this.appSecret = appSecret;
this.scope = scope;
}
public string ProviderName
{
get { return "FacebookPro"; }
}
public void RequestAuthentication(System.Web.HttpContextBase context, Uri returnUrl)
{
string url = baseUrl + appId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + scope;
context.Response.Redirect(url);
}
public AuthenticationResult VerifyAuthentication(System.Web.HttpContextBase context)
{
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(code, rawUrl);
if(userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
string id = userData["id"];
string username = userData["email"];
if(username == null || username == "")
{
username = userData["username"];
}
//userData.Remove("id");
userData.Remove("username");
AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
return result;
}
}
after running your posted url that's causing the error through a url decoder the issue lies in for some reason your url encoding the entire query string and not just the url.
you will notice in that url a bunch of %26 items those are url encoded & and that's what is throwing your error. the Facebook parser is seeing %26 instead of & and treating it as one single parameter.
the & separates url query string parameters when sending to a page. Without the full code I can't tell you where to look but some where in your code your completely encoding the entire query string and need to find that piece of code and only encode the embedded urls.
ok after reading over things maybe try this theory.
I think your code is receiving this stuff from Facebook, url encoded, and then your system is re-encoding it. try taking anything received and first url decode it, manipulate it and then re-encode things as needed.
hope this helps
Try it with sandbox mode off within facebook app.
Noticing your URL's query string, I found an answer from Stackoverflow. Please see if it solves your issue:
https://stackoverflow.com/a/16699058/2005136
Steve S posted as a response:
"In our case, we were doing something unusual (so this might not be relevant to your case). Our redirect_uri was a URL with another URL embedded as an encoded path element. The URL-within-a-URL, doubly-encoded when passed to FB, had started causing problems with the Facebook API servers.
We resolved this by changing the encoding of the nested URL to a long hex number rather than % encoding, so all Facebook servers see is a simple redirect_uri containing some hex within the path, unaffected by normal URL encoding/decoding."
Related
I am a begginer and i work in a MVC project which I cant understand it well yet.
I can't understand where does the API takes data from when I try to connect in Login Screen.
It doesn't use Entity Framework and there isn't a json with the data.
When I enter Id and Pass it calls an API (GetAPIResponse) which somehow finds that is correct.
Need help to understand the code and the logic behind it.
LoginBL class contains:
public bool IsAuthenticated(LoginEntity user)
{
string url = string.Empty;
string callType = string.Empty;
string server = string.Empty;
try
{
// get URL, Call type, Server from config file
url = ConfigurationManager.AppSettings["login_url"].ToString();
callType = ConfigurationManager.AppSettings["calltype"].ToString();
server = ConfigurationManager.AppSettings["server"].ToString();
// Encrypt password
string password = Scrambler.GenerateMD5Hash(user.Password);
// Prepare content for the POST request
string content = #"calltype=" + callType + "&server=" + server + "&user=" + user.UserName + "&pass=" + password + "";
Debug.WriteLine("Callcenter login url: " + content);
HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);
LoginResponseEntity obj = new LoginResponseEntity();
obj = JsonConvert.DeserializeObject<LoginResponseEntity>(json_list.Content.ReadAsStringAsync().Result);
Debug.WriteLine(callType + " Response: " + json_list.Content.ReadAsStringAsync().Result);
//if API resultCode return 0 then user details and token save in session for further use
if (obj.ResultCode == 0)
{
int restrict = obj.UserInfo.RestrictCallType.HasValue ?
obj.UserInfo.RestrictCallType.Value : 0;
HttpContext.Current.Session["user_id"] = obj.UserInfo.usr_id;
HttpContext.Current.Session["user_name"] = obj.UserInfo.usr_username;
HttpContext.Current.Session["user_group_id"] = obj.UserInfo.UserGroupID;
HttpContext.Current.Session["groupid"] = obj.UserInfo.groupid;
HttpContext.Current.Session["token"] = obj.Token;
HttpContext.Current.Session["web_server_url"] = obj.ServerInfo.web_server_url;
HttpContext.Current.Session["centerX"] = obj.ServerInfo.DefaultGeoX;
HttpContext.Current.Session["centerY"] = obj.ServerInfo.DefaultGeoY;
HttpContext.Current.Session["dateFormat"] = obj.ServerInfo.dateFormat;
HttpContext.Current.Session["currency"] = obj.ServerInfo.currency;
HttpContext.Current.Session["customer_img"] = obj.ServerInfo.customer_img;
HttpContext.Current.Session["groups"] = obj.groups;
HttpContext.Current.Session["restrict_call_type"] = restrict ;
Debug.WriteLine("obj.UserInfo.UserGroupID " + obj.UserInfo.UserGroupID);
Debug.WriteLine("obj.UserInfo.groups " + obj.groups);
//HttpContext.Current.Session["defaultLanguage"] = obj.ServerInfo.defaultLanguage;
HttpCookie cookie = new HttpCookie("Login");
// if remember me checked then user name and password stored in cookie else cookes is expired
if (user.RememberMe)
{
cookie.Values.Add("user_name", obj.UserInfo.usr_username);
cookie.Values.Add("pwd", user.Password);
cookie.Expires = DateTime.Now.AddDays(15);
HttpContext.Current.Response.Cookies.Add(cookie);
}
else
{
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
return true;
}
else
{
//ResultCode -5 :Invalid Login ,-1:Database Error ,-2:Server Error ,-3:Invalid Parameter specified ,-4:Invalid Token
return false;
}
}
catch
{
throw;
}
finally
{
url = string.Empty;
callType = string.Empty;
server = string.Empty;
}
}
Okay here after converts pass to MD5 creates a "string content" with the information given.
Then in next line (HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);) calls the API with the url and content as parameters where it finds if the data exists.
API code:
public static HttpResponseMessage GetAPIResponse(string url, string content)
{
StringBuilder traceLog = null;
HttpContent httpContent = null;
try
{
traceLog = new StringBuilder();
traceLog.AppendLine("Start: BusinessLayer getAPIResponse() Request Data:- " + DateTime.Now + "URL = " + url + "&content = " + httpContent);
using (HttpClient client = new HttpClient())
{
httpContent = new StringContent(content);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var resp = client.PostAsync(url, httpContent).Result;
Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);
traceLog.AppendLine("End: BusinessLayer getAPIResponse() call completed HttpResponseMessage received");
return resp;
}
}
catch
{
throw;
}
finally
{
traceLog = null;
httpContent.Dispose();
url = string.Empty;
content = string.Empty;
}
}
In the following line, console prints the result that I cant understand where it cames from (Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);)
Sorry for the confusion , I am in my first job with zero work experience and I am called to learn how this works alone without proper education on ASP.NET from them.
You will not go very far without debbugger. Learn how to debug in Visual Studio (YouTube tutorials might be fastest way). Place debug points along critical points in code (for example moment when client sends and receives response is line var resp = client.PostAsync...) and check variables.
Url for API server is actually defined in the line
url = ConfigurationManager.AppSettings["login_url"].ToString();
ConfigurationManager means Web.config file, check it's appSettings section for login_url entry, there is your url.
Btw, using (HttpClient client = new HttpClient()) is not a good way to use a HttpClient and will lead to port exhaustion. It's ok for small number of requests, but for larger ones you must reuse it, or use HttpClientFactory (for .NET Core).
I'm totally new in c#, I wrote my code in VB.NET before but I'm trying to use c# to re-develop my program and as a lesson for learning c#. I tried long time but still can't get a proper conversion from VB.NET to c# , can anyone help me to convert as I will more easier to understand c#, thank you !
This is my code from VB.NET
Module GetStaffList
Dim Url As String
Dim CorpID As String
Dim Secret As String
Const ErrCode As String = """errcode"":0,""errmsg"":""ok"""
Function Token(CorpID As String, Secret As String) As String
CorpID = "wwe1f80304633b3"
Secret = "Ev7_oVNNbTpzkfcZ_QhX9l0VjZnAQ"
Dim http = CreateObject("MSXML2.ServerXMLHTTP")
Url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" & CorpID & "&corpsecret=" & Secret
http.Open("get", Url, False)
http.send()
If http.Status = 200 Then
Token = http.responseText
End If
If InStr(Token, "access_token") > 1 Then
Token = Split(Token, ",")(2)
Token = Split(Token, ":")(1)
Token = Replace(Token, """", "")
MainPage.TxtToken.Text = Token
Else
Token = ""
End If
End Function
And below is what I trying convert to c# but still hard to do
namespace SC_System
{
class MSG
{
const string ErrCode = "\"errcode\":0,\"errmsg\":\"ok\"";
public void Token(string CorpID, string Secret)
{
var http = CreateObject("MSXML2.ServerXMLHTTP");
string Url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + PDC.CorpID + "&corpsecret=" + PDC.Secret +"";
HttpWebRequest GetUrl = (HttpWebRequest)WebRequest.Create(Url);
HttpWebResponse ResponseUrl = (HttpWebResponse)GetUrl.GetResponse();
if (ResponseUrl.StatusCode == HttpStatusCode.OK)
{
Console.WriteLine(ResponseUrl.StatusDescription);
ResponseUrl.Close();
}
}
private object CreateObject(string v)
{
throw new NotImplementedException();
}
}
}
This translates the working parts of the original VB:
internal static class GetStaffList
{
//this doesn't seem to be used right now
internal const string ErrCode = "\"errcode\":0,\"errmsg\":\"ok\"";
internal static void Token(string CorpID, string Secret)
{
CorpID = CorpID ?? "wwe1f80304633b3";
Secret = Secret ?? "Ev7_oVNNbTpzkfcZ_QhX9l0VjZnAQ";
string token;
using (var wc = new WebClient())
{
token = wc.DownloadString($"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={CorpID}&corpsecret={Secret}");
}
if (token.Contains("access_token"))
{
token = token.Split(",")[2].Split(":")[1].Replace("\"", "");
MainPage.TxtToken.Text = token;
}
else
{
token = "";
}
}
}
But you're probably better off actually returning a value, and not updating the UI, as was only hinted at in the VB method:
internal static string Token(string CorpID, string Secret)
{
CorpID = CorpID ?? "wwe1f80304633b3";
Secret = Secret ?? "Ev7_oVNNbTpzkfcZ_QhX9l0VjZnAQ";
string token;
using (var wc = new WebClient())
{
token = wc.DownloadString($"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={CorpID}&corpsecret={Secret}");
}
if (token.Contains("access_token"))
{
return token.Split(",")[2].Split(":")[1].Replace("\"", "");
}
return "";
}
And then you'd call it like this:
string token = GetStaffList.Token(null, null);
if (!string.IsNullOrEmpty(token))
{
MainPage.TxtToken.Text = token;
}
Never have your utility methods or classes directly update the UI.
In the same vein, it's better to write the VB like this:
Public Module GetStaffList
Const ErrCode As String = """errcode"":0,""errmsg"":""ok"""
Public Function Token(Optional CorpID As String = Nothing, Optional Secret As String = Nothing) As String
CorpID = If(CorpID,"wwe1f80304633b3")
Secret = If(Secret,"Ev7_oVNNbTpzkfcZ_QhX9l0VjZnAQ")
Dim token As String
Using wc As New WebClient()
token = wc.DownlaodString(string.Format("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={0}&corpsecret={1}", CorpID, Secret))
End Using
If token.Contains("access_token") Then
Return token.Split(",")(2).Split(":")(1).Replace("""", "")
End If
Return ""
End Function
End Module
Finally, you should consider using an actual JSON parser to extract the token value you need from the download result. The Split() method is notorious for gotchas with this kind of thing, and actually tends to be slower than a dedicated parser.
In VB, a Function returns some value. In your code it returns a string
Function Token(CorpID As String, Secret As String) As String
becomes
string Token(string CorpID, string Secret){
// do something...and then
return "some string value";
}
and needs to return some string value. The function name doesn't infer a variable of the same name (Token) as the return value
a Sub doesn't return anything so:
Sub Token(CorpID As String, Secret As String)
becomes void in c#
void Token(string CorpID, string Secret)
Hopefully should get you going...
I want to sign in to youtube with post request. I used xNet for HttpRequest.
I wrote following codes:
static void Main(string[] args)
{
string url = "https://accounts.google.com/_/signin/sl/lookup?hl=en&_reqid=55174&rt=j";
HttpClass httpClass = new HttpClass();;
httpClass.PostRequestAsync(url, "username", "password");
}
class HttpClass {
public async Task PostRequestAsync(string url, string account, string pass)
{
xNet.HttpRequest http = new xNet.HttpRequest();
http.Cookies = new CookieDictionary();
string type = "application/x-www-form-urlencoded;charset=utf-8";
string query =
"continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Ffeature%3Dsign_in_button%26action_handle_signin%3Dtrue%26app%3Ddesktop%26next%3D%252F%26hl%3Den&service=youtube&hl=en&f.req=%5B%22" + account + "%22%2C%22AEThLlxFJqXTI-dLw8jxU_Lw8c4Qtpc4DAAeEE1rpkbEUFqwwK1U86bZEzsWmZKM5IjRccPvbYTLgb0yonB3vputyMTNm-8YcGqbe_GeaB6RHFJImp_gZ-y0jFv4nduPGxM-zpJX8BahbDlIyeY2sP8-puVe3W1iwKX3rGcSFGMevHHK-ByNEUY%22%2C%5B%5D%2Cnull%2C%22TR%22%2Cnull%2Cnull%2C2%2Cfalse%2Ctrue%2C%5Bnull%2Cnull%2C%5B2%2C1%2Cnull%2C1%2C%22https%3A%2F%2Faccounts.google.com%2FServiceLogin%3Fuilel%3D3%26passive%3Dtrue%26service%3Dyoutube%26continue%3Dhttps%253A%252F%252Fwww.youtube.com%252Fsignin%253Ffeature%253Dsign_in_button%2526action_handle_signin%253Dtrue%2526app%253Ddesktop%2526next%253D%25252F%2526hl%253Den%26hl%3Den%22%2Cnull%2C%5B%5D%2C4%2C%5B%5D%2C%22GlifWebSignIn%22%5D%2C1%2C%5Bnull%2Cnull%2C%5B%5D%5D%2Cnull%2Cnull%2Cnull%2Ctrue%5D%2C%22" + account + "%22%5D&bgRequest=%5B%22identifier%22%2C%22!Pj2lPRxCiup4YICaVSxEHyFXdsNE5lECAAAAQ1IAAAAbCgAW9I8p8C1f10xg_NjCyA99rybP30APm5kBCr5B19mb-UkpwTj1ZsyybospA0TSjuUTuJeCHmkiRqKfhHxRE1CV0Yd7nifpK8VCTMNnmUMrl4-anneYlV-Bs3NQESEmJTEcxBOjvbo_tXSasO8KbZopdTxzUHm-qBGOQRTUZM4Hw6x-1HJdLoCQ2bi4FoAhbsWEt6paR0K4neYHS1kdxewjDKefWWCQ__O3C71yOjm6p0S1rjNUEM0ak9V8N2CcnIFYQ77b1B98nHCZmgMr81YtgAOF8ClSb4ZV8AiUc96rC1rvMV2RIvW54RUgsJwWHXBx0nid8tRMdUmzCTymoa-_at7qE1nJL8SMAU9WEnGOs0u2xKlBKGsjNgnqhligTDBDPnp7%22%5D&azt=AFoagUUuZ6teJ3APaa8f6ly_olQZHdGWBg%3A1525177142108&cookiesDisabled=false&deviceinfo=%5Bnull%2Cnull%2Cnull%2C%5B%5D%2Cnull%2C%22TR%22%2Cnull%2Cnull%2C%5B%5D%2C%22GlifWebSignIn%22%2Cnull%2C%5Bnull%2Cnull%2C%5B%5D%5D%5D&gmscoreversion=undefined&checkConnection=youtube%3A288%3A1&checkedDomains=youtube&pstMsg=1&";
string html;
html = http.Post(url, query, type).ToString();
htmlTest(html);
type = "application/x-www-form-urlencoded";
query =
account +
"&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Ffeature%3Dsign_in_button%26action_handle_signin%3Dtrue%26app%3Ddesktop%26next%3D%252F%26hl%3Den&password=" +
pass + "&ca=&ct=";
http.Cookies = new CookieDictionary();
html = http.Post(url, query, type).ToString();
htmlTest(html);
}
public void htmlTest(string html)
{
File.WriteAllText("a.html", html);
Process.Start("a.html");
}
}
Response is:
)]}' [[["er",null,null,null,["gf.rrerr",1,"https://support.google.com/accounts/answer/61416?hl\u003den"] ,null,"gf.rrerr"] ,["e",2,null,null,149] ]]
The given link "https://support.google.com/accounts/answer/61416?hl\u003den" is says you must to open your cache data. I think I need to pass cookies with post but How can I pass cookies with post request?
Try to use cookíe with code
request.Cookies = new CookieDictionary()
{
{"ggg", "hhh"}
};
I'am new to Instagram API, I have a client that needs to display the latest media on his website, but am doing the test under my personal Instagram account.
Under Security Tab both [Disable implicit OAuth] & [Enforce signed requests] are checked.
The Endpoint am trying to call is: [users/{user-id}/media/recent]
Signature Generation Method:
string GenerateSignature(string endpoint,
Dictionary<string, string> parameters, string secret) {
StringBuilder message = new StringBuilder(endpoint);
foreach (var param in parameters.OrderBy(p => p.Key))
{
message.AppendFormat("|{0}={1}", param.Key, param.Value);
}
return HMACSHA256_Hash(secret, message.ToString());
}
string HMACSHA256_Hash(string secret, string message)
{
// Create a HMAC-SHA256 digest of the message using the secret key
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
byte[] digest = hmac.ComputeHash(Encoding.UTF8.GetBytes(message.ToString()));
// Return the digest as a hexstring to be used as a signature for the request
return ByteArrayToString(digest);
}
string ByteArrayToString(byte[] array)
{
// Convert the bytes in the array to a lower-case hexstring
return array.Aggregate(new StringBuilder(), (sb, b) => sb.Append(b.ToString("x2"))).ToString();
}
Get Recent Media:
protected void GetUserDetails()
{
string instagram_Username = "";
string instagram_UserID = "";
string instagram_AccessToken = "";
string instagram_ClientID = "";
string instagram_ClientSecret = "";
int count = 1;
string url = String.Format(#"https://api.instagram.com/v1/users/{0}/media/recent?client_id={1}&count={2}", instagram_UserID, instagram_ClientID, count);
string endPoint = String.Format("/users/{0}/media/recent", instagram_UserID);
Dictionary<string, string> endPointParameters = new Dictionary<string, string>();
endPointParameters.Add("client_id", instagram_ClientID);
endPointParameters.Add("count", count.ToString());
string signature = GenerateSignature(endPoint, endPointParameters, instagram_AccessToken);
url = url + "&sig=" + signature;
}
URL being called: https://api.instagram.com/v1/users/{user-id}/media/recent?client_id={cliend_id}&count=1&sig=7c024d67c1000106686c412778dc3534614d43822f9e13554418c3d3d5386872
Response: {"code": 403, "error_type": "OAuthForbiddenException", "error_message": "Invalid signed-request: Signature does not match"}
IN ADDITION; can I please know if access token will expire Or I can use same one for all my calls, noting that the call will be done each time the home page of the website is rendered :-)
I appreciate the help ! many thanks
I've missed that the secret should be the client Secret.
string signature = GenerateSignature(endPoint, endPointParameters, instagram_ClientSecret);
This is what I have so far, but it isn't working as I don't understand how DotNetOpenAuth is supposed to work. I only need it to sign the outcome with my key, but I am not having luck. Everything seems to point towards me needing to get the client to authorize my access, but I just need to get it signed as I don't need the user for this request.
Refer to http://developer.netflix.com/docs/read/Security , the section labeled "Netflix API Requests"
public class class1
{
private void Main()
{
string consumerKey = "<MyAPIKey>";
string consumerSecret = "<MyAPISharedSecret>";
var tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
MessageReceivingEndpoint oauthEndpoint =
new MessageReceivingEndpoint(new Uri("http://api-public.netflix.com/catalog/titles/index"),
HttpDeliveryMethods.PostRequest);
WebConsumer consumer = new WebConsumer(
new ServiceProviderDescription
{
RequestTokenEndpoint = oauthEndpoint,
UserAuthorizationEndpoint = oauthEndpoint,
AccessTokenEndpoint = oauthEndpoint,
TamperProtectionElements =
new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement()},
},
tokenManager);
var result = consumer.Channel.Request(new AccessProtectedResourceRequest());
}
internal class InMemoryTokenManager : IConsumerTokenManager
{
private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
public InMemoryTokenManager(string consumerKey, string consumerSecret)
{
if (string.IsNullOrEmpty(consumerKey))
{
throw new ArgumentNullException("consumerKey");
}
this.ConsumerKey = consumerKey;
this.ConsumerSecret = consumerSecret;
}
public string ConsumerKey { get; private set; }
public string ConsumerSecret { get; private set; }
public string GetTokenSecret(string token)
{
return this.tokensAndSecrets[token];
}
public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
{
this.tokensAndSecrets[response.Token] = response.TokenSecret;
}
public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken,
string accessTokenSecret)
{
this.tokensAndSecrets.Remove(requestToken);
this.tokensAndSecrets[accessToken] = accessTokenSecret;
}
public TokenType GetTokenType(string token)
{
throw new NotImplementedException();
}
}
}
Your actual question should be something like 'Is it possible to use DotNetOpenAuth to sign requests with or without access token?", to answer that question I should say I don't know and even I can't find it out by reading DotNetOpenAuth codebase.
There is no single page of documentation available for DotNetOpenAuth and the codebase is so huge that you can't read it and understand what is supported by it or not.
I guess making non-authenticated request is not an issue as it is simply a query string parameter added to your request.
But to make signed requests you need to follow a simple process:
Collecting request parameters
Calculating signature
Making request(signed/protected)
Collecting request parameters
These are basically two categories of parameters, oauth specific parameters and Netflix API specific parameters.
Among the OAuth specific parameters is nonce, this is the code in which you can use to generate a nonce value:
public static string GenerateNonce()
{
byte[] bytes = new byte[32];
var first = Guid.NewGuid().ToByteArray();
var second = Guid.NewGuid().ToByteArray();
for (var i = 0; i < 16; i++)
bytes[i] = first[i];
for (var i = 16; i < 32; i++)
bytes[i] = second[i - 16];
var result = Convert.ToBase64String(bytes, Base64FormattingOptions.None);
result = new string(result.ToCharArray().Where(char.IsLetter).ToArray());
return result;
}
And another OAuth specific parameter is timestamp, this is the code in which you can use to calculate timestamp:
DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds
Other oauth specific parameters are easy to provision and no need to write a specific code for them.
API specific parameters are any value you add to query string or to the authorization headers(except the oauth_signature itself) or to the body request(if request content type is application/x-www-form-urlencoded).
Calculating signature
To make either a signed request or a protected signature you need to calculate a signature, which the process is almost the same, except the way that you create signing key:
Calculate signature base string
Calculate signing key
Creating the signature by signing the signature base string using signing key
To calculate signature base string you need to first concatenate all parameters into a string and the percent encode the whole string. This is the code which helps you doing percent encoding:
public static string Encode(string source)
{
Func<char, string> encodeCharacter = c => {
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '.' || c == '-' || c == '_' || c == '~'))
return new string(c, 1);
return EncodeCharacter(c);
};
return string.Concat(source.ToCharArray().Select(encodeCharacter));
}
Also you need to sort parameters in alphabetical order and be concatenated using '&'. Here is the code which you may have to write to do this:
public static string CalculateParameterString(KeyValuePair<string, string>[] parameters)
{
var q = from entry in parameters
let encodedkey = PercentEncode.Encode(entry.Key)
let encodedValue = PercentEncode.Encode(entry.Value)
let encodedEntry = encodedkey + "=" + encodedValue
orderby encodedEntry
select encodedEntry;
var result = string.Join("&", q.ToArray());
return result;
}
Lets call the above string 'parameters string'. Then to calculate signature base string all you need is to concatenate http verb of your request, your request's url and parameters string together using '&'. Also you need to percent encode them first. Here is the code which does that:
public static string CalcualteSignatureBaseString(string httpMethod, string baseUri, string parametersString)
{
return httpMethod.ToUpper() + "&" + PercentEncode.Encode(baseUri) + "&" + PercentEncode.Encode(parametersString);
}
Once you have created signature base string then you the next step is to calculate signing key.
If you just need to make a signed request, then you create signing key based on your consumer key(shared secret) only. This the signing key to be used to make a signed request.
During authorization process, if you just made a request token request and recieved a temporary oauth token, then your singing key is based on your consumer key and that oauth token. This is the signing key used to make request to get the access token.
If a user authorized your application and you have the relevant access token, then your signing key would be your consumer key and access token. This is the signing key to make a protected request.
This is the code that will generate the signing key:
public static string GetSigningKey(string ConsumerSecret, string OAuthTokenSecret = null)
{
return ConsumerSecret + "&" + (OAuthTokenSecret != null ? OAuthTokenSecret : "");
}
In your case, to make a signed request, you just need pass null value for OAuthTokenSecret parameter.
Ok, now you have a signature base string, all you need to do now is to sign using HMAC-SHA1 algorithm:
public static string Sign(string signatureBaseString, string signingKey)
{
var keyBytes = System.Text.Encoding.ASCII.GetBytes(signingKey);
using (var myhmacsha1 = new System.Security.Cryptography.HMACSHA1(keyBytes)) {
byte[] byteArray = System.Text.Encoding.ASCII.GetBytes(signatureBaseString);
var stream = new MemoryStream(byteArray);
var signedValue = myhmacsha1.ComputeHash(stream);
var result = Convert.ToBase64String(signedValue, Base64FormattingOptions.None);
return result;
}
}
To sum up this is the whole process for calculating signature:
public virtual string GetSignature(string consumerSecret, string tokenSecret, string uri, string method, params ParameterSet[] parameters)
{
var allParameters = parameters.SelectMany(p => p.ToList()).ToArray();
var parametersString = CalculateSignatureBaseString(allParameters);
var signatureBaseString = OAuth1aUtil.CalcualteSignatureBaseString(method, uri, parametersString);
var sigingKey = GetSigningKey(consumerSecret, tokenSecret);
var signature = Sign(signatureBaseString, sigingKey);
return signature;
}
Making request
Now you just need to make a valid http request and add the oauth parameters to the request as the 'Authorization' header.