I have been using Visual Studio 2019 to develop desktop and mobile applications using Xamarin. I could successfully deploy my Xamarin UWP app to Microsoft Store until I upgraded my Visual Studio 2019 to 16.10.2.
Normally, before we can publish our UWP app to Microsoft Store, we need to launch WACK (Windows App Certification Kit) to validate our app package. However, in VS2019 16.10.2 (and onwards), there will be an error occurs, as shown in the screenshot below, and the validation cannot be completed.
MSBuild Project Build Output
Since my code is the same, so the first thing that I suspect is that the new updates in Visual Studio 2019 are causing this issue. Hence, I changed the verbosity of the project build output to Diagnostic, as shown below. This will help us understand better about what’s happening during the build.
By comparing the current build output with the one using the previous version of Visual Studio 2019, I realised that there is something new in the current build ouput. The parameter GenerateTemporaryStoreCertificate is set to false while BuildAppxUploadPackageForUap is true, as shown below.
1>Target "_RemoveDisposableSigningCertificate: (TargetId:293)" in file "C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\MSBuild\Microsoft\VisualStudio\v16.0\AppxPackage\Microsoft.AppXPackage.Targets" from project "...UWP.csproj" (target "_GenerateAppxPackage" depends on it):
1>Task "RemoveDisposableSigningCertificate" skipped, due to false condition; ('$(GenerateTemporaryStoreCertificate)' == 'true' and '$(BuildAppxUploadPackageForUap)' == 'true') was evaluated as ('false' == 'true' and 'true' == 'true').
1>Done building target "_RemoveDisposableSigningCertificate" in project "...UWP.csproj".: (TargetId:293)
Online Discussions
Meanwhile, there are only two discussion threads online about this issue.
Few days later, on 1st of July 2021, another developer Tautvydas Zilys also reported a similar issue as Nick Stevens’. Interestingly, the same Microsoft engineer, James Parsons, replied to them with the similar answer, i.e. adding the following property in their project file to set GenerateTemporaryStoreCertificate to true.
That’s all to fix the issue. I hope this article, which is also the 3rd in the world discussing about this Visual Studio 2019 problem, is helpful to other Xamarin UWP developers who are running into the same problem.
KOSD, or Kopi-O Siew Dai, is a type of Singapore coffee that I enjoy. It is basically a cup of coffee with a little bit of sugar. This series is meant to blog about technical knowledge that I gained while having a small cup of Kopi-O Siew Dai.
In the previous article, we talked about how to generate realistic but not real patient data using Synthea(TM) and then also how to store them securely in Azure Storage.
Setup Azure API for FHIR®
Today, we will continue the journey. The first step we need to do is to setup the Azure API for FHIR®.
The Azure API for FHIR® is a managed, standards-based, and healthcare data platform available on Azure. It enables organisations to bring their clinical health data into the cloud based on the interoperable data standard FHIR®. The reason why we choose to use it is because security and privacy features are embedded into the service. As customers, we own and control the patient data, knowing how it is stored and accessed. Hence, it’s a PaaS that enables us build healthcare data solution easily.
When we are setting up the Azure API for FHIR®, we need to specify the version of FHIR® we are going to use. Currently there are only four milestone releases of FHIR®. The latest version, R4, was released in December 2018. On Azure, we can only choose either R4 or STU3 (which is the third release). We will go for R4.
For the Authentication of the API service, we will be using Azure Access Control (IAM) which is the default option. Hence, we will use the Authority and Audience default values.
When we are setting up this API service, we also need to specify the throughput of the database which will be used to store the imported patient data later.
After we click on the button to create the API service, it will take about 5 minutes to successfully deploy it on Azure.
The following screenshot shows how we register the client application with a redirect URI pointing to https://www.getpostman.com/oauth2/callback which will help us to test the connectivity via Postman later.
Once the client application is created we need to proceed to create a client secret, as shown in the following screenshot, so that later we can use it to request a token.
Then we have to allow this client application to access our Azure API for FHIR®. There are two things we need to do.
Firstly, we need to grant the client application a permission called user_impersonation from the Azure Healthcare APIs, as shown in the screenshot below.
Secondly, we need to head back to our Azure API for FHIR® to enable this client application to access it, as shown in the following screenshot.
The reason we choose only “FHIR Data Writer” role is because this roles enable both read and write access to the API. Once the role is successfully added, we shall see something similar as shown in the screenshot below.
Test the API with Postman
To make sure our Azure API for FHIR® is running well, we can visit its metadata link without any authentication. If it is running smoothly, we shall see something as shown in the following screenshot.
To access the patient data, we need to authenticate ourselves. In order to do so, we first need to get an access token from the client application in Azure Active Directory. We do so by making a POST request to the following URL https://login.microsoftonline.com/<tenant-id>/oauth2/token.
As shown in the following screenshot, the Tenant ID (and also Client ID) can be found at the Overview page of the client application. The resource is basically the URL of the Azure API for FHIR®.
Once we have the access token, we then can access the Patient endpoint, as shown in the following screenshot.
Now that we have the realistic but real patient data in the Azure Storage and we have the Azure API for FHIR® with a SQL database. So the next step that we need to do is pump the data into the SQL database so that other clients can consume the data through the Azure API for FHIR®. In order to do so, we will need a data importer.
Firstly, we will create an Azure Function which will do the data import. There is an official sample on how to write this Function. I didn’t really follow the deployment steps given in the README of the project. Instead, I created a new Azure Function project in the Visual Studio and published it to the Azure. Interestingly, if I use VS Code, the deployment will fail.
In Visual Studio, we will be creating a C# function which will run whenever a new patient data is uploaded to the container. Then the same function will remove the patient data from the Azure Storage once the data is fully updated.
When we are creating a new Azure Function project on Visual Studio, for the convenience later, it’s better we use back the Azure Storage that we use for storing the realistic but not real patient data for our Azure Function app storage as well, as shown in the following screenshot. Thus, the Connection Setting Name will be AzureWebJobsStorage and the Path will point to the container storing our patient data (I recreated the container from syntheadata used in previous article to fhirimport in this article).
After the deployment is successful, we need to add the following application settings to the Azure Function.
Audience: <found in Authentication of Azure API for FHIR®>
Authority: <found in Authentication of Azure API for FHIR®>
ClientId: <found in the Overview of the Client App registered>
ClientSecret: <found in the Certificates & secrets of the Client App>
FhirServerUrl: <found in the Overview of Azure API for FHIR®>
After that, in order to help us diagnose problems happening in each data import, it’s recommended to integrate Application Insights to our Azure Function. After that, we can use ILogger to log information, warnings, or errors in our Azure Function, for example
log.LogWarning($"Request failed for {resource_type} (id: {id}) with {result.Result.StatusCode}.");
Then with Application Insights, we can easily get the log information from the Azure Function in its Monitor section.
From the official sample code, I made a small change to the waiting time between each try of the request to the Azure API for FHIR®, as shown in the following screenshot.
In the FhirBundleBlobTrigger.cs, I increased the waiting time to have extra 30 seconds because the original waiting time is short that sometimes the data import will fail.
In the following screenshot, the Observation data can only be uploaded after 5 attempts. In the mean time, our request rate has exceeded the maximum API request rate and thus has been throttled too. So we cannot make calls to Azure API for FHIR® too frequent.
Now, when we make a GET request to the Patient endpoint of Azure API for FHIR® with a certain ID, we will be able to get the corresponding patient data back on Postman.
Yup, so at this stage, we have successfully imported data generated by Synthea(TM) to the Azure API for FHIR® database.
Few days ago, my teammate would like to learn how to use MS SQL Server. However, he only has a Macbook and MS SQL Server doesn’t run on macOS. Hence, I decided to write him a quick setup guide on how to do that with the help of container.
We need to run Docker on our Mac machine. Since my teammate is new to Docker, he can simply choose a rather straight-forward path for this, which is to use Docker Desktop on Mac. Kindly take note of the system requirement before proceed to install it.
Once the Docker is up and running, we can proceed to pull the image of SQL Server 2019 from the Docker Hub.
We can run the following command in Terminal window to start the database server. Here we are using 1501 as the port. Take note that, we need to replace the password with our password which meets the following guideline:
at least 8 characters;
including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.
In the command above, there are two environment variables.
Firstly, it is the environment variable “ACCEPT_EULA”. Setting it to Y means that we accept the End-User Licensing Agreement of the product. So far I still couldn’t find the EULA of the Microsoft SQL Server 2019. If you know, please drop me a message in the comment section. Thanks!
Secondly, it is the “SA_PASSWORD” which is used to set the password that we will later use to connect to the SQL server later as the database admin (userid = “sa”).
Actually, there is another environment variable which is not set here. It is the MSSQL_PID, i.e. the product ID of the SQL Server. By default, it is the Developer edition. If we would like to use Express or Enterprise edition, we can specify it here.
To visualise and manage our data in the databases, we need to use tools such as SQL Server Management Studio (SSMS). However, SSMS is only for Windows (AMD or Intel). So, on macOS, we have to choose another cross-platform alternative, which is Azure Data Studio. Azure Data Studio is usable on Windows and Linux too.
Now we can connect to the SQL Server from Azure Data Studio as shown below. Take note that the Server is “localhost,1501” and it is using comma, not dot, between the word localhost and the port number.
If the connection is successful, we shall be able to see the Server Dashboard as shown below.
That’s all. Now we can have MS SQL Server running on our Mac machine for local testing and development.
KOSD, or Kopi-O Siew Dai, is a type of Singapore coffee that I enjoy. It is basically a cup of coffee with a little bit of sugar. This series is meant to blog about technical knowledge that I gained while having a small cup of Kopi-O Siew Dai.
In my previous post, I shared about the way to connect Android app with IdentityServer4 using AppAuth for Android. However, that way will popup a login page on a web browser on phone when users are trying to login to our app. This may not be what the business people want. Sometimes, they are looking for a customized native login page on the app itself.
To do so, we can continue to make use of IdentityServer4.
In this setup, I will be using in-memory configuration.
As a start, I need to introduce a new ApiResource with the following codes in the Startup.cs of our IdentityServer project.
var availableResources = new List<ApiResource>();
...
availableResources.Add(new ApiResource("mobile-app-api", "Mobile App API Main Scope"));
...
services.AddIdentityServer()
...
.AddInMemoryApiResources(availableResources)
.AddInMemoryClients(new ClientStore(Configuration).GetClients())
.AddAspNetIdentity<ApplicationUser>();
Identity Server Setup: Defining New Client
As the code above shows, there is a ClientStore that we need to add a new client to with the following codes.
public class ClientStore : IClientStore
{
...
public IEnumerable<Client> GetClients()
{
var availableClients = new List<Client>();
...
availableClients.Add(new Client
{
ClientId = "mobile-app-api",
ClientName = "Mobile App APIs",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = { new Secret(Configuration["MobileAppApi:ClientSecret"].Sha256()) },
AllowedScopes = { "mobile-app-api" }
});
return availableClients;
}
}
Configuring Services in Web API
In the Startup.cs of our Web API project, we need to update it as follows.
Besides the step above, we also need to make sure the following one line “app.UseAuthentication()” in the Startup.cs. Without this, we cannot make the authentication and authorization to work in our Web API project.
Receiving Username and Password to Return Access Token
We also need to add a new controller to receive username and password which will in return tell the mobile app whether the login of the user is successful or not. If the user is logged in successfully, then an access token will be returned.
[Route("api/[controller]")]
public class AuthenticateController : Controller
{
...
[HttpPost]
[Route("login")]
public async Task<ActionResult> Login([FromBody] string userName, string password)
{
var disco = await DiscoveryClient.GetAsync("<URL of the identity server>");
var tokenClient = new TokenClient(disco.TokenEndpoint, "mobile-app-api", Configuration["MobileAppApi:ClientSecret"]);
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(userName, password, "mobile-app-api");
if (tokenResponse.IsError)
{
return Unauthorized();
}
return new JsonResult(tokenResponse.Json);
}
...
}
Securing our APIs
We can now proceed to protect our Web APIs with [Authorize] attribute. In the code below, I also try to return the available claims via the API. The claims will tell the Web API who is logging in and calling the API now via the mobile app.
[HttpGet]
[Authorize]
public IEnumerable<string> Get()
{
var claimTypesAndValues = new List<string>();
foreach (var claim in User.Claims)
{
claimTypesAndValues.Add($"{ claim.Type }: { claim.Value }");
}
return claimTypesAndValues.ToArray();
}
Conclusion
This project took me two days to find out how to make the authentication works because I misunderstand how IdentityServer4 works in this case. Hence, it is always important to fully understand the things on your hands before working on them.