Created
January 8, 2020 23:42
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// <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