microsoft / referencesource

Source from the Microsoft .NET Reference Source that represent a subset of the .NET Framework
https://referencesource.microsoft.com/
MIT License
3.17k stars 1.27k forks source link

Fhir Epic Sandbox : Creating a JWT in C# to Obtain an Access Token for a Backend Service #198

Open fayazshaik07 opened 8 months ago

fayazshaik07 commented 8 months ago

I'm trying to use the sandbox from https://fhir.epic.com/ for Backend Services.

I am following this tutorial : https://fhir.epic.com/Documentation?docId=oauth2&section=BackendOAuth2Guide

I should send a POST request to this URL: https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token

Below is my code

    static async System.Threading.Tasks.Task GetFHIRToken()
    {
        string clientId = "883a61da-4fb3-465b-8ac0-531dd0b673fe";
        string privateKeyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads", "privatekey.pem");
        string tokenEndpoint = "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token";

        var jwt = GenerateJwt(clientId, privateKeyPath);

        using (var client = new HttpClient())
        {
            var requestData = new Dictionary<string, string>
        {
            { "grant_type", "client_credentials" },
            { "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
            { "client_assertion", jwt }
        };

            var response = await client.PostAsync(tokenEndpoint, new FormUrlEncodedContent(requestData));
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine($"Error: {response.StatusCode} - {response.ReasonPhrase}");
                var errorContent = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"Error Content: {errorContent}");
            }
            else
            {
                var responseData = await response.Content.ReadAsStringAsync();
                Console.WriteLine(responseData);
            }
            var responseContent = await response.Content.ReadAsStringAsync();

            Console.WriteLine(responseContent);
        }
    }

    static string GenerateJwt(string clientId, string privateKeyPath)
    {
        var privateKey = File.ReadAllText(privateKeyPath);

        var rsa = new RSACryptoServiceProvider();
        rsa.ImportFromPem(privateKey);

        var now = DateTime.UtcNow;
        var expires = now.AddMinutes(5);

        var handler = new JwtSecurityTokenHandler();
        var token = handler.CreateToken(new SecurityTokenDescriptor
        {
            Issuer = clientId,
            Audience = "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token",
            NotBefore = now,
            Expires = expires,
            SigningCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha384)
        });

        var tokenString = handler.WriteToken(token);
        return tokenString;
    }

public static class RSAExtensions
{
public static RSAParameters GetParametersFromPem(byte[] pemBytes)
{
    var pemString = Encoding.UTF8.GetString(pemBytes);
    var lines = pemString.Split('\n');
    var keyBytes = Convert.FromBase64String(string.Join("", lines[1..^1]));

    using (var rsa = RSA.Create())
    {
        rsa.ImportSubjectPublicKeyInfo(keyBytes, out _);
        return rsa.ExportParameters(false);
    }
}

}

Using the above code, I'm getting the below error

{
"error": "invalid_client",
"error_description": null
}

not sure what is it I'm missing.

tub0ng commented 2 months ago

Hi, do you have the solution for this issue? Could you please share?