PROBLEM:2nd call to the graph api fails every time with Bad Request 400 error
If I do the following things, I can never get past #4.
Authenticate with facebook to get authtoken. It redirects back to the page with code querystring param
I get the authtoken from param and make a call to the following url
string url = "https://graph.facebook.com/me?access_token=" + Token; (any graph api call works fine on the first call)
I get json data back. No problem. Now I have the id info from facebook.
I try to repeat the process. Every call to https://graph.facebook.com/me/xxxxxx fails. If I try getting a new token or using the initial token I get Bad Request 400 error.
There must be an order of operations that needs to occur (requests and getting tokens) that I don't understand.
(IT IS NOT AN apikey or apisecret PROBLEM)
What you describe should work. Be sure that when you get the 400-Bad Request error that you catch the WebException and read the content of the Response. It should provide you with the reason the API call failed. The catch portion of your try/catch block would look something like this:
catch (WebException ex)
{
using (StreamReader reader = new StreamReader(ex.Response.GetResponseStream()))
{
string jsonMessageString = reader.ReadToEnd();
}
}
Try this API, it's new and supported. (ie. I support it) See if it's authentication resolves your issue. If you're like me then this would have saved you hours of time fiddling with it. Well worth the 50 bucks.
Related
So I have an application that's calling the graph API.
See the following snippet:
When the try (AquireTokenSilent) is called, the Web Request completes successfully no problem.
However, when make the same web request with the token I get from AcquireTokenAsync, I'm getting a 404 error and an exception thrown.
1) Can you recommend some good tools to analyze the HTTP request (so I can compare the difference and identify a problem). The Visual Studio debugger is helpful, but I can't see the whole picture, which obfuscates a possible problem here.
2) Can you help me identify why exactly one is successful and one fails? Both tokens seem to be acquired successfully, so I'm not sure what the problem is.
So I have figured out what was the underlying cause here.
In our authentication scenario, we have multiple products using azure AD SSO in the ecosystem. Since 'OnAuthorizationCodeReceived' is only called on login, and NOT when a valid login cookie is already held, the token cache will not be populated with the authorization code. So in this case, the Microsoft code samples for this scenario are dead wrong. Issuing an authentication challenge wont cause 'OnAuthorizationCodeReceived' to be called, as you already hold a valid login token.
So, while it's a litte ugly, the fix is dead simple. Force a logout, so that the token cache can be populated.
catch (AdalSilentTokenAcquisitionException e)
{
//in this case, it's possible there's no authorization code because the login cookie is from another session in
//the ecosystem. So in this scenario, force a logout so we can get a token into the tokencache
context.GetOwinContext().Authentication.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
sessionState.Abandon();
}
Now, because we're using this code outside the Controllers, and we call await, HttpContext will be null. Some serious voodoo going on in HttpContext, but I digress. We can use this little workaround to hang onto the context:
var context = HttpContext.Current;
var sessionState = context.Session;
EDIT: Came across one more problem when deploying the application to an azure app-service. You want to make sure Azure AD Authentication is toggle on in the 'Authentication' panel on Azure. We had some infinite login loop problems until I toggled this.
EDIT:
So, forcing a logout in this scenario really didn't sit well with me. BUT, I came across this question:
Azure Active Directory Graph API - access token for signed in user
And what we can do is follow the answer, and call AcquireTokenByAuthorizationCodeAsync(...), and make sure to use the 4 parameter method overload, where the last param is "https://graph.windows.net/"
Now, as long as we store the authorization code somewhere (in my case stored in a DB table). We should be able to get the authorization code for a given user, and get a new GraphAPI token, in the case where AcquireTokenSilentAsync(...) fails.
Now your statefull tokencache can be backed up by a stateless database call!
catch (AdalSilentTokenAcquisitionException e)
{
//in this case, the stateful cache is empty, so lets get the codeId from the DB
PersistentTokenCache pt = db.PersistentTokenCaches.Find(userObjectId);
if (pt != null && pt.token != null)
{
try
{
result = await ath.AcquireTokenByAuthorizationCodeAsync(pt.token,
new Uri(Startup.hostUri),
cc,
"https://graph.windows.net");
}
catch (AdalException ex)
{
Debug.WriteLine(ex.StackTrace);
//both authentication types have failed
pt.token = null;
await db.SaveChangesAsync();
context.GetOwinContext().Authentication.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
sessionState.Abandon();
return -1;
}
}
}
I am using an MVC application that will send an email to JIRA with the information required to create an Issue in JIRA.
This is all works successfully but the next step is to retrieve the information in a page. Currently I am displaying the information that was sent by retrieving it from the database.
The problem is that I also need to retrieve the KEY and the status of the issue. This cannot be just entered as the user will not know what they are, it has to be done in JIRA.
Originally I was going to use an API to get the information from JIRA but because the JIRA site is not hosted online the API does not meet the Access-Control-Allow-Headers" Header.
I was told that I would have to use webhooks to get the information but I am unsure about how to go about this.
I know that I have to first register the webhook which I am doing Via the JIRA Administration UI. What I what to know is how can I retrieve that information in my application using webhooks, I know the webhooks must have a friendly name for the webhook created, the URL where the callback should be sent.
The scope of the webhook and the events to post to the URL, either "all events" or a specific set of events.
I also know that the Post function web hooks will not fire if added to the Create Issue workflow transition. We recommend that you configure your web hook to fire from the issue_created event instead.
So how can I successfully retrieve this information, I am currently trying this:
Public Function Webhook() As ActionResult
Dim status As String = "Status"
If Request("secret") <> status Then
Response.StatusCode = 403
Return Content("Invalid status secret")
End If
If Request("event") = "incoming_messages" Then
Dim Key As String = Request("Key")
Dim jiraStatus As String = Request("status")
Dim reply As Dictionary(Of String, Object) = New Dictionary(Of String, Object)()
reply("content") = "Thanks for your submission!"
Dim result As Dictionary(Of String, Object) = New Dictionary(Of String, Object)()
result("messages") = New Object() {reply}
Return Json(result)
Else
Response.StatusCode = 400
Return Content("Unknown event")
End If
Return View()
End Function
But I am pretty sure I am doing it wrong, what steps do I need to follow to do this correctly?
Update
Where should my Webhook URL fire to currently I am sending it to RequestB.in for testing which is working but where should I fire it to get the information in my MVC application? Should it fire to the MVC application, if so where should it fire to.
How can I process the Json payload currently in my MVC application, I am trying to deserialize it but I have not done this before and am unsure how to connect the Json Payload with the application. This is what I have tried so far:
Request.InputStream.Position = 0
Request.InputStream.Seek(0, SeekOrigin.Begin)
Using reader = New StreamReader(Request.InputStream)
Dim jiraJson = reader.ReadToEnd()
Dim contentType As String = Request.ContentType
Dim body = JsonConvert.DeserializeObject(jiraJson)
Try
Select Case DirectCast(body.key, String)
Case ""
Return Json(jiraJson)
Case Else
Return Json(jiraJson)
End Select
Catch ex As Exception
End Try
End Using
Return View()
I am unsure what to place in switch statement and also how to return the json and then display it, how can I do this?
If I'm understanding you correctly, a high level process of how this should work would be :
An email is sent to Jira.
An issue is created under the given project, which triggers the webhook.
The webhook will POST a JSON payload to the URL you've specified.
The URL should be a public route within your MVC application that consumes and processes the request.
So given where you're already at, all you need to consume the webhook is a publicly accessible MVC route. Let's say you have a controller named "JiraUpdateController", and on that controller exists a method called "ProcessWebhook" :
public class JiraUpdateController : Controller
{
/// <summary>
/// Set our default base logger for the update tasks
/// </summary>
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public static Logger Log
{
get { return logger; }
}
[HttpPost]
public ActionResult ProcessWebhook()
{
// Process the JSON payload here accordingly.
}
}
Now, you need to set this as the URL that the webhook should post to. Ultimately it should look something like :
http://internal.company.com/JiraUpdate/ProcessWebhook
I've done something very similar to this, so here's a couple things to keep in mind.
Make sure you're setting the webhook to a minimal scope. If this is something you want to trigger across all projects whenever an issue is created in any of them, you're going to have quite a few requests going to the ProcessWebhook endpoint. Try to limit the projects to just the ones you need, and the event to only the "issue created" event.
If you don't already have a public endpoint for testing, you should definitely check out RequestBin. What this will allow you to do, is setup a temporary URL for the webhook, so you can see exactly what the JSON payload from JIRA will look like. From there, build your business logic in the ProcessWebhook method, and you should be good to go. To use RequestBin, just go to the site, generate a URL, and set that as the Webhook URL. Create an issue in JIRA under the project you're working with, and you should see a large JSON payload go to that RequestBin URL - this will give you a feel for exactly what the JSON looks like and how to serialize it into a workable construct.
I'm attempting to build a service in ServiceStack whose sole responsibility will be to interpret requests, and send a redirect response. Something like this:
[Route("/redirect/", "POST")
public class Redirect : IReturnVoid
{
public string Something { get; set; }
}
public class RedirectService : Service
{
public object Post(Redirect req)
{
// make some decisions about stuff
return new HttpResult(){ StatusCode = HttpStatusCode.Redirect, Headers = {{HttpHeaders.Location, "place"}}};
}
}
I did initial testing using fiddler, setting a content-type of application/json and creating an appropriate request body.This did exactly as expected: the service request gave a 302 response and redirected to the expected location.
I've also tested this by using a basic Html form post, with an action of http://myserviceuri/redirect/, which also works as expected and redirects appropriately.
However, i've hit an issue when attempting to use the SS c# client to call the same service. If I call the following code in an aspx code behind or an mvc controller
var client = new JsonServiceClient("uri);
client.post(new Redirect{Something = "something});
I get a 500 and the error message:
The remote certificate is invalid according to the validation procedure.
Which makes sense as it's a development server, with a self-cert. But I get the feeling that, as I can call the service successfully by other means, that this is a red herring.
Should I be using a different type of c# client to make the request, or setting any more custom headers, or something else? Am I fundamentally not understanding what i'm trying to do?
Please let me know if more info is needed. Thanks.
What's happening here is that the JsonServiceClient is happily following the redirect, doing more than what you've expected it to do.
I'll reference a related question and answer for posterity ( - hopefully you've resolved this issue a long time ago...).
POST to ServiceStack Service and retrieve Location Header
Essentially you'd use .net's WebRequest or the ServiceStack extensions mentioned in the answer to see the redirect and act as you see fit.
I have code that uses an HttpWebRequest to request a .ASP page (asp 3.0), with some parameters in the URL. That ASP page will generate a PDF by getting some data from a database. Due to error in the ASP code, I'm getting a WebException with IIS's 500 server error when I try to get the response from the request. And that's fine, but the exception's message doesn't really say anything other than 500 error occurred.
If I copy paste the URL that I'm requesting in IE, I do get some details since I have friendly errors disable and IIS configured to send the real error text to the user.
"Technical Information (for support personnel)
Error Type:
ADODB.Recordset (0x800A0CC1)
Item cannot be found in the collection corresponding to the requested name or ordinal.
"
I've done a QuickWatch on the Exception and visited all of the properties, but not one contained this data.
Is there a way to get this information on the WebException?
Thanks.
Is there a way to get this information on the WebException?
Yes, you can read the response body:
try
{
...
}
catch (WebException ex)
{
using (var stream = ex.Response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
string text = reader.ReadToEnd();
// text will contain the response from the server
}
}
I am trying to make a Test Webservice and throw a SoapException. But when I open the service through browser, it shows Internal server error - 500.
If i try to consume it manually by sending a manually created SoapRequest (in stringbuilder), I get the same error "Servererror - 500" in Visual Studio itself in the line "WebResponse response = req.GetResponse()"
Is there anyway I can actually see the "fault in response XML".
It sounds like there is something wrong with the service and you need to debug it on the server side rather than the client side. I come to this conclusion because you have a problem with your client code and a web browser.
Assuming you are using .NET have you enabled display of ASP.NET errors on the server? See this article for info.
Update:
So you are throwing an error on the server and want to get the error text on the client? An error on the server is supposed to result in a 500 error message and is unlikely to return any XML to the client. Perhaps you can pass something to the SoapException constructor?
Have you looked at the docs for SoapException? They have some examples of passing information using the Detail property of SoapException.
Can you get to the asmx (assuming you are using asmx) in the browser to see if it is working at all?
after surfing through for 5-6 hours finally got it......here it is:
When you are getting the response manually, use the following:
try
{
WebResponse response = req.GetResponse();
Stream str = response.GetResponseStream();
StreamReader rdr = new StreamReader(str);
Response.Write(rdr.ReadToEnd());
Response.End();
}
catch (WebException webEx)
{
Stream str = webEx.Response.GetResponseStream();
StreamReader rdr = new StreamReader(str);
Response.Write(rdr.ReadToEnd());
Response.End();
}