Trapping errors in Aurelias HTTP client - ajax

Hi All (Especially the Aurelia core team hanging about round here)
I have an aurelia app using the "aurelia-http-client" to make requests to my back end API.
My back end API is a C# based service running on Nancy.
In my front end Iv'e abstracted the http client out to my own network lib as follows:
import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { HttpClient } from 'aurelia-http-client';
import environment from './environment';
#inject(HttpClient, Router)
export default class httpservice {
private http: HttpClient = null;
private router: Router = null;
private authService: any = null;
private authToken: string = "";
constructor(HttpClient, Router) {
this.http = HttpClient;
this.router = Router;
HttpClient.configure(http => {
http.withBaseUrl(environment.servicebase);
});
}
public setAuthService(authService: any) {
this.authService = authService;
}
public get(url: string, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let self = this;
let client = this.http
.createRequest(url)
.asGet()
.withHeader("AuthenticationToken", myToken)
.withInterceptor({
responseError(responseError) {
console.log(responseError);
if (responseError.statusCode === 401) {
if (myAuth) {
myAuth.destroySession();
}
}
if (responseError.statusCode === 404) {
self.router.navigateToRoute("missing");
}
return responseError;
}
});
return client;
}
public post(url: string, postData: any, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let self = this;
let client = this.http
.createRequest(url)
.asPost().withContent(postData)
.withHeader("AuthenticationToken", myToken)
.withInterceptor({
responseError(responseError) {
console.log(responseError);
if (responseError.statusCode === 401) {
if (myAuth) {
myAuth.destroySession();
}
}
if (responseError.statusCode === 404) {
self.router.navigateToRoute("missing");
}
return responseError;
}
});
return client;
}
}
and I then use this in my other modules/classes as follows:
import { Aurelia, inject } from 'aurelia-framework';
import HttpService from './httpservice';
import environment from './environment';
import { EventAggregator } from 'aurelia-event-aggregator';
#inject(EventAggregator, Aurelia, HttpService)
export default class Authservice {
public http: HttpService = null;
public app: Aurelia = null;
public ea: EventAggregator = null;
public authToken: any = null;
private loginUrl: string = "";
private logoutUrl: string = "";
private checkUrl: string = "";
constructor(eventAggregator, aurelia, httpService) {
this.http = httpService;
this.app = aurelia;
this.ea = eventAggregator;
this.loginUrl = "/login";
}
public getAuthToken() {
if (!sessionStorage[environment.tokenname] ||
(sessionStorage[environment.tokenname] == null)) {
return null;
}
return sessionStorage[environment.tokenname];
}
public login(loginName, password) {
let postData = {
loginName: loginName,
password: password
};
let client = this.http.post(this.loginUrl, postData);
client.send()
.then((response) => response.content)
.then((data) => {
if (data.error) {
this.ea.publish("loginMessage", { message: data.errorMessage });
return;
}
if (data.authenticationFailed) {
this.ea.publish("loginMessage", { message: "Invalid user name and/or password supplied." });
return;
}
if (data.accountSuspended) {
this.ea.publish("loginMessage", { message: "Your account has been suspended, please contact support." });
return;
}
sessionStorage[environment.tokenname] = data.token;
sessionStorage["displayedLoginName"] = data.displayName;
location.assign('#/');
this.app.setRoot('app');
})
.catch(() =>
{
debugger;
alert("Something bad happened trying to connect to server.");
});
}
public isAuthenticated() {
// TODO: hook this up to check auth token validity via rest call???
let token = this.getAuthToken();
return token !== null;
}
}
enum LoginStates {
LoginValid = 0,
BadUserNameOrPassword,
AccountSuspended
}
Please note I've stripped some of the code out of the auth library to reduce confusion
In general ALL of this works well. The interceptors get triggered when 401s and 404s occur, and if I add a 500 that get's handled too, so where all good there.
The problem I have is handling communication failures.
As you can see in the login routine, I have a catch following the then.
I expected that if the server couldn't be reached or some other base communications failure occurred, that this catch would trigger rather than the "then" and thus allow me to handle the error, but instead it does not.
What I get instead is this in the console:
Worse still, my login routine doesn't abort, it actually succeeds and allows the logged in page to be shown.
It seems that while the library is making the OPTIONS call (Which is when this error occurs) none of my user code is taken into account.
The OPTIONS call is required for successful pre-flight/ajax requests, so stopping this happening is not an option, and I feel that if the OPTIONS call did not abort, but made it to the POST call,t hat my error handling would then be taken into consideration.
It seems silly to be not able to trap errors like this, especially in today's mobile world where a device may be out of coverage or temporarily offline.
If anyone has any thoughts on how this can be solved, I'd love to hear them.
Update 1
My problem seems to be related to this one:
aurelia-fetch-client - a promise was rejected with a non-error: [object Response]
However, I'm not using "useStandardConfiguration()" which is apparently the cause for that case. I'm also not using the fetch client, however I do note that the API in both clients is practically the same, so I wonder if the underlying code is also similar.

Ok.... so, after a long hard afternoon of head scratching and hair pulling, it turns out, the whole thing is actually linked to a reported issue with the "BlueBird promises library" which is what aurelia uses to manage it's promises.
The link to the issue with BlueBird can be found here:
https://github.com/petkaantonov/bluebird/issues/990
It's not specifically an issue according to the BB dev's but to many folks encountering it, it sure looks like one.
The bottom line is that the library is not designed to throw the errors generated directly by it (As the example on the issue page shows)
The correct way according to the BB team, is to either throw a new error completely, or derive a new instance from the one passed to the promise, and alter the parameters to it before then re-throwing it.
Of course, because of the abstraction in Aurelia, this is not an option for most of us, unless we want to go about changing the http client library code.
Some of the marks for this need to go to "TheBlueFox" for His/Her comments above.
The solution ended up being something like the following:
import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { HttpClient, Interceptor } from 'aurelia-http-client';
import environment from './environment';
import Debugger = require("_debugger");
#inject(HttpClient, Router)
export default class httpservice {
private http: HttpClient = null;
private router: Router = null;
private authService: any = null;
private authToken: string = "";
private myInterceptors: Interceptor;
constructor(HttpClient, Router) {
this.http = HttpClient;
this.router = Router;
HttpClient.configure(http => {
http.withBaseUrl(environment.servicebase);
http.withInterceptor(new HttpInterceptors());
});
}
public setAuthService(authService: any) {
this.authService = authService;
}
public get(url: string, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let client = this.http
.createRequest(url)
.asGet()
.withHeader("AuthenticationToken", myToken);
return client;
}
public post(url: string, postData: any, authObject?: any): any {
let myAuth = this.authService ? this.authService : authObject;
let myToken = "";
if (myAuth) {
myToken = myAuth.getAuthToken();
}
let self = this;
let client = this.http
.createRequest(url)
.asPost().withContent(postData)
.withHeader("AuthenticationToken", myToken);
return client;
}
}
class HttpInterceptors implements Interceptor {
responceError(error)
{
if (error.statusCode === 0) {
throw new Error("Could not contact server");
}
if (error.statusCode === 401) {
// do auth handling here
}
if (error.statusCode === 404) {
// do 404 handling here
}
return error;
}
}
The magic is in the HttpInterceptors class attached to the bottom of my HttpService. You should be able to see a check for a status code of 0, and that the actual action performed here is to throw a new error.
It's the action of this new error being thrown that then causes the "catch" in the actual call to the http client to be caught.
If you don't throw at that point, then everything just falls apart and you get the scenario seen in my original question post, throw and you get to catch it and deal with it in user code.
This way of doing things is also apparent in the aurelia-fetch-client too, as that works in a broadly similar way, using the BlueBird promise library.

Related

Enabling OPTIONS method in CORS during REST request from AJAX on WCF Service

I have scratched my head for 7 hours trying to figure this out. I have searched all over the web but no luck. I have an Angular App that is making requests to a WCF command-line hosted service application. I managed to get by CORS by using these two classes:
public class CustomHeaderMessageInspector : IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CustomHeaderMessageInspector(Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request,
System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
And:
public class EnableCorsBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{ }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>();
requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
}
public void Validate(ServiceEndpoint endpoint) { }
public override Type BehaviorType
{
get { return typeof(EnableCorsBehavior); }
}
protected override object CreateBehavior()
{
return new EnableCorsBehavior();
}
}
Adding this custom extension to the app.config file solved my CORS problem. My current problem is whenever I make a POST request, I get the error:
Request Method:OPTIONS
Status Code:405 Method Not Allowed
I am quite new to C# and I can't seem to find where to place the code that will allow me to get past this. I have an idea that it should be placed somewhere in the BeforeSendReply() method. Please help me! I will really really appreciate it!
Regards!
I figured out the solution to this and i hope this helps everyone who comes across this same issue. In the CustomHeaderMessageInspector class that I posted in the question, I edited the following code in the AfterReceiveRequest method as follows:
// return null;
var httpRequest = (HttpRequestMessageProperty)request
.Properties[HttpRequestMessageProperty.Name];
return new
{
origin = httpRequest.Headers["Origin"],
handlePreflight = httpRequest.Method.Equals("OPTIONS",
StringComparison.InvariantCultureIgnoreCase)
};
What I hoped that code did is monitor any request with the OPTIONS method and "tag" it with a preflight state. Then I modified the code in the BeforeSendReply to look as follows:
var state = (dynamic)correlationState;
if (state.handlePreflight)
{
reply = Message.CreateMessage(MessageVersion.None, "PreflightReturn");
var httpResponse = new HttpResponseMessageProperty();
reply.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
httpResponse.SuppressEntityBody = true;
httpResponse.StatusCode = HttpStatusCode.OK;
}
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
What that does (i hope) is get any request tagged with OPTIONS and handle it by returning a 200 status code. This got it finally working and I hope it helps someone!
In addition to realnsleo answer:
I had problems to use (dynamic)correlationState because my project has to be
in Framework 3.5
I tried to simplify some lines, too:
private class CORSHeaderInjectingMessageInspector : IDispatchMessageInspector
{
private static IDictionary<string, string> _headersToInject = new Dictionary<string, string>
{
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS" },
{ "Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Origin,Accept" },
{ "Access-Control-Request-Headers", "POST" }
};
public object AfterReceiveRequest( ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
return httpRequest.Method.Equals("OPTIONS", StringComparison.InvariantCulture);
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if ((bool) correlationState)
{
var httpResponse = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
httpResponse.SuppressEntityBody = true;
httpResponse.StatusCode = HttpStatusCode.OK;
}
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in _headersToInject)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}

Get HttpHeaders from HttpRequestException?

I have a Web API, When the incoming request is not valid then the API sends back a HttpStatusCode.BadRequest and API would also add a CorrelationId into Response's HttpHeader. Something like below
public class ValidateRequestAttribute : ActionFilterAttribute
{
public ValidateRequestAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid == false)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.HttpContext.Response.Headers.Add("x-correlationid", "someid");
context.Result = new ContentResult()
{
Content = "bad request."
};
}
}
}
On client side im using HttpClient to access the API. I am not sure how client would retrieve HttpStatusCode and HttpHeader here. Here is my client code
public bool Process(url)
{
bool result = false;
try
{
Task.Run(async () => await _httpClient.GetStringAsync(url).ConfigureAwait(false)).Result;
}
catch (Exception ex)
{
if(ex is AggregateException)
{
var aggregateException = ex as AggregateException;
foreach(var innerException in aggregateException.InnerExceptions)
{
if (innerException is HttpRequestException)
{
var httpRequestException = innerException as HttpRequestException;
// how do i get StatusCode and HttpHeader values here??
}
}
}
}
return result;
}
I have already gone through SO post here and MSDN article here and also Stephen Cleary's article here
Even though its recommended to make async all the way down, I this case Client and API are both disconnected from each other and client is synchronous. Note that Client's Process method is synchronous method.
Like this:
public bool Process(string url)
{
var result = _httpClient.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
if (result.StatusCode == HttpStatusCode.BadRequest)
{
IEnumerable<string> values;
if (result.Headers.TryGetValues("x-correlationid", out values))
{
// Should print out "someid"
Console.WriteLine(values.First());
}
}
return result.IsSuccessStatusCode;
}
Also note that doing .GetAwaiter().GetResult(); vs .Result; is recommended since it makes the code easier to work with because it does not throw an AggregateException.
If you want to read the response content as a string just do:
var content = result.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
If you want to make your code async though you should use the async/await keyword and skip the .GetAwaiter().GetResult();.

Why Context.User.Identity.Name is empty from Xamarin?

I can't figure out why when I try to connect from Xamarin Context.User.Indetity.Name is empty. Is there anything special I need to do? I logged in to the server and the user has a connection stablished. After that I use the following code:
var Connection = new HubConnection(Url);
_hub = Connection.CreateHubProxy(hubName);
_hub.On(srvEvent, onData);
await Connection.Start();
But I never get the username. What am I doing wrong?
Here's the code for the server:
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
It works when it comes from the web app, not from the xamarin app.
Thanks!
Here is the code I was telling you about.
I'm using an external OAuth2 server for authentication, so I must pass the access token to SignalR somehow, because SignalR uses web sockets for the messages back and forth I can't pass the access token in the header because this is not supported by web sockets.
I'm passing that access token as a query string parameter this way (Javascript client)
$.connection.hub.qs = "access_token=" + mytoken;
Then on my SignalR I added a middleware that takes that query string and adds it to the header as an Authorization header using Bearer Token. This is done this way in my startup class
app.UseAuthQSTokenExtractor();
The code for the middleware is this one
namespace Owin
{
public static class AuthorizationQSTokenExtractorExtension
{
public static void UseAuthQSTokenExtractor(this IAppBuilder app)
{
app.Use<AuthorizationQsTokenExtractorMiddleware>();
}
}
}
namespace Chat.Middleware
{
public class AuthorizationQsTokenExtractorMiddleware : OwinMiddleware
{
public AuthorizationQsTokenExtractorMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Debug.WriteLine("signalr-auth-middleware");
string bearerToken = context.Request.Query.Get("access_token");
Debug.WriteLine("signar-bearer: " + bearerToken);
if (bearerToken != null)
{
TokenHelper.DecodeAndWrite(bearerToken);
string[] authorization = { "Bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
await Next.Invoke(context);
}
}
My startup class then looks like this
app.UseCors(CorsOptions.AllowAll);
app.UseAuthQSTokenExtractor();
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
app.RunSignalR(hubConfiguration);
You can see in the code above where I tell SignalR to use the Oauth2 Server, that code is this one
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
After all this is set up I have access to my Context.User.Identity.Name and if you want to get the others IdentityClaim you can do this
var identity = Context.User.Identity as ClaimsIdentity;
Which I'm using that code above to get the subjectId (userid) like this
public static string[] GetIdentityClaimsIssSub(HubCallerContext Context)
{
var identity = Context.User.Identity as ClaimsIdentity;
if (identity == null)
return null;
var issuerFromIdentity = identity.FindFirst("iss");
var subFromIdentity = identity.FindFirst("sub");
if (issuerFromIdentity == null || subFromIdentity == null)
return null;
return new string[] { issuerFromIdentity.Value, subFromIdentity.Value };
}
I hope it helps

Spring-MVC, Cometd : Check who is typing in chat in Comet

I am working on a Spring-MVC application in which I have implemented chat functionality using Cometd. As a feature, I would like to know if there is any way Cometd has support or some way I can show which user is typing. Ofcourse the user information I can retrieve. Here is my chat code. Thanks.
ChatServiceImpl :
#Named
#Singleton
#Service
public class ChatServiceImpl {
#Inject
private BayeuxServer bayeux;
#Session
private ServerSession serverSession;
#Listener(value = "/service/person/{id}")
public void privateChat(ServerSession remote, ServerMessage.Mutable message,#Param("id")String id) {
System.out.println("wassup");
Person sender = this.personService.getCurrentlyAuthenticatedUser();
String senderName = sender.getFirstName();
Map<String, Object> input = message.getDataAsMap();
String data = (String) input.get("name");
String timestamp = (String) input.get("timestamp");
String temp = message.getChannel();
String temp1 = temp;
temp = temp.replace("/service/person/", "");
String channelName = temp1.replace("/service","");
final int conversationId = Integer.valueOf(temp);
Replies replies = new Replies();
replies.setReplyingPersonName(senderName);
replies.setReplyText(data);
replies.setReplyTimeStamp(timestamp);
replies.setReplyingPersonId(sender.getId());
replies.setRead(false);
Long replyId = this.repliesService.addReply(replies, conversationId, sender);
Map<String, Object> output = new HashMap<String, Object>();
output.put("text", data);
output.put("firstname", senderName);
output.put("channelname", channelName);
output.put("timestamp", timestamp);
output.put("id",sender.getId());
output.put("read","true");
output.put("replyid",replyId);
ServerChannel serverChannel = bayeux.createChannelIfAbsent("/person/" + id).getReference();
serverChannel.setPersistent(true);
serverChannel.publish(serverSession, output);
}
Application.js : Please note, I am using parts of this file in other JS file.
(function($)
{
var cometd = $.cometd;
$(document).ready(function()
{
function _connectionEstablished()
{
$('#body').append('<div>CometD Connection Established</div>');
}
function _connectionBroken()
{
$('#body').append('<div>CometD Connection Broken</div>');
}
function _connectionClosed()
{
$('#body').append('<div>CometD Connection Closed</div>');
}
var _connected = false;
function _metaConnect(message)
{
if (cometd.isDisconnected())
{
_connected = false;
_connectionClosed();
return;
}
var wasConnected = _connected;
_connected = message.successful === true;
if (!wasConnected && _connected)
{
_connectionEstablished();
}
else if (wasConnected && !_connected)
{
_connectionBroken();
}
}
// Function invoked when first contacting the server and
// when the server has lost the state of this client
function _metaHandshake(handshake)
{
if (handshake.successful === true)
{
cometd.batch(function()
{
cometd.subscribe('/chat/1306', function(message)
{
var data = message.data;
$('#body').append('<div>Server Says: ' + data.firstname + '/' + data.accountid + data.time1+'</div>');
});
});
}
}
// Disconnect when the page unloads
$(window).unload(function()
{
cometd.disconnect(true);
});
$(document).on('click', '#sender', function()
{
cometd.publish('/service/chat/1306', { name: 'hello_' + Date.now() });
});
var cometURL = location.protocol + "//" + location.host + config.contextPath + "/cometd";
cometd.configure({
url: cometURL,
logLevel: 'debug'
});
cometd.websocketEnabled = false;
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.handshake();
});
})(jQuery);
Kindly let me know how I can achieve this, as I cannot find many references for this. Thanks a lot. :-)
This is easily achieved by detecting on the client side the typing start/stop (in a smart way to avoid to send too many messages to the server), then send a CometD service message to the server.
The server can then just broadcast a message to a special channel (say /chat/typing) with the nickname of the user that is typing.
The client application will subscribe to /chat/typing and receive these messages, then display in the UI who is typing, possibly coalescing multiple users into a single UI notification.
The CometD part is trivial, the detection of the start/stop of the typing in a smart way is probably most of the work.

Twitter, OAuth, Hammock, TweetSharp and Windows Phone 7

I've been trying for days to get OAuth working with Twitter in my Windows Phone app, but all the information I find is out dated or difficult to follow. I eventually got somewhere when I found this blog post http://samjarawan.blogspot.co.uk/2010/09/building-real-windows-phone-7-twitter_18.html which got me all the way to getting the access token, at which point it failed.
My code is almost identical to the one in the blog post, pretty much just changed the consumer key and consumer secret. Even their app doesn't work. It displays the Twitter login screen fine, and successfully authenticates, but in the RequestAccessToken function, it fails at this point:
if (String.IsNullOrEmpty(twitteruser.AccessToken) || String.IsNullOrEmpty(twitteruser.AccessTokenSecret))
{
Dispatcher.BeginInvoke(() => MessageBox.Show(response.Content));
return;
}
The really annoying thing is the message box only shows the Unicode replacement character (�) and nothing else. I also checked the response.StatusCode and it is OK, so there is no error as far as I can tell.
If someone could help me out with this, that would be great. I've seen other tutorials which require the user type in a PIN, but I haven't been able to get any of those to work either.
EDIT: I've just tried getting TweetSharp to work, but once again it fails to get the access token. Here is the code I'm using for TweetSharp:
public partial class TwitterAuthorisationPage : PhoneApplicationPage
{
private const string consumerKey = "myKey";
private const string consumerSecret = "mySecret"; // These are the correct values for my app
private const string requestTokenUri = "https://api.twitter.com/oauth/request_token";
private const string oAuthVersion = "1.0a";
private const string authorizeUri = "https://api.twitter.com/oauth/authorize";
private const string accessTokenUri = "https://api.twitter.com/oauth/access_token";
private const string callbackUri = "http://bing.com";
private TwitterService twitterService = new TwitterService(consumerKey, consumerSecret);
private OAuthRequestToken _requestToken = null;
public TwitterAuthorisationPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
twitterService.GetRequestToken((requestToken, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
_requestToken = requestToken;
Dispatcher.BeginInvoke(() => BrowserControl.Navigate(twitterService.GetAuthorizationUri(requestToken)));
}
else
{
Dispatcher.BeginInvoke(() => MessageBox.Show("Failed to connect to Twitter. Please try again.\n" + response.StatusDescription));
}
});
}
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
twitterService.GetAccessToken(_requestToken, PINEntry.Text, (accessToken, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
//These lines just print ?
System.Diagnostics.Debug.WriteLine(accessToken.Token);
System.Diagnostics.Debug.WriteLine(accessToken.TokenSecret);
twitterService.AuthenticateWith(accessToken.Token, accessToken.TokenSecret);
twitterService.VerifyCredentials((user, verifyResponse) =>
{
if (verifyResponse.StatusCode == HttpStatusCode.OK)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(user.Name));
}
else
{
// Fails here
Dispatcher.BeginInvoke(() => MessageBox.Show("Failed to connect to Twitter. Please try again.1\n" + verifyResponse.StatusDescription));
}
});
}
else
{
Dispatcher.BeginInvoke(() => MessageBox.Show("Failed to connect to Twitter. Please try again.0\n" + response.StatusDescription));
}
});
}
}
EDIT 2: Could it be to do with this? https://dev.twitter.com/blog/ssl-upgrade-for-twitterapi
I worked it out! It turns out Twitter was returning the access token Gzipped. Using the method described in the blog post, I had to change the second RestClient to be constructed like so:
var client = new RestClient
{
Authority = "https://api.twitter.com/oauth",
Credentials = credentials,
HasElevatedPermissions = true,
SilverlightAcceptEncodingHeader = "gzip",
DecompressionMethods = DecompressionMethods.GZip
};
And now it works!
I am having the same problem but I didn't understand your solution, could you explain a bit more where you changed the rest client?
-----EDIT----
I finally was able to make it work with TweetSharp.
I downloaded the source code and added the lines you mentioned to the rest client configuration it uses and the compiled the project again.
Since i cannot push my changes to that github, I upload the dll here. TweetSharp recompiled dll
This is the code I use which with it works
// Step 1 - Retrieve an OAuth Request Token
Service.GetRequestToken((requestToken, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
Request = requestToken;
Uri uri = Service.GetAuthorizationUri(requestToken);
Dispatcher.BeginInvoke(() =>
{
Browser.Navigate(uri);
}
);
}
});
//Step 2, get the pincode
string html = Browser.SaveToString(); //gets the DOM as a string
Regex expression = new Regex(#"<code>(?<word>\w+)</code>");
Match match = expression.Match(html);
string pin = match.Groups["word"].Value;
if (pin != "")
{
loginTwitter(pin); //we login with the pin extracted
}
//step 3, get access tokens from twitter
private void loginTwitter(string pin)
{
Service.GetAccessToken(Request, pin, processAccessToken);
}
public void processAccessToken(OAuthAccessToken access, TwitterResponse Response){
if (Response.StatusCode == HttpStatusCode.OK)
{
if (access != null)
{
Access = access; // Store it for reuse
Service.AuthenticateWith(access.Token, access.TokenSecret);
}
}
}

Resources