After postman GET request is sent, breakpoints in Visual Studio Web API controller are not hit - visual-studio-2017

Currently I'm sending a GET request to a Web API controller method and get a http 500 response, but none of the breakpoints in the controller code are being hit.
If i move the same code to another controller, the breakpoints are hit.
https://i.stack.imgur.com/dz3PM.png
[postman parameters][1]
API
[HttpGet]
public async Task<IActionResult> GetOrderParts([FromQuery]UserParams
userParams)
{
var orderParts = await _repo.GetOrderParts(userParams);
Response.AddPagination(orderParts.CurrentPage, orderParts.PageSize,
orderParts.TotalCount, orderParts.TotalPages);
return Ok(orderParts);
}

Related

SignalR returns "Connection ID required" when ran in an AWS ECS container

I am trying to get a .Net Core 3.1 SignalR Web API with an Angular front end to work with websockets.
The code works perfectly fine when ran locally, either from within in the IDE or via docker run. However, once the code gets deployed to an ECS instance in AWS behind an API Gateway the web sockets refuse to connect.
I setup my mappings like so:
app.UsePathBase("/ui");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
"default",
"{controller}/{action=Index}/{id?}")
.RequireCors(PolicyName);
endpoints.MapHub<SessionHub>("/ws/session");
endpoints.MapHub<SessionsHub>("/ws/sessions");
});
And on the client I connect to the hub like so:
this.sessionsHubConnection = new HubConnectionBuilder()
.withUrl(`${window.origin}/ws/sessions`, {
skipNegotiation: true,
transport: HttpTransportType.WebSockets,
accessTokenFactory: () => this.getAccessToken()
})
.withAutomaticReconnect()
.build();
The following Fiddler trace shows the initial HTTP request to initialize the websocket connection and the error being returned by kestrel.
I tweaked my web socket middleware for handling the access token to also Console.Write some additional debugging statements that I think might prove insightful:
public async Task Invoke(HttpContext httpContext)
{
var request = httpContext.Request;
Console.WriteLine($"Starting connection id: {httpContext.Connection.Id}");
// web sockets cannot pass headers so we must take the access token from query param and
// add it to the header before authentication middleware runs
if (request.Path.StartsWithSegments("/ws", StringComparison.OrdinalIgnoreCase)
&&
request.Query.TryGetValue("access_token", out var accessToken))
{
request.Headers.Add("Authorization", $"Bearer {accessToken}");
}
try
{
var sb = new StringBuilder();
sb.AppendLine($"Connection Id: {httpContext.Connection.Id}");
Console.WriteLine(sb.ToString());
await _next(httpContext);
sb = new StringBuilder();
sb.AppendLine($"Status code {httpContext.Response.StatusCode}"); <-- this line
sb.AppendLine($"Connection Id: {httpContext.Connection.Id}"); <-- and this line
Console.WriteLine(sb.ToString());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
throw;
}
}
And in the AWS logs you can see that the connection Id is present but is being ignored by the EndpointMiddleware(?) for some reason.
Any idea what could be causing this? Two ideas I have yet to be able to rule out are:
The HTTP/S termination at our API gateway is confusing Kestrel since the browser client is building the socket request under HTTPS, but as far as Kestrel is concerned everything is communicating over HTTP?
app.UsePathBase("/ui"); is confusing Kestrel and all the web socket paths should actually be /ui/ws/session?

IdentityServer4 + OWIN client: logout request not sending cookies to external IdP (because it is xhr)

The setup
I have an identity provider based on IdentityServer4 with AD FS as an external identity provider.
I have 3 different types of clients:
a production hybrid flow client based on OWIN lib
a test hybrid flow client (the latest sample client from the IdentityServer4 repo)
implicit flow clients (Angular)
The problem
The test client and the implicit flow clients work as expected.
When logging out from the OWIN client, the user is logged out from my IdP, but not from AD FS.
Some analysis
When logging out from the OWIN client, cookies are not sent when logging out from AD FS. In consequence, AD FS does not delete any cookies and thus the user stays logged in at the AD FS.
The ultimate reason is that the corresponding request is an xhr request which cannot send cookies (see also SAML logout request is not sending cookies to IdP, although we don't use SAML).
To summarize here are the calls you can observe in the browser:
Request GET https://myidp/connect/endsession
Query Parameters post_logout_redirect_uri=https://xxx, id_token_hint=xxx, x-client-SKU=ID_NET45, x-client-ver=5.5.0.0
Response Redirect to https://myidp/account/logout?logoutId=xxx
Request GET https://myidp/account/logout
Query Parameters logoutId=xxx
Response Redirect to https://myadfs/oauth2/logout?post_logout_redirect_uri=xxx&id_token_hint=xxx&state=xxx&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.5.0.0
Request GET https://myadfs/oauth2/logout
Request type xhr
Query parameters post_logout_redirect_uri=xxx, id_token_hint=xxx, state=xxx, x-client-SKU=ID_NETSTANDARD2_0, x-client-ver=5.5.0.0
Response Ok
For the OWIN client, the last call is of type xhr, for the other clients, it is of type document. I think something in the initial call from the client to myidp must be different such hat the redirect chain ends in a xhr request. What is this something? How can I change this behaviour?
I checked to headers of each call meticulously. Most suspect to me look the Sec-Fetch-Headers (the rest is pretty much equal for all clients), but I don't understand what they actually do.
For the OWIN client:
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
For the other clients:
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
I appreciate any hint!
The post I cited above actually was perfectly right: We have to interrupt the chain of xhr redirects and do a normal browser redirect instead.
However, we needed some time to figure out how to achieve this. Here is our solution:
The Logout action in our AuthorizationController returned Redirect, we had to replace it by a Ok response. This means replace
public class AuthorizationController : Controller
{
public ActionResult Logout()
{
Request.GetOwinContext().Authentication.SignOut("Cookies");
// ...Code for clearing cookies...
return Redirect("/myroute");
}
}
by
public class AuthorizationController : Controller
{
public ActionResult Logout()
{
Request.GetOwinContext().Authentication.SignOut("Cookies");
// ...Code for clearing cookies...
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}

API Gateway new (beta) http api

I have created a sample HTTP API (which is currently in a beta release) using the API gateway. This API does not use any authentication and has a lambda as an integration. The route accepts any HTTP method and I have confirmed that the lambda has the proper API gateway permission. This permission was added when I created the API.
However, when I call the API I receive an HTTP status of 500 and a body of: {"message":"Internal Server Error"}.
This same lambda and API will work if I set it up as a REST API rather than an HTTP API.
Any ideas why this isn't working in the HTTP API?
I had a similar issue with Internal Server Error. I was using the new API Gateway HTTP API (beta) with a nodejs lambda integration. The lambda on its own worked and the REST API worked fine, like you noted.
The 2 issues that cost me 8 hours:
I had to add an async prefix to the handler or use a callback, but I couldn't just return a response without async. See https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html
The response structure has to be correct, see this page: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html#api-gateway-proxy-integration-create-lambda-backend, scroll to the example function code and you'll see this:
// The output from a Lambda proxy integration must be
// in the following JSON object. The 'headers' property
// is for custom response headers in addition to standard
// ones. The 'body' property must be a JSON string. For
// base64-encoded payload, you must also set the 'isBase64Encoded'
// property to 'true'.
So my function ended up looking like this, note the async and the proper response structure:
exports.handler = async function(event, context) {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!')
};
return response;
}

On what domain is my Javascript in my Office Add-In

We have an Outlook Add-In which runs in OWA.
The Manifest sits on https://company.ourdomain.com
The Javascript sits on https://company.ourdomain.com
The Custom Web Service we wrote in-house sits on https://company.ourdomain.com
When I make a call from within JavaScript in response to an Add-In Command, I use the format https://company.ourdomain.com/api/Controller/Action in the ajax call.
I end up getting one of those CORS errors (sometimes it's pre-flight, other times CORB). Why am I getting this if the Javascript is literally sitting on the same domain as the web service?
I'm assuming I'm authenticated since I've logged into my Outlook account.
What gives?
NOTE:
As an experiment I attempted a RESTful call by directly typing in the URL (No OWA involved). This caused the code to Authenticate against Azure AD. Then afterward I logged into OWA in the same browser session and everything worked fine. Do I actually need to authenticate within the Javascript even if the webservice I'm calling is in the same domain?
AJAX CALL WHICH GENERATES ERROR
Remember, it will work just fine after I've made a RESTful call by making a call to my web service directly from the Browser
var apiUri = '/api/People/ShowRecord';
$.ajax({
url: apiUri,
type: 'POST',
data: JSON.stringify(serviceRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}).done(function (response) {
if (!response.isError) {
// response to successful call
}
else {
// ...
}
}).fail(function (status) {
// some other response
}).always(function () {
console.log("Completed");
});
OBSERVATION
When I call the api from the Address Bar the code below is run. This code never gets invoked by Javascript
[assembly: OwinStartup(typeof(EEWService.AuthStartup))]
namespace EEWService
{
public partial class AuthStartup
{
public void Configuration(IAppBuilder app)
{ app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = "ourdomain.com";
return Task.FromResult(0);
}
},
MetadataAddress = ConfigurationManager.AppSettings["ida:MetadataAddress"],
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new string[] { $"spn:{ConfigurationManager.AppSettings["ida:Audience"]}" }
}
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
MetadataAddress = ConfigurationManager.AppSettings["ida:MetadataAddress"],
});
}
}
}
There are a few problems with this I think.
The first one is you are trying to serve your static content off the same server you are serving your code from. This is in general considered a bad-practice, purely because no point in wasting those precious server resources for static content. Ideally you should upload your static content to a CDN - and let the users' browser make a request to some super-cached file server. However - I understand this option might not be available to you as of now. This also isn't the root cause.
The second and the real problem is, (you think you are but) you are not authenticated. Authentication in Outlook web-addins doesn't come by default, it's something you need to handle. When Outlook loads your web add-in into the side panel it makes certain methods available to you which you can use and kind-of create a pseudo-identity (as an example Office.context.mailbox.userProfile.emailAddress ) - but if you want real authentication, you will need to do that yourself.
There are three ways of doing that as far as I can tell.
The first one is through the Exchange Identity Token
Second one is through the Single Sign On feature
The third one - which I think is the most convenient and the simplest in logic to implement is using WebSockets. (SignalR might be what you need).
When the user loads your first page, make sure a JS value like window.Unique_ID available to them. This will come in handy.
Have a button in your UI - which reads "Authenticate"
When the user clicks to this button, you pop them out to a url which will redirect to your authentication URL. (Something like https://company.ourdomain.com/redirToAuth). This would save you the trouble of getting blocked in the side-panel, because you are using window.open with a url that's on your domain. Pass that Unique_ID to redirection which then redirects you to OAuth login URL. That should look like https://login.microsoftonline.com/......&state=Unique_ID
Right after popping the user to sign in window, in your main JS (which is client-side), you open a web-socket to your server, again with that Unique_ID and start listening.
When the user completes authentication, the OAuth flow should post back either an access token, or the code. If you get the access token, you can send it through the sockets to front-end (using the Unique_ID which is in the parameters of post-back) or if you had the code, you finish authenticating the user with a server-to-server call and pass the access token the same way afterwards. So you use that unique Id to track the socket that user connected from and relay access token to only that user.

getting http session cookies in spring-ws soap web service client

I have a SOAP web service client written with Spring-WS. In response to a logon request, the web service I connect to returns a session cookie in the HTTP response header with a token in it. Each subsequent call to the service requires that cookie with the token. How can I get the session cookie from the response and then add it to the HTTP header on subsequent calls to the service?
To summarize what I'm asking, how do you extract and inject cookies with Spring-WS.
Here is a snippet of code similar to what I am using to call into the web service:
MySessionLogon api = new MySessionLogon();
api.setUsername(username);
api.setPassword(password);
MySessionLogonResponse response = (MySessionLogonResponse) getWebServiceTemplate()
.marshalSendAndReceive(
"http://myserver/soapservice.asmx",
api,
new SoapActionCallback("http://www.myserver.com/MySession_Logon"));
return response;
I have searched the internet and read a lot of things that seem close to what I need, but I haven't seen a good way to get the value of the cookies I need to use. Any help would be appreciated, thanks in advance.
Did you check WebServiceMessageExtractor ?
You could do the following
MySessionLogonResponse response = (MySessionLogonResponse) getWebServiceTemplate()
.sendAndReceive(
"http://myserver/soapservice.asmx",
api,
new SoapActionCallback("http://www.myserver.com/MySession_Logon"),
new WebServiceMessageExtractor<ResponseAndHeader>() {
public ResponseAndHeader extractData(WebServiceMessage message) throws IOException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection con = (HttpComponentsConnection) context.getConnection();
httpResponse httpResponse = con.getHttpResponse();
//use the method getHeaders from to retrieve the data you want
});