Skip to content

Instantly share code, notes, and snippets.

@brianpos
Created January 8, 2020 23:42
Show Gist options
  • Save brianpos/a4b95bedf06d2ba7789b055c92b92d0a to your computer and use it in GitHub Desktop.
Save brianpos/a4b95bedf06d2ba7789b055c92b92d0a to your computer and use it in GitHub Desktop.
Retrieving an access token and calling a FHIR api using a NASH based JWT bearer
// <package id="jose-jwt" version="2.1.0" targetFramework="net461" />
public async Task UseNASHcertificate()
{
// retrieve the NASH/Medicare certificate (from the local store - but could come from elsewhere)
var cert = GetNASHOrMedicareCertificate();
// Use the Digital certificate as a client cert for the http connection
// to the token endpoint
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificates.Add(cert);
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
// the parameters to send to the Token endpoint to exchange for an access token
// are in form encoding in the body, so prepare these
List<KeyValuePair<string, string>> values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"));
// replace these scopes with the ones that are desired
values.Add(new KeyValuePair<string, string>("scope", "fhir_complete profile openid launch"));
// These client ID and Secret will be provided per application connecting to the system
// (not per user connecting to the system)
values.Add(new KeyValuePair<string, string>("client_id", "MedTech"));
values.Add(new KeyValuePair<string, string>("client_secret", "D3928CE6-DA8F-4EDC-9989-7A9D336F54D0"));
// This is the JWT token (that will be signed with the NASH/Medicare digital certificate
// created by the software to assert the identity
values.Add(new KeyValuePair<string, string>("assertion", GenerateSampleJWT_Token(cert)));
// Exchange the JWT assertion for an access token to communicate with the FHIR server
// note the handler is passed into the client so that the client certificate is included
HttpClient client = new HttpClient(handler);
var formContent = new FormUrlEncodedContent(values);
var result = client.PostAsync("https://localhost:44335/connect/token", formContent).Result;
System.Diagnostics.Trace.WriteLine(result);
string resultTokenJson = result.Content.ReadAsStringAsync().Result;
System.Diagnostics.Trace.WriteLine(resultTokenJson);
var tokenParsed = Newtonsoft.Json.JsonConvert.DeserializeObject<AuthResult>(resultTokenJson);
System.Diagnostics.Trace.WriteLine($"Access token: {tokenParsed.access_token}");
System.Diagnostics.Trace.WriteLine($"Expires in: {tokenParsed.expires_in}");
System.Diagnostics.Trace.WriteLine($"Token type: {tokenParsed.token_type}");
Assert.IsNotNull(tokenParsed.access_token);
Assert.AreEqual(3600U, tokenParsed.expires_in);
Assert.AreEqual("Bearer", tokenParsed.token_type);
// re-create the httpclient to ensure that the client digital certificate isn't used
// (although it doesn't matter if it is included, but just to ensure that isnt some side effect)
client = new HttpClient();
client.SetBearerToken(tokenParsed.access_token);
// call the actual API using the bearer token
var response = await client.GetAsync("https://example.org/fhir/Patient?name=brian");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
Assert.Fail(response.ReasonPhrase);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
// call the actual API using the bearer token (again)
var response = await client.GetAsync("https://example.org/fhir/Patient?name=peter");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
Assert.Fail(response.ReasonPhrase);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
}
X509Certificate2 GetNASHOrMedicareCertificate()
{
// TODO
}
class AuthResult
{
public string access_token { get; set; }
public uint expires_in { get; set; }
public string token_type { get; set; }
}
private static string GenerateSampleJWT_Token(X509Certificate2 cert = null)
{
var payload = new
{
sub = "medtech\\example\\uid", // less than 127 chars
name = "Dr Frederick Findacure",
profile = "Practitioner/1",
iss = "http://example.org/issuer", // CONFIGURED for each identity provider
aud = "https://example.org/audience", // CONFIGURED for each client application accessing the system
exp = ToUnixTime(DateTime.Now.AddHours(12)),
nbf = ToUnixTime(DateTime.Now),
hpio = "8003629900017878" // from the NASH cert where exists (will be validated from the client cert)
// or hpii
};
return Jose.JWT.Encode(payload, cert.GetRSAPrivateKey(), Jose.JwsAlgorithm.RS256);
}
static long ToUnixTime(DateTime dateTime)
{
return (int)(dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment