FHIR CLIENT - Connect to NPHIES FHIR - OBB Server - hl7-fhir

How to connect NPHIES FHIR OBB(Testing) Server inorder to submit eligibility/preauthorization/claims and soon.
Lot of companies built tools to work with FHIR SERVERS.
Firely - .Net Framework / .Net Core - link
HAPI- JAVA - link

Those who were working with Firely - .NET SDK, click this link to view the documentation.
In order to send the request to NPHIES OBB SERVER, you need to pass Username, password and content-type
username: xxxxx
password: yyyyy
content-type: application/fhir+json
Check the below code to send a request to NPHIES - OBB FHIR Server
using (var handler = new HttpClientEventHandler())
{
handler.OnBeforeRequest += (sender, e) =>
{
e.RawRequest.Content.Headers.ContentType.CharSet = "";
e.RawRequest.Content.Headers.Remove("Content-Type"); // "{application/json; charset=utf-8}"
e.RawRequest.Content.Headers.Add("Content-Type", "application/fhir+json");
e.RawRequest.Content.Headers.Add("Username", xxxx);
e.RawRequest.Content.Headers.Add("Password", yyyy);
};
handler.OnAfterResponse += (sender, e) =>
{
};
using (FhirClient fhirClient = new FhirClient(
nphiesServerURL, new FhirClientSettings() { PreferredFormat = ResourceFormat.Json},handler))
{
Bundle responseBundle = fhirClient.Transaction(bundle);
}
}

Related

MsalClientException IDW10104 from GetAccessTokenForAppAsync

I have an ASP.NET Core Web API set up as App Service in Azure with an App Registration in our AzureAd
In appsettings.json I have (anonimized)
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "ourdomain.co.uk",
"TenantId": "n9n999n9-9999-nnnn-9n9n9-9n9n9n9n9n9",
"ClientId": "81933a15-157f-45b0-bc32-3d7d6d62f4a7",
"Audience": "https://ourdomain.co.uk/breathe.notifications-service",
"ClientSecret": "a6a6a6a~EEizqWNa8itAAAjcrycxnCtxaVgKTFx"
},
That app has an API permission in Azure Ad that allows me to call another app service, Audit. The audit service does not have any specific scopes defined but it does have an app role called Audit.Write
In the calling API i need to get a token to call audit so I run this code
var accessToken = await this.tokenAcquisition.GetAccessTokenForAppAsync(this.auditApiScope);
this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
this.httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Note the call to GetAccessTokenForAppAsync rather than the more common GetAccessTokenForUserAsync
The scope string that I am passing is
https://ourdomain.co.uk/us.audit-service/.default
When I call GetAccessTokenForAppAsync it is failing with MSALException
IDW10104: Both client secret and client certificate cannot be null or
whitespace, and only ONE must be included in the configuration of the
web app when calling a web API. For instance, in the appsettings.json
file.
The client secret is in the AzureAd config, I am not specifying a certificate.
I now have this working and have two options but before I outline those I need to offer some extra background.
This Web Api and others we have created offer functionality to Azure Ad users and Azure B2C users. This functionality was first possible with Microsoft.Identity.Web 1.11.0 and we hjave been using 1.11.0 since it was released. However we always had an issue where we would generate thousands of exceptions because MSAL was getting confused ny which scheme to use.
We came across this blog post, Removing misleading IDX10501 logs when using multiple authentication schemes in ASP.NET Core 3.1 there is more detail in this github thread, https://github.com/oliviervaillancourt/blog/issues/3.
Our Startup.cs Configure Services looks like this
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(this.configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.AddAuthentication()
.AddMicrosoftIdentityWebApi(this.configuration, "AzureAdB2C", "B2CScheme", true);
services.AddAuthentication("AzureAD_OR_AzureAdB2C")
.AddMicrosoftIdentityWebApi(
jwtBearerOptions =>
{
var azureAdB2CConfig = this.configuration.GetSection("AzureAdB2C");
jwtBearerOptions.ForwardDefaultSelector = context =>
{
var token = string.Empty;
if (context.Request.Headers.TryGetValue("Authorization", out var value))
{
string authorization = value;
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("Bearer ".Length).Trim();
}
}
if (token == null)
{
this.logger.LogInformation($"Cannot get the Token out of the Authorization header");
}
var jwtHandler = new JwtSecurityTokenHandler();
if (jwtHandler.CanReadToken(token))
{
var jwtToken = jwtHandler.ReadJwtToken(token);
var expectedB2CIssuer = $"{azureAdB2CConfig.GetValue<string>("Instance")}/{azureAdB2CConfig.GetValue<string>("TenantId")}/v2.0/";
if (string.Compare(jwtToken.Issuer, expectedB2CIssuer, true) == 0)
{
// Claim is from B2C so this request should be validated against the B2C scheme.
this.logger.LogInformation($"Request is with a B2C issued token so refer to B2CScheme. Token issuer: {jwtToken.Issuer} B2C Issuer: {expectedB2CIssuer}");
return "B2CScheme";
}
else
{
this.logger.LogInformation($"Request is not with a B2C issued token so refer to Bearer scheme. Token issuer: {jwtToken.Issuer} B2C Issuer: {expectedB2CIssuer}");
}
}
else
{
this.logger.LogInformation("Request token could not be read so refer to Bearer scheme");
}
return "Bearer";
};
},
identityOptions =>
{
var azureAdB2CConfig = this.configuration.GetSection("AzureAdB2C");
identityOptions.Instance = azureAdB2CConfig.GetValue<string>("Instance");
identityOptions.TenantId = "AzureAD_OR_AzureAdB2C";
identityOptions.ClientId = "AzureAD_OR_AzureAdB2C";
},
"AzureAD_OR_AzureAdB2C",
false);
services.AddControllers()
.AddNewtonsoftJson();
services.AddLogging(options =>
{
// hook the Console Log Provider
options.AddConsole();
options.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
// hook the Application Insights Provider
options.AddFilter<ApplicationInsightsLoggerProvider>(string.Empty, Microsoft.Extensions.Logging.LogLevel.Trace);
// pass the InstrumentationKey provided under the appsettings
options.AddApplicationInsights(this.configuration["APPINSIGHTS_INSTRUMENTATIONKEY"]);
});
}
The logic used by the ForwardDefaultSelector is what helps us work with multiple schemes and forward ASP.NET to the right scheme.
Now back to the answer.
If I remove the ForwardDefaultSelector I no longer get the IDW10104 however that is what we use to remopve all the extraneous exceptions schemes so that is not really going to be workable.
The only viable option is to move the Web Api from the latest version of Microsoft.Identity.Web 1.21.1 to 1.16.0. The issue that is causing us to get the exception was introduced in 1.16.1. I will raise an issue on the MSAL github for 1.16.1. We were previously using 1.11.0.

App running in Android Emulator fails to perform an HTTP Post to localhost

I'm unable to perform an HTTP Post with an app running in an Android Emulator.
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content:
System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{ Server: Microsoft-HTTPAPI/2.0 Date: Wed, 23 Oct 2019 00:58:01
GMT Connection: close Forwarded: host=XXX.XXX.X.XX:XXXXX;
proto=https Content-Type: text/html; charset=us-ascii
Content-Length: 374 }}
Setup:
I'm using an IP address generated by Conveyor by Keyoti
I installed a security certificate on the emulator required by Conveyor by Keyoti
I swapped out Microsoft.AspNetCore.Mvc.HttpPost attribute with System.Web.Http.HttpPost
Emulator:
Successful: HTTP Get
Failed: HTTP Post
Integration Test:
Successful: HTTP Post (using same endpoint)
Code:
I wrote an automated test that calls the same HTTP Post implementation.
Because I executed the same code successfully on my laptop via an automated test, I don't think the actual code is the issue:
open Microsoft.AspNetCore.Mvc
open Newtonsoft.Json
[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
inherit ControllerBase()
[<System.Web.Http.HttpPost>]
member x.Post([<FromBody>] json:string) =
...
Summary:
In conclusion, I have isolated the environment to the Android Emulator and not my laptop. Hence, the emulator can successfully trigger an HTTP Get. However, it fails to perform a HTTP Post even though my laptop device can do both.
UPDATE:
I applied guidance from this Xamarin Android ASP.Net Core WebAPI document.
Specifically, I installed another security certificate on the Android emulator.
I was then able to observe an HTTP Get on the Android Emulator.
However, I continue to get an error for HTTP Post.
OperationCanceledException
Physical Device:
If I run the app from a physical android device I observe the following:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
Date: Wed, 23 Oct 2019 13:33:20 GMT
Server: Kestrel
Transfer-Encoding: chunked
Forwarded: host=xxx.xxx.x.xx:xxxxx; proto=https
Content-Type: text/plain
}}
New Update:
I disabled debugging on just my code on the server implementation and discovered the following exception:
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: 'Bad chunk size data.'
Any suggestions?
this might not be a direct answer to your question, but i would like to suggest
localtunnel. a very easy way to temporarily expose your local api so that you can test it either on emulator or even physical device. Have used this alot my self, as it is very convenient to just type a single line in terminal to start it.
The following reference solved my issue.
Infrastructure:
type GlobalHttpClient private () =
static let mutable (httpClient:System.Net.Http.HttpClient) = null
static member val Instance = httpClient with get,set
Xamarin.Android project:
using Android.Http;
using Android.Net;
using Javax.Net.Ssl;
using System.Net.Http;
using Xamarin.Android.Net;
using Xamarin.Forms;
using WebGatewaySupport;
[assembly: Dependency(typeof(HTTPClientHandlerCreationService_Android))]
namespace Android.Http
{
public class HTTPClientHandlerCreationService_Android : IHTTPClientHandlerCreationService
{
public HttpClientHandler GetInsecureHandler()
{
return new IgnoreSSLClientHandler();
}
}
internal class IgnoreSSLClientHandler : AndroidClientHandler
{
protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
{
return SSLCertificateSocketFactory.GetInsecure(1000, null);
}
protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
{
return new IgnoreSSLHostnameVerifier();
}
}
internal class IgnoreSSLHostnameVerifier : Java.Lang.Object, IHostnameVerifier
{
public bool Verify(string hostname, ISSLSession session)
{
return true;
}
}
}
Xamarin.Forms App:
switch (Device.RuntimePlatform)
{
case Device.Android:
GlobalHttpClient.Instance = new HttpClient(DependencyService.Get<IHTTPClientHandlerCreationService>().GetInsecureHandler());
break;
default:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
GlobalHttpClient.Instance = new HttpClient(new HttpClientHandler());
break;
}
Client Gateway:
let postTo (baseAddress:string) (resource:string) (payload:Object) =
GlobalHttpClient.Instance.BaseAddress <- Uri(baseAddress)
let encoded = Uri.EscapeUriString(resource)
let result = GlobalHttpClient.Instance.PostAsJsonAsync(encoded, payload) |> toResult
result
Looks like you have a .NET Core Api. .NET Core does not have System.Web in Asp.NET. The HttpPost attribute and HttpGet attributes should come from Microsoft.AspNetCore.Mvc namespace which you have open.
Also since you are using the ApiController attribute model binding will just work as long as you bind to a model and not just a json string.
Create a model that you want the json to bind to and use that type for your parameter on Post and remove the FromBody attribute. Also if you do that you probably don't need newtonsoft.json.
open Microsoft.AspNetCore.Mvc
[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
inherit ControllerBase()
[<HttpPost>]
member x.Post(thing:TypeOfThing) =

OAuthValidateClientAuthenticationContext.ClientId and Parameters are null after Publish

Server Side Code
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
try
{
string clientId;
string clientSecret, deviceId;
string appId = context.Parameters["applicationId"];//renamed from applicationName
string version = context.Parameters["version"];
deviceId = context.Parameters["deviceId"];//renamed from applicationName
context.OwinContext.Set("appId", appId);
ApiAppClient client;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (string.IsNullOrWhiteSpace(clientId))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (string.IsNullOrWhiteSpace(clientId))
{
var query = context.Parameters.Where(x => x.Key == "client_id");
if (query.Any())
{
clientId = query.FirstOrDefault().Value?[0];
}
}
if (string.IsNullOrWhiteSpace(clientId))
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
//context.Validated();
context.SetError($"Missing ClientId.");
////context.Rejected();
return;
}
Client Side Code
POST https://example.com/token HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Host: example.com
Content-Length: 258
client_id=xxxxxxxxxxxxxxxxxxxxxx&client_secret=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz&applicationId=aaaaaaaaaaaaaaaaaaaaaaaa&username=me&password=somthing&grant_type=password&deviceId=5545555&version=3.2.1.89
I have a strange case for above OAuth process ClientId is null as well as Parameters is empty.
The above code works fine on all of our developer machines.
Compiled Version of Web API also works if hosted in IIS on Developer Machine.
But When I try to host Web API on my remote server it always gives error "Missing ClientId".
It uses to work perfectly on the same server before upgrading NuGet packages.
Microsoft.Owin.Security.OAuth 3.0.1.0 upgraded to 3.1.0.0
Microsoft.Owin.Security 3.0.1.0 upgraded to 3.1.0.0
And All Unity Packages

Web API add openid scope to auth url for swagger/swashbuckle UI

We have a asp.net web api application which uses swagger/swashbuckle for it's api documentation. The api is secured by azure AD using oauth/openid-connect. The configuration for swagger is done in code:
var oauthParams = new Dictionary<string, string>
{
{ "resource", "https://blahblahblah/someId" }
};
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion(Version, Name);
c.UseFullTypeNameInSchemaIds();
c.OAuth2("oauth2")
.Description("OAuth2 Implicit Grant")
.Flow("implicit")
.AuthorizationUrl(
"https://login.microsoftonline.com/te/ourtenant/ourcustompolicy/oauth2/authorize")
.TokenUrl(
"https://login.microsoftonline.com/te/ourtenant/ourcustompolicy/oauth2/token");
c.OperationFilter<AssignOAuth2SecurityRequirements>();
})
.EnableSwaggerUi(c =>
{
c.EnableOAuth2Support(_applicationId, null, "http://localhost:49919/swagger/ui/o2c-html", "Swagger", " ", oauthParams);
c.BooleanValues(new[] { "0", "1" });
c.DisableValidator();
c.DocExpansion(DocExpansion.List);
});
When swashbuckle constructs the auth url for login, it automatically adds:
&scope=
However I need this to be:
&scope=openid
I have tried adding this:
var oauthParams = new Dictionary<string, string>
{
{ "resource", "https://blahblahblah/someId" },
{ "scope", "openid" }
};
But this then adds:
&scope=&someotherparam=someothervalue&scope=openid
Any ideas how to add
&scope=openid
To the auth url that swashbuckle constructs?
Many thanks
So, found out what the issue was, the offending code can be found here:
https://github.com/swagger-api/swagger-ui/blob/2.x/dist/lib/swagger-oauth.js
These js files are from a git submodule that references the old version of the UI.
I can see on lines 154-158 we have this code:
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator));
url += '&state=' + encodeURIComponent(state);
It basically adds scopes regardless of whether there are scopes or not. This means you cannot add scopes in the additionalQueryParams dictionary that gets sent into EnableOAuth2Support as you will get a url that contains 2 scope query params i.e.
&scope=&otherparam=otherparamvalue&scope=openid
A simple length check around the scopes would fix it.
I ended up removing swashbuckle from the web api project and added a different nuget package called swagger-net, found here:
https://www.nuget.org/packages/Swagger-Net/
This is actively maintained and it resolved the issue and uses a newer version of the swagger ui. The configuration remained exactly the same, the only thing you need to change is your reply url which is now:
http://your-url/swagger/ui/oauth2-redirect-html

Xamarin Android : Https Post Request to Local Intranet Web Api Causes error : StatusCode: 404, ReasonPhrase: 'Not Found'

This is going to be a long question..
Our company has to follow PCI Standards, so a while back we had to ensure all our Servers were TLS1.2 compliant. As a result we implemented TLS as explained here in our Xamarin Forms app. But we noticed issues in Android versions less then Api 22. So we implemented a dependency service for fetching the HTTPClient and if the Api versions were less than 22 we implemented a custom ssl socket factory, here's the example.
Everything was fine till a few weeks back there was a decision to upgrade the servers to Windows 2016 on the dev environment. We've redeployed our Web Api to the server and ever since then, the api is inaccessible from a few devices. The problem we've faced is in Samsung Galaxy S4(Android 4.4) and Nexus 5(Android 5.1.1). We've tried testing the app on a Samsung Galaxy A7(Android 6) and it works okay. iOS is also fine.
This is the error we recieve on the S4 and Nexus 5
StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content:
System.Net.Http.StreamContent, Headers: { Date: Wed, 20 Sep 2017
04:00:09 GMT Server: Microsoft-IIS/10.0 X-Android-Received-Millis:
1505880010792 X-Android-Response-Source: NETWORK 404
X-Android-Selected-Transport: http/1.1 X-Android-Sent-Millis:
1505880010781 X-Powered-By: ASP.NET Content-Length: 1245 Content-Type:
text/html
Here's the signature of the Web Api
[HttpPost("GetMinimumVersion")]
public GetMinimumVersionResponse GetMinimumVersion([FromBody] GetMinimumVersionRequest value)
And this is the code we use to make a post request
using (_httpclient = _deviceInfo.GetHttpClient())
{
_httpclient.MaxResponseContentBufferSize = 256000;
_httpclient.BaseAddress = new Uri(BaseAddress);
_httpclient.Timeout = timeout > 0 ? TimeSpan.FromSeconds(timeout) : TimeSpan.FromSeconds(60000);
Insights.Track("ApiUrlCalled", new Dictionary<string, string> { { "Api URL", url } });
var jsonOut = new StringContent(JsonConvert.SerializeObject(body, new IsoDateTimeConverter()));
jsonOut.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await _httpclient.PostAsync(url, jsonOut);
switch (response.StatusCode)
{
case HttpStatusCode.OK:
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<T>(content);
ReceiveNotificationDateTime(result);
return result;
default:
var result1 = new T { StatusID = (int)SystemStatusOutcomes.Failed, StatusMessage = response.ToString() };
ReceiveNotificationDateTime(result1);
return result1;
}
}
It's worth noting that the app when talking to the production api works fine on all devices. And we're also able to make post requests to the dev api via Postman.
After some digging and scratching, I found out that the ciphers used on production and dev were different.
Here's the cipher used on Prod
and here's the one used on dev.
I had a look at the SSL Ciphers Android supports here. And it looks like the ciper suite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 is supported in Android Api version 20+. This makes sense that it wont work on Android 4.4. But why would we get this error on Nexus 5? Any pointers?
Also is there any workaround to get this cipher enabled on Android 4.4?

Resources