Coinbase unauthorized access - c#

I'm trying to list my coinbase accounts with the following API https://api.coinbase.com/.
I used the following path GET https://api.coinbase.com/v2/accounts.
I systematically get a reject 'Unauthorized (401) error'
Below some important checks I have done:
My software correctly get ressource which does not require specific authorization level like /v2/time for instance
I have waited 48 hours since my API key creation
I have set all the privileges on all the accounts (BTC wallet, EUR wallet etc)
I manage to view my balance on my trading account https://api.pro.coinbase.com/
Any ideas ?
HttpClient _httpClient
var timeStamp = GetSecondsSinceEpoch();
var signature = ComputeSignature($"{timeStamp}GET/v2/accounts", privateKey);
_httpClient.DefaultRequestHeaders.Add("User-Agent", "MyAppClient");
_httpClient.DefaultRequestHeaders.Add("CB-ACCESS-KEY", publicKey);
_httpClient.DefaultRequestHeaders.Add("CB-ACCESS-SIGN", signature);
_httpClient.DefaultRequestHeaders.Add("CB-ACCESS-TIMESTAMP", timeStamp);
private static string ComputeSignature(string preHashString, string privateKey)
{
try
{
using (var hmac = new HMACSHA256(Convert.FromBase64String(privateKey)))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(preHashString));
return Convert.ToBase64String(hash);
}
}
catch (Exception)
{
//
}
return string.Empty;
}
var response = await _httpClient.DefaultRequestHeaders.GetAsync(new Uri("https://api.coinbase.com/v2/accounts"));
response.EnsureSuccessStatusCode();
var jsonString = await response.Content.ReadAsStringAsync();
EDIT
In fact for access to GET https://api.coinbase.com/v2/accounts, it seems that we should use OAuth authorization and not API KEY, so it takes the form of :
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer","MyAccessToken")
Once I have created Oauth access via my Coinbase Main Account, I successfully manage to retrieve my accounts information by using Invoke-WebRequest command but I still get Unauthorized (401) error with the CSharp code equivalent

Related

Updating class to use MFA for accessing Dynamics 365

The system administrator enabled 2FA so I'm having to go through and update some programs to utilizes this for accessing the Dynamics API. Otherwise, we received the following:
{
"error":"interaction_required",
"error_description":"AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access '00000007-0000-0000-c000-000000000000'.\r\nTrace ID: 24822bc6-9e93-476d-8580-fd04e3889300\r\nCorrelation ID: efd5dbc5-dead-4665-a5a6-570ae15a55fb\r\nTimestamp: 2020-02-24 20:35:15Z",
"error_codes":[
50076
],
"timestamp":"2020-02-24 20:35:15Z",
"trace_id":"24822bc6-9e93-476d-8580-fd04e3889300",
"correlation_id":"efd5dbc5-dead-4665-a5a6-570ae15a55fb",
"error_uri":"https://login.windows.net/error?code=50076",
"suberror":"basic_action"
}
This article makes it sound pretty straight forward and is the process we had to use for Outlook and other apps. Basically, generating an App Password.
However, I'm trying to use the App Password instead of the Default password we've used for a while and still am unable to get an access token for the program to use.
Here is what we've been using:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace CrmQbInvoiceSync
{
class CrmAuthorization
{
// Serialize the JSON response for the access_token
public class AccessToken
{
public string access_token { get; set; }
}
public static async Task<string> GetCrmAccessToken()
{
var values = new Dictionary<string, string>
{
// Connection parameters
{"client_id", ConfigurationManager.AppSettings["clientId"]},
{"client_secret", ConfigurationManager.AppSettings["clientSecret"]},
{"resource", ConfigurationManager.AppSettings["crmOrg"]},
{"username", ConfigurationManager.AppSettings["username"]},
{"password", ConfigurationManager.AppSettings["userPassword"]},
{"grant_type", "password"}
};
// Console.WriteLine(values);
// Convert to x-www-form-urlencoded
var content = new FormUrlEncodedContent(values);
try
{
// Send the x-www-form-urlencoded info to the OAuth2 end point
HttpResponseMessage response = await Services.Client.PostAsync(ConfigurationManager.AppSettings["crmUri"], content);
// Get the body from the response
var responseContent = await response.Content.ReadAsStringAsync();
// Extract out the access token from the response
AccessToken responseBody = JsonConvert.DeserializeObject<AccessToken>(responseContent);
// Test if there is an access token present
if (responseBody.access_token != null)
{
// If there is an access token, take it and use it in
// generating the query
var accessToken = responseBody.access_token;
return accessToken;
}
else
{
var accessToken = "Could not get the access token.";
Services.WriteLogFile(accessToken);
Console.WriteLine(accessToken);
return null;
}
}
catch (Exception e)
{
var error = e;
Services.WriteLogFile(error.ToString());
Console.WriteLine(error);
throw;
}
}
}
}
The {"password", ConfigurationManager.AppSettings["userPassword"]} line is what should be affected so I updated the AppSettings with the new App Password. Get this error, but seems like it should be working given I'm using the App Password:
Formatted JSON Data
{
"error":"invalid_grant",
"error_description":"AADSTS50126: Error validating credentials due to invalid username or password.\r\nTrace ID: 17bf1365-32a0-439e-bd99-9eaf8e3bab00\r\nCorrelation ID: 4d24cac1-dae9-49b7-961f-c7c739f885f4\r\nTimestamp: 2020-02-24 20:33:43Z",
"error_codes":[
50126
],
"timestamp":"2020-02-24 20:33:43Z",
"trace_id":"17bf1365-32a0-439e-bd99-9eaf8e3bab00",
"correlation_id":"4d24cac1-dae9-49b7-961f-c7c739f885f4",
"error_uri":"https://login.windows.net/error?code=50126"
}
Really, not sure if I should be updating something else in the program to accommodate MFA, but articles I've read indicate I should just be generating the App Password and it should be good. Suggestions?
I suggest you use a refresh token to refresh the access token. With refresh token, you can bypass this limitation of MFA.
To get a refresh token, you need to follow Azure AD OAuth2 auth code flow to get a refresh token interactively. And then you can get a new token with the refresh token you got.
Notice that the refresh token should be kept in secret. If it was compromised, you can revoke all refresh tokens of a specific use with PowerShell Revoke-AzureADUserAllRefreshToken

CryptographicException in StateDataFormat.Unprotect()

I recently posted a question which has been answered but led to this new problem. If interested, it can be seen at Previous post.
Intro
I am currently developing an application using AD-B2C as my identity provider. This is integrated into the solution using their guidelines at AD B2C graph, which uses openid-connect.
I need to use a form of email activation (outside of their register policy) and as such I need to be able to pass a value from the URL in the email, through the sign-up process at B2C and back to the redirection URL.
For this we use the state parameter.
Problem
In my OnRedirectToIdentityProvider I encrypt the state
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var temp = notification.ProtocolMessage.State;
// To be used later
var mycustomparameter = notification.OwinContext.Get<string>("mycustomparameter");
if (notification.ProtocolMessage.State != null)
{
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("mycustomparameter", "testing");
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
}
return Task.FromResult(0);
}
This works for all I can tell.
Now the user is passed to the sign in on the AD B2C and is after the login redirected back where the OnMessageReceived is triggered.
private Task OnMessageReceived(MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
string mycustomparameter;
var protectedState = notification.ProtocolMessage.State.Split('=')[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.TryGetValue("mycustomparameter", out mycustomparameter);
return Task.FromResult(0);
}
this is where it breaks. In the ...StateDataFormat.Unprotect(protectedState)
It throws an error System.Security.Cryptography.CryptographicException with the message "Error occurred during a cryptographic operation."
EDIT: Stacktrace:
System.Web.dll!System.Web.Security.Cryptography.HomogenizingCryptoServiceWrapper.HomogenizeErrors(System.Func<byte[], byte[]> func, byte[] input) Unknown
System.Web.dll!System.Web.Security.Cryptography.HomogenizingCryptoServiceWrapper.Unprotect(byte[] protectedData) Unknown
System.Web.dll!System.Web.Security.MachineKey.Unprotect(System.Web.Security.Cryptography.ICryptoServiceProvider cryptoServiceProvider, byte[] protectedData, string[] purposes) Unknown
System.Web.dll!System.Web.Security.MachineKey.Unprotect(byte[] protectedData, string[] purposes) Unknown
Microsoft.Owin.Host.SystemWeb.dll!Microsoft.Owin.Host.SystemWeb.DataProtection.MachineKeyDataProtector.Unprotect(byte[] protectedData) Unknown
Microsoft.Owin.Security.dll!Microsoft.Owin.Security.DataProtection.AppBuilderExtensions.CallDataProtectionProvider.CallDataProtection.Unprotect(byte[] protectedData) Unknown
Microsoft.Owin.Security.dll!Microsoft.Owin.Security.DataHandler.SecureDataFormat<Microsoft.Owin.Security.AuthenticationProperties>.Unprotect(string protectedText) Unknown
IntellifyPortal.dll!IntellifyPortal.Startup.OnMessageReceived(Microsoft.Owin.Security.Notifications.MessageReceivedNotification notification) Line 171 C#
My attempts
I have tried specifying machine keys in the Web.config
I have tried messing with the "CallbackPath property in OpenIdConnectAuthenticationOptions, with no success.
I have tried a lot of diffent tweaks, but I can't seem to figure out why I can't "unprotect" the inbound state.
Any help is appreciated,
Best regards.
Update: Solution
I have decided to use an alternative method, which I found to work(hopefully it may of use to others):
Azure-sample which I used as guidance
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policy = notification.OwinContext.Get<string>("Policy");
if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
{
notification.ProtocolMessage.Scope = OpenIdConnectScopes.OpenId;
notification.ProtocolMessage.ResponseType = OpenIdConnectResponseTypes.IdToken;
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
}
// Accept Invitation Email
string testValue= notification.OwinContext.Get<string>("testValue");
string testValue2= notification.OwinContext.Get<string>("testValue2");
if (!string.IsNullOrEmpty(testValue) && !string.IsNullOrEmpty(testValue2))
{
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("testValue", testValue);
state.Dictionary.Add("testValue2", testValue2);
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
}
return Task.FromResult(0);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
// Extract the code from the response notification
var code = notification.Code;
string signedInUserID = notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, new ClientCredential(ClientSecret), userTokenCache, null);
try
{
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, Scopes);
// Look for acceptInvitation
string testValue;
string testValue2;
var protectedState = notification.ProtocolMessage.State.Split('=')[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.TryGetValue("testValue", out testValue);
state.Dictionary.TryGetValue("testValue2", out testValue2);
// InvitationAccept / store values
if(!string.IsNullOrEmpty(testValue) && !string.IsNullOrEmpty(testValue2))
{
// How can I pass values to the redirect controller?
// Can I somehow transfer it from here to that destination
}
}
catch (Exception ex)
{
//TODO: Handle
throw;
}
}
Final Question
I can now receive the values back as expected. These values has to be used in creating a relation between the new account and other accounts/groups in the application.
I therefore want to transfer these values back to the application (controller) for processing. I've tried storing the values in the context, in the response headers and in the claims to no avail. I guess this is because that this is the "middleware" and that the actual "redirect" happens directly from AD B2C thus not holding my params.
Can I somehow get the params back to the controller as well, without relying on the request URI (originating from the original user link) - Preferably directly in the claims, so that a user already logged in does not have to "re-signin" upon clicking the link.
How can I get my values (in the state, which are handled in the OnMessageRecieved) passed to the controller which is redirected to?
You're not supposed to decrypt the hint. Instead of this:
ProtocolMessage.State.Split('
Remove the hint so you only have encrypted data:
ProtocolMessage.State.Parameters["state"].Replace("OpenId.AuthenticationOptions=","")
Then you can you decrypt value of sate:
StateDataFormat.Unprotect("TC%$t43tj9358utj3")
It should deserialize to AuthenticationOptions.

C# TweetSharp not sending Tweets

I am using TweetSharp to send tweets to users (currently testing it) however it keeps coming back with Bad Authentication Data
{"errors":[{"code":215,"message":"Bad Authentication data."}]}
I have checked my app settings and it has full read and write access. I have also tried to regenerate my consumer keys but still not luck.
here is my code
public ActionResult AccessToken()
{
string oauth_consumer_key = "<consumer key>";
string oauth_consumer_secret = "<consumer secret>";
var service = new TwitterService(oauth_consumer_key, oauth_consumer_secret);
// Now we need the Token and TokenSecret
OAuthRequestToken requestToken = service.GetRequestToken("http://localhost:37808/");
string authURL = service.GetAuthorizationUri(requestToken).ToString();
Process.Start(authURL);
SendTweetOptions options = new SendTweetOptions();
options.Status = "Hello there Twitter";
service.SendTweet(options);
var re = service.Response.Response;
return View();
}
Am I doing anything wrong?
Finally solved the issue and it works well. Based upon comments from Yort.
public ActionResult AccessToken()
{
// Step 1 - Retrieve an OAuth Request Token
TwitterService service = new TwitterService(ConfigurationManager.AppSettings["TwitterConsumerKey"], ConfigurationManager.AppSettings["TwitterConsumerSecret"]);
// This is the registered callback URL
OAuthRequestToken requestToken = service.GetRequestToken("http://localhost:37808/Twitter/OToken");
// Step 2 - Redirect to the OAuth Authorization URL
Uri uri = service.GetAuthorizationUri(requestToken);
return new RedirectResult(uri.ToString(), false /*permanent*/);
//return View();
}
public ActionResult OToken()
{
return View();
}
public ActionResult UserInfo(string oauth_token, string oauth_verifier)
{
var requestToken = new OAuthRequestToken { Token = oauth_token };
// Step 3 - Exchange the Request Token for an Access Token
TwitterService service = new TwitterService(ConfigurationManager.AppSettings["TwitterConsumerKey"],
ConfigurationManager.AppSettings["TwitterConsumerSecret"]);
OAuthAccessToken accessToken = service.GetAccessToken(requestToken, oauth_verifier);
// Step 4 - User authenticates using the Access Token
service.AuthenticateWith(accessToken.Token, accessToken.TokenSecret);
TwitterUser user = service.VerifyCredentials(new VerifyCredentialsOptions());
ViewBag.Message = string.Format("{0}", user.ScreenName);
// Step 5 - Send Tweet to User TimeLine
SendTweetOptions options = new SendTweetOptions();
string URL = "file:\\C:\\Users\\<User>\\Desktop\\test.jpg";
string path = new Uri(URL).LocalPath;
// Sending with Media
using (var stream = new FileStream(path, FileMode.Open))
{
service.SendTweetWithMedia(new SendTweetWithMediaOptions
{
Status = "<status>",
Images = new Dictionary<string, Stream> { { path, stream } }
});
}
var responseText = service.Response.StatusCode;
if (responseText.ToString() == "OK")
{
ViewBag.Message = "Tweet Successful";
}
else
{
ViewBag.Message = "Tweet Unsuccessful";
}
return View();
}
}
I don't believe you can send Tweets as just a consumer, the Tweets have to be "owned" by a user account. You need to register a Twitter account, then do the full oauth authentication process to get an access token (in addition to the consumer token), then reauthorise the TweetSharp service using both tokens.
Your code above nearly gets there (I think). After the Process.start call there needs to be logic to use the verifier returned in the browser (a number displayed after the user logs in) to complete the auth process and act as that user. At the moment, your code gets half way through that process but does not complete it, so when you try to tweet your TweetSharp service is only authed as the app and not the user.
The originalTweetSharp readme.md does include the missing bits of code. Step 3 needs the actual verifier returned in the browser after login:
// Step 3 - Exchange the Request Token for an Access Token
string verifier = "123456"; // <-- This is input into your application by your user
OAuthAccessToken access = service.GetAccessToken(requestToken, verifier);
// Step 4 - User authenticates using the Access Token
service.AuthenticateWith(access.Token, access.TokenSecret);
//Now your tweet call should work here.
It also looks like you're doing this in a web app on the server? In which case you're using entirely the wrong oauth flow (I believe). This one is designed for desktop apps, hence the call that starts a new browser process for the user to login with. I'm not entirely sure how the web flow works as I've never used it, but I believe you need to redirect the user to the authorisation url you receive, and the callback registered with Twitter should point back to your site. I think there is some kind of state parameter that can be passed back through the oauth flow so you can implement your own logic to pickup where you left off based on a session id or similar.
I worked on this subject before. You have to developer account before the send tweet because you need tokens and keys. It's my windows service project.
I wrote my tokens and key codes in App.config
<appSettings>
<add key="twitterAccessToken" value="*****"/>
<add key="twitterAccessTokenSecret" value="*****"/>
<add key="twitterConsumerKey" value="*****"/>
<add key="twitterConsumerSecret" value="*****"/>
public static void SendTweet()
{
try
{
GetPixelImageFile();
string key = ConfigurationSettings.AppSettings.Get("twitterConsumerKey");
string secret = ConfigurationSettings.AppSettings.Get("twitterConsumerSecret");
string token = ConfigurationSettings.AppSettings.Get("twitterAccessToken");
string tokenSecret = ConfigurationSettings.AppSettings.Get("twitterAccessTokenSecret");
string message = "Color, Colorful, Pixel, Art, PixelColouring, Follow";
var service = new TweetSharp.TwitterService(key, secret);
service.AuthenticateWith(token, tokenSecret);
using (var stream = new FileStream(#"C:\Images\Pixel.png", FileMode.Open))
{
var result = service.SendTweetWithMedia(new SendTweetWithMediaOptions
{
Status = message,
Images = new Dictionary<string, Stream> { { "john", stream } }
});
SendMail("SendTweet", (result == null ? "" : result.Text));
}
}
catch (Exception ex)
{
SendMail("SendTweet", ex.Message);
}
}

How to post Twitter message to authenticated user's timeline via OAuth

I've been wrestling with the Twitter API for a few days now but I cannot post a message to an authenticated user's timeline. I've got an ASP.NET MVC 4 application that signs a user in via Twitter and saves the access token that comes back from the sign in process. That part works fine. I can see my application with read and write permissions within the authenticated user's twitter account.
I'm then using that access token, along with the consumer key, consumer secret and oauth token secret associated with my Twitter application, to post to the user's timeline. I'm getting a 401 unauthorised error every time. I've tried using the 1.1 API and the 1 API with the same result.
Most of the code comes from Gary Short's article here: http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/
Here's what I've got so far. If anyone can spot any clues as to what I'm missing I'd be most grateful.
public async Task<bool> Push(TwitterMessage twitterMessage)
{
const string updateApi = "http://api.twitter.com/1/statuses/update.json";
const string oauthConsumerKey = "<consumerKey>";
const string consumerSecret = "<consumerSecret>";
const string oauthSignatureMethod = "HMAC-SHA1";
const string oauthTokenSecret = "<tokenSecret>";
var signingKey = string.Format("{0}&{1}", consumerSecret.Escaped(), oauthTokenSecret.Escaped());
var postBody = "status=" + Uri.EscapeDataString(twitterMessage.MessageContent);
var oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
var oauthToken = "<authenticatedUserToken>";
var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
var oauthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();
var message = string.Format("POST {0}?{1} HTTP/1.1", updateApi, postBody.Escaped());
var hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
var signatureString = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(message)));
ServicePointManager.Expect100Continue = false;
var request = (HttpWebRequest)WebRequest.Create(updateApi);
request.KeepAlive = false;
var authorisationBuilder = new StringBuilder();
authorisationBuilder.Append("OAuth ");
authorisationBuilder.AppendFormat("oauth_consumer_key=\"{0}\",", oauthConsumerKey.Escaped());
authorisationBuilder.AppendFormat("oauth_signature_method=\"{0}\",", oauthSignatureMethod.Escaped());
authorisationBuilder.AppendFormat("oauth_timestamp=\"{0}\",", oauthTimestamp.Escaped());
authorisationBuilder.AppendFormat("oauth_nonce=\"{0}\",", oauthNonce.Escaped());
authorisationBuilder.AppendFormat("oauth_token=\"{0}\",", oauthToken.Escaped());
authorisationBuilder.AppendFormat("oauth_signature=\"{0}\"", signatureString.Escaped());
var authorisation = authorisationBuilder.ToString();
request.Headers.Add("Authorization", authorisation);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (var stream = await request.GetRequestStreamAsync())
{
var bodyBytes = new ASCIIEncoding().GetBytes(postBody);
stream.Write(bodyBytes, 0, bodyBytes.Length);
}
//Allow us a reasonable timeout in case Twitter's busy
request.Timeout = 3 * 60 * 1000;
try
{
var response = await request.GetResponseAsync() as HttpWebResponse;
return true;
}
catch (WebException)
{
return false;
}
}
public static string Escaped(this string input)
{
return Uri.EscapeDataString(input);
}
UPDATE Looking at this SO post it looks like I can't use the DotNetOpenAuth twitter client for authorisation, which I had been doing. The suggestion there is to extend the twitter consumer class instead to perform the authorisation, which will allow me to retrieve the user's token secret (the missing piece of my puzzle I think). Will post another update when I get this working.
Check this code and link/article simple and easy :
protected void btnTweet_Click(object sender, EventArgs e)
{
string oauthAccessToken = Session["twtoken"].ToString();
string oauthAccessTokenSecret = Session["twsecret"].ToString();
OAuthHelper oauthhelper = new OAuthHelper();
oauthhelper.TweetOnBehalfOf(oauthAccessToken, oauthAccessTokenSecret, txtTweet.Text);
if (string.IsNullOrEmpty(oauthhelper.oauth_error))
Response.Write("Twit Posted Successfully");
else
Response.Write(oauthhelper.oauth_error);
}
Read more how to get access token and secret key and download OAuthHelper and OAuthUtility Class below is the link -
How to post tweet on behalf of an user from asp.net using oauth authentication
Login with twitter using oauth authentication in asp.net and get access token, screen name and userid
So the problem is an issue with DotNetOpenAuth as it currently stands. For Twitter authentication, the DotNetOpenAuth client doesn't allow for the full authorisation flow (needed for posting to a user's timeline). Only the access token is retrieved from the initial handshake and not the access token secret. I was using the access token secret associated with my Twitter app, rather than the Twitter user who was signing in, so authorisation was failing every time.
UPDATE: I've finally gone with using Daniel Crenna's Tweetsharp library, which makes the code a little simpler than writing my own API wrapper would have been:
public async Task<bool> Push(TwitterAccount account)
{
var twitterService = new TwitterService(consumerKey, consumerSecret);
twitterService.AuthenticateWith(account.AccessToken, account.AccessTokenSecret);
var options = new SendTweetOptions {Status = string.Format("{0} {1}", account.Message.MessageContent, account.Message.ShortLink)};
var status = twitterService.SendTweet(options);
return status != null;
}

Getting information with Token. OAuth

I am creating an app to get information from Fitbit.com using OAuth.
protected void btnConnect_Click(object sender, EventArgs e)
{
// Create OAuthService object, containing oauth consumer configuration
OAuthService service = OAuthService.Create(
new EndPoint(RequestTokenUrl, "POST"), // requestTokenEndPoint
new Uri(AuthorizationUrl), // authorizationUri
new EndPoint(AccessTokenUrl, "POST"), // accessTokenEndPoint
true, // useAuthorizationHeader
"http://app.fitbit.com", // realm
"HMAC-SHA1", // signatureMethod
"1.0", // oauthVersion
new OAuthConsumer(ConsumerKey, ConsumerSecret) // consumer
);
try
{
var personRepository = new PersonRepository();
var person = personRepository.GetPersonById(int.Parse(personSelect.SelectedItem.Value));
OAuthRequest request = OAuthRequest.Create(
new EndPoint(ProfileUrl, "GET"),
service,
this.Context.Request.Url,
//this.Context.Session.SessionID);
person.FitbitAuthAccessToken,
);
request.VerificationHandler = AspNetOAuthRequest.HandleVerification;
OAuthResponse response = request.GetResource();
// Check if OAuthResponse object has protected resource
if (!response.HasProtectedResource)
{
var token = new OAuthToken(TokenType.Request, person.FitbitAuthAccessToken,
person.FitbitAuthSecret, ConsumerKey);
// If not we are not authorized yet, build authorization URL and redirect to it
string authorizationUrl = service.BuildAuthorizationUrl(response.Token).AbsoluteUri;
Response.Redirect(authorizationUrl);
}
person.FitbitAuthAccessToken = response.Token.Token;
person.FitbitAuthSecret = response.Token.Secret;
person.PersonEncodedId = Doc["result"]["user"]["encodedId"].InnerText;
personRepository.Update(person);
// Store the access token in session variable
Session["access_token"] = response.Token;
}
catch (WebException ex)
{
Response.Write(ex.Message);
Response.Close();
}
catch (OAuthRequestException ex)
{
Response.Write(ex.Message);
Response.Close();
}
}
I save Fitbit Access Token and Secret in database.
How can I get information using just Access token and secret, without authorizing every time?
This would assume that the FitBit api was robust enough to not quire authentication every single time. I have seen API's implementing OAuth where you have an authentication process, then from there most of your calls simply require the AccessToken or secret. I would look at the method signatures for the service and see what types of parameters they are requiring.
If you look at the FitBit API about authentication and accessing resources, you will see that you just need to request the data you are interested in and add in the oAuth header with the access token. Here is what it should look like (from the API page):
GET /1/user/-/activities/date/2010-04-02.json HTTP/1.1
Host: api.fitbit.com
Authorization: OAuth realm="api.fitbit.com",
oauth_consumer_key="fitbit-example-client-application",
oauth_token="8d3221fb072f31b5ef1b3bcfc5d8a27a",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1270248088",
oauth_nonce="515379974",
oauth_signature="Gf5NUq1Pvg3DrtxHJyVaMXq4Foo%3D"
oauth_version="1.0"`
The base signature string will look like:
GET&http%3A%2F%2Fapi.fitbit.com%2F1%2Fuser%2F-%2Factivities%2Fdate%2F2010-04-02.json&oauth_consumer_key%3Dfitbit-example-client-application%26oauth_nonce%3D515379974%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270248088%26oauth_token%3D8d3221fb072f31b5ef1b3bcfc5d8a27a%26oauth_version%3D1.0
I figured I'd offer my VerifyAuthenticationCore that is part of my FitbitClient that inherits from OAuthClient. It took me a while to get this working but I found that I was missing HttpDeliveryMethods.AuthorizationHeaderRequest when I was creating the web request. Adding this allowed the call to stop returning bad request (400) error messages.
The code below is basically using the user id and the access token to get the user profile information. All calls should basically work this way. All you would need to do is change the url and provide the id and token.
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
string username;
var accessToken = response.AccessToken;
var userId = response.ExtraData["encoded_user_id"];
var httpWebRequest = WebWorker.PrepareAuthorizedRequest(new MessageReceivingEndpoint(new Uri("http://api.fitbit.com/1/user/" + userId + "/profile.json"), HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), accessToken);
var dictionary = new Dictionary<string, string>();
dictionary.Add("accesstoken", accessToken);
dictionary.Add("link", "http://www.fitbit.com/user/" + userId);
using (var webResponse = httpWebRequest.GetResponse())
{
using (var stream = webResponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
var profile = JObject.Parse(reader.ReadToEnd())["user"];
dictionary.AddItemIfNotEmpty("name", profile["displayName"]);
dictionary.AddItemIfNotEmpty("pictureUrl", profile["avatar"]);
username = dictionary["name"];
}
}
return new AuthenticationResult(true, ProviderName, userId, username, dictionary);
}

Categories