I'm having an issue with sending a POST request for Pinterest in a UWP app I'm working on. I already have the access code from a previous WebAuthenticationBroker function. I've tried using the WebAuthenticationBroker with the UseHttpPost option under options with authenticating async, but, as I've provided my if functions, it returns ERROR. I just get a "message": "405: Method Not Allowed" and "type" : "http". I've looked all over, I've even tried using an HttpClient and PostAsync(), but I couldn't get it to get the access token. Any advise to what I'm doing wrong?
private void OutputToken(string TokenUri)
{
int tokenString = TokenUri.IndexOf('=');
string TheTokenUri = TokenUri.Substring(tokenString + 54);
PinterestReturnedTokenText.Text = TheTokenUri;
outputToken = TheTokenUri;
}
private async void auth()
{
try
{
string CallbackUrl = "https://localhost/";
string PinterestUrl = "https://api.pinterest.com/v1/oauth/token?grant_type=authorization_code&client_id=" + PinterestClientID + "&client_secret=" + PinterestClientSecret + "&code=" + outputCode;
Uri StartUri = new Uri(PinterestUrl);
Uri EndUri = new Uri(CallbackUrl);
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseHttpPost, StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
OutputToken(WebAuthenticationResult.ResponseData.ToString());
await GetPinterestNameAsync(WebAuthenticationResult.ResponseData.ToString());
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
OutputToken("HTTP Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseErrorDetail.ToString());
}
else
{
OutputToken("Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseStatus.ToString());
}
}
catch (Exception Error)
{
PinterestReturnedTokenText.Text = "ERROR";
}
}
Related
I need to log exceptions and bad requests in my API. Currently I am using try catch to catch the exception and add to my logs in the catch block. Is this the right way? I read about Global Error Handling in ASP.NET. How can I implement that approach for this case?
Below is my API Controller example:
[HttpPost]
[Authorize]
[ValidateModel]
[Route("CheckProgramOwner")]
public async Task<IHttpActionResult> CheckProgramOwner([FromBody] CheckProgramOwner _data)
{
try
{
using (var db = new VisualVoiceFlowEntities())
{
var Result= await db.VVF_ScriptFlow.Where(s => s.ProgramId == _data.ProgramId).OrderByDescending(s => s.ID).FirstOrDefaultAsync();
if(Result== null)
{
Log.Error("Error in CheckProgramOwner POST Request - " + "ProgramId not found");
return Content(HttpStatusCode.BadRequest, "ProgramId not found");
}
string CurrentOwner = Result.ReadBy.ToString();
return Ok(CurrentOwner);
}
}
catch (Exception ex)
{
Log.Error("Error in CheckProgramOwner POST Request - " + ex.Message, ex);
NewRelic.Api.Agent.NewRelic.NoticeError("Error in CheckProgramOwner POST Request - " + ex.Message, null);
return Content(HttpStatusCode.InternalServerError, "Internal Server Error. Please Contact Admin.");
}
}
If you read the document previously posted by Casey, you will find a link to the following document, which explains how to implement and register an exception filter globally:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling#registering_exception_filters
You could then implement your logging logic in the filter's body thus avoiding having to repetitively log errors on each try/catch. I would suggest logging the more obvious errors using your original approach and use the filter to log any other errors (that you might not expect.)
I did it using ExceptionFilter.
I created Exception Filter Class as below -
public class MyExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
log4net.ThreadContext.Properties["addr"] = HttpContext.Current.Request.UserHostAddress;
log4net.ThreadContext.Properties["Hostname"] = Dns.GetHostName().ToString();
log4net.ThreadContext.Properties["PCName"] = Dns.GetHostAddresses(Environment.MachineName)[0].ToString();
string RequestMethod = context.Request.Method.Method;
dynamic ControllerInfo = context.ActionContext.ControllerContext.Controller;
string RequestName = ControllerInfo.Url.Request.RequestUri.LocalPath.ToString().Replace("/api/", "").Replace("/VVFAPI", "");
Log.Error("Error in " + RequestName +" "+ RequestMethod+ " Request - " + context.Exception.Message, context.Exception);
NewRelic.Api.Agent.NewRelic.NoticeError("Error in " + RequestName + " " + RequestMethod + " Request - " + context.Exception.Message, null);
HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("Internal Server Error. Please Contact Admin."),
ReasonPhrase = "Critical Exception."
};
context.Response = msg;
}
}
Also, I changed my controller accordingly
[HttpPost]
[Authorize]
[ValidateModel]
[MyExceptionFilter]
[Route("CheckProgramOwner")]
public async Task<IHttpActionResult> CheckProgramOwner([FromBody] CheckProgramOwner _data)
{
Log.Info("CheckProgramOwner POST Request");
using (var db = new VisualVoiceFlowEntities())
{
var Result = await db.VVF_ScriptFlow.Where(s => s.ProgramId == _data.ProgramId).OrderByDescending(s => s.ID).FirstOrDefaultAsync();
if (Result == null)
{
Log.Error("Error in CheckProgramOwner POST Request - " + "ProgramId not found");
return Content(HttpStatusCode.BadRequest, "ProgramId not found");
}
string CurrentOwner = Result.ReadBy.ToString();
return Ok(CurrentOwner);
}
}
I have a plugin using HttpClient which is supposed to create a record in external ERP database. I am sending a request, everything on my side is ok, I receive a status OK but response.Content.ReadAsStringAsync() is empty. In ERP database record is not created. Code below
public class OnPostCreate : BaseClass
{
private static Uri baseAdress = new Uri("http://adress/ws/Methodname.json?raw=");
private static HttpClient client = new HttpClient();
public static string trace = "";
public override void ExecutePlugin()
{
Account account = ((Entity)context.InputParameters["Target"]).ToEntity<Account>();
Account getAcc = dataContext.AccountSet.Where(p => p.Id == account.Id)
.Select(p => new Account()
{
Id = p.Id,
jar_id = p.jar_id,
jar_field1= p.jar_field1,
jar_field2= p.jar_field2,
jar_field3= p.jar_field3,
jar_field4= p.jar_field4
})
.SingleOrDefault();
if (getAcc == null) return;
ModelClass model = CreateModel(getAcc);
string content = GetContent(model);
Account acc = new Account
{
Description = baseAdress + content,
Id = getAcc.Id
};
service.Update(acc);
RunAsync(content, tracingService).GetAwaiter().GetResult();
tracingService.Trace(trace);
}
private static async Task RunAsync(string content, ITracingService tracingService)
{
client.BaseAddress = baseAdress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
try
{
await SendRequest(HttpMethod.Post, content);
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message + e.InnerException + e.InnerException.InnerException);
}
}
private static async Task SendRequest(HttpMethod method, string query)
{
HttpRequestMessage request = new HttpRequestMessage(method, query);
HttpResponseMessage response = await client.SendAsync(request);
string responsetext = await response.Content.ReadAsStringAsync();
trace += "Code:" + response.StatusCode.ToString();
trace += "/n Response: " + responsetext;
response.EnsureSuccessStatusCode();
}
private ModelClass CreateModel(Account account) => new ModelClass()
{
Id = account.jar_id,
field4= account.jar_field4,
field1 = Convert.ToInt32(account.jar_field1 ),
field2= Convert.ToInt32(account.jar_field2),
field3= account.jar_field3
};
private string GetContent(ClassModel model) => $"<RequestData>" +
$"<Id>{model.Id}</Id>" +
$"<field1>{model.field1}</field1>" +
$"<field2>{model.field2}</field2>" +
$"<field3>{model.field3}</field3>" +
$"<field4>{model.field4}</field4>" +
$"</RequestData>";
}
Please help! Any suggestions why on my side everything seems to be OK (but response is empty??) and on the other side there is no even a mark of request.
I pasted the request url to postman and at first attempt i got no response, second one: 503 error Object reference not set to an instance of an object at DataService.Modules.Json.JsonHandler.Process(HttpListenerContext context) at DataService.Common.HttpServer.BeginGetContextCompleted(IAsyncResult result)
Regards
I have been using Azure Notification Hubs along with GCM to send notifications to the users of my app. This was all working great until I published the app to the Play Store.
Now it gives a 500 Server error whenever I try to post a notification. I have no idea why this error is happening. Perhaps the app is not picking up the RavenDB where the notifications are stored?
But it looks more like the service is not getting back users that are registered on the Hub. I really don't know... Any help would be so appreciated!
This is my stacktrace when run locally, it is the same but less detailed when published:
"Message": "An error has occurred.",
"ExceptionMessage": "Value cannot be null.\r\nParameter name: source",
"ExceptionType": "System.ArgumentNullException",
"StackTrace": " at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)\r\n
at AcademicAssistantService.Controllers.NotificationController.<GetRecipientNamesFromNotificationHub>d__8.MoveNext()
in C:\\Users\\Kenneth\\Documents\\College\\Semester 8\\AcademicAssistantService\\AcademicAssistantService\\Controllers\\NotificationController.cs:line 105
This is the Controller action:
// POST api/notification
public async Task<IHttpActionResult> Post([FromBody]Notification notification, String key)
{
var notificationToSave = new Notification
{
NotificationGuid = Guid.NewGuid().ToString(),
TimeStamp = DateTime.UtcNow,
Message = notification.Message,
SenderName = notification.SenderName
};
var recipientNames = await GetRecipientNamesFromNotificationHub(key);
var recipientNamesString = CreateCustomRecipientNamesString(recipientNames);
string notificationJsonPayload =
"{\"data\" : " +
" {" +
" \"message\": \"" + notificationToSave.Message + "\"," +
" \"senderName\": \"" + notificationToSave.SenderName + "\"," +
" \"recipientNames\": \"" + recipientNamesString + "\"" +
" }" +
"}";
if (key == null)
{
var result = await _hubClient.SendGcmNativeNotificationAsync(notificationJsonPayload);
notificationToSave.TrackingId = result.TrackingId;
notificationToSave.Recipients = recipientNames;
}
else
{
foreach (string r in recipientNames)
{
if ((r != notification.SenderName))
{
var result = await _hubClient.SendGcmNativeNotificationAsync(notificationJsonPayload, "user:" + r);
notificationToSave.TrackingId = result.TrackingId;
notificationToSave.Recipients = recipientNames;
}
}
}
await Session.StoreAsync(notificationToSave);
return Ok(notificationToSave);
}
To get names from hub:
public async Task<List<string>> GetRecipientNamesFromNotificationHub(String key)
{
var registrationDescriptions = await _hubClient.GetAllRegistrationsAsync(Int32.MaxValue);
var recipientNames = new List<String>();
foreach (var registration in registrationDescriptions)
{
if (registration is GcmRegistrationDescription)
{
var userName = registration.Tags
.Where(t => t.StartsWith("user"))
.Select(t => t.Split(':')[1].Replace("_", " "))
.FirstOrDefault();
userName = userName ?? "Unknown User";
Conversation convo = db.Conversations.Find(key);
foreach (User u in convo.Users)
{
if (u.Email == userName && !recipientNames.Contains(userName))
{
recipientNames.Add(userName);
}
}
}
}
return recipientNames;
}
Could you use Service Bus Explorer and verify indeed you have tags starts with "user". And I also see you are using GetAllRegistrationsAsync API, which is recommend to use only for debugging purpose. This is heavily throttled API.
Thanks,
Sateesh
My website is using facebook as it's oauth provider. Users will be able to buy things through my site so I want to force them to authenticate even if they already have an active session with facebook.
I found this link in facebook's api documentation that discusses reauthentication but I can't get it to work with my mvc app. Anyone know if this is possible?
var extra = new Dictionary<string, object>();
extra.Add("auth_type", "reauthenticate");
OAuthWebSecurity.RegisterFacebookClient(
appId: "**********",
appSecret: "**********************",
displayName: "",
extraData: extra);
Found the solution. I had to create my own client instead of using the default one provided by OAuthWebSecurity.RegisterFacebookClient
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
namespace Namespace.Helpers
{
public class MyFacebookClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
{
private const string AuthorizationEP = "https://www.facebook.com/dialog/oauth";
private const string TokenEP = "https://graph.facebook.com/oauth/access_token";
private readonly string _appId;
private readonly string _appSecret;
public MyFacebookClient(string appId, string appSecret)
: base("facebook")
{
this._appId = appId;
this._appSecret = appSecret;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
return new Uri(
AuthorizationEP
+ "?client_id=" + this._appId
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&scope=email,user_about_me"
+ "&display=page"
+ "&auth_type=reauthenticate"
);
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
WebClient client = new WebClient();
string content = client.DownloadString(
"https://graph.facebook.com/me?access_token=" + accessToken
);
dynamic data = Json.Decode(content);
return new Dictionary<string, string> {
{
"id",
data.id
},
{
"name",
data.name
},
{
"photo",
"https://graph.facebook.com/" + data.id + "/picture"
},
{
"email",
data.email
}
};
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(content);
if (nameValueCollection != null)
{
string result = nameValueCollection["access_token"];
return result;
}
return null;
}
}
}
and then in AuthConfig.cs...
OAuthWebSecurity.RegisterClient(
new MyFacebookClient(
appId: "xxxxxxxxxx",
appSecret: "xxxxxxxxxxxxxxxx"),
"facebook", null
);
As a note to those that happen upon this if your Facebook Authentication stopped working when v2.3 became the lowest version you have access to (non versioned calls get the lowest version an app has access to). The API now returns JSON and not name value pairs so you have to update the QueryAccessToken method shown above by #Ben Tidman
Below is the updated method
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
dynamic json = System.Web.Helpers.Json.Decode(content);
if (json != null)
{
string result = json.access_token;
return result;
}
return null;
}
There's one issue in using the MyFacebookClient implementation.
Probably someone tryin to implement it came across the error:
The given key was not present in the dictionary
attempting to call the ExternalLoginCallback method in ActionController.
The error raises when the method
OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
is called.
In order to get it working the method VerifyAuthentication has to be overridden.
In particular the
public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl);
overload of the abstract class OAuth2Client.
If you use the following:
public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.OriginalString;
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(QueryAccessToken(returnPageUrl, code));
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
AuthenticationResult result = new AuthenticationResult(true, ProviderName, userData["id"], userData["name"], userData);
userData.Remove("id");
userData.Remove("name");
return result;
}
}
finally you get the method called in the right way and no excpetion is thrown.
I am writing an Metrol Style App to update status on my Twitter. I use LINQ to Twitter library. But I don't understand why my app throws exception 401 Unauthorized. Here is my code:
private void UpdateStatus()
{
// configure the OAuth object
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = "ConsumerKey",
ConsumerSecret = "ConsumerSecret",
OAuthToken = "TwitterAccessToken",
AccessToken = "TwitterAccessTokenSecret"
}
};
using (var twitterCtx = new TwitterContext(auth, "https://api.twitter.com/1/", "https://search.twitter.com/"))
{
var tweet = twitterCtx.UpdateStatus("Hi everybody!"); // error here
viewTextBlock.Text = String.Empty;
viewTextBlock.Text = viewTextBlock.Text + "Status returned: " +
"(" + tweet.StatusID + ")" +
tweet.User.Name + ", " +
tweet.Text + "\n";
}
}
I just posted a blog entry on using OAuth in Windows 8 with LINQ to Twitter:
http://geekswithblogs.net/WinAZ/archive/2012/07/02/using-linq-to-twitter-oauth-with-windows-8.aspx
I also included a 401 FAQ in the LINQ to Twitter docs here:
http://linqtotwitter.codeplex.com/wikipage?title=LINQ%20to%20Twitter%20FAQ&referringTitle=Documentation
You can implement it using the Twitterizer assembly.
Firstly you can create a token which can be used to access Twitter and then using that particular token you can update TwitterStatus (Twitterizer.Core.TwitterObject.TwitterStatus).
Sample code is as follows.
public void CreateCachedAccessToken(string requestToken)
{
string ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
OAuthTokenResponse responseToken = OAuthUtility.GetAccessToken(ConsumerKey, ConsumerSecret, requestToken);
//Cache the UserId
Session["GetCachedUserId"] = responseToken.UserId;
OAuthTokens accessToken = new OAuthTokens();
accessToken.AccessToken = responseToken.Token;
accessToken.AccessTokenSecret = responseToken.TokenSecret;
accessToken.ConsumerKey = ConsumerKey;
accessToken.ConsumerSecret = ConsumerSecret;
Session["AccessToken"] = accessToken;
}
To update the TwitterStatus you can do as follows.
public OAuthTokens GetCachedAccessToken()
{
if (Session["AccessToken"] != null)
{
return (OAuthTokens)(Session["AccessToken"]);
}
else
{
return null;
}
}
TwitterStatus.Update(GetCachedAccessToken(), txtTweet.Trim());
The below mentioned method can be used to implement sign in.
protected string GetTwitterAuthorizationUrl()
{
string ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
OAuthTokenResponse reqToken = OAuthUtility.GetRequestToken(ConsumerKey, ConsumerSecret);
return "https://twitter.com/oauth/authorize?oauth_token=" + reqToken.Token;
}
Hope this helps.
If there are any clarifications please raise. Thanks