Search code examples
c#apiazure-api-managementclient-certificates

Azure API - AUTHENTICATING APIS WITH A CLIENT CERTIFICATE + OAUTH 2.0


I have implemented Oauth 2.0 Azure API Authentication by creating a token with Client Secret. I am trying to use a Client certificate instead of a Client secret for creating OAuth 2.0 token. Could you please guide me on how to use the Client certificate to get a token? C# Code needed for implementing same.


Solution

  • Try to see here. It shows how to get an access token with a certificate by the HTTP request.

    POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token 
    Content-Type: application/x-www-form-urlencoded
    
    scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
    &client_id=<client-id>
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion=<An assertion (a JSON web token) that you need to create and sign with the certificate you registered as credentials for your application. >
    &grant_type=client_credentials
    

    C#:

    1. Use Azure.Identity

    // Authenticate a service principal with a certificate
    
    using Azure.Identity;
    
    var certificate = new X509Certificate2("./app/certs/certificate.pfx");
    var credential = new CertificateCredential(tenantId, clientId, certificate);
    

    2. Use WithCertificate in MSAL.NET

    The blog: https://cmatskas.com/create-a-net-core-deamon-app-that-calls-msgraph-with-a-certificate/

    using Microsoft.Graph;
    using Microsoft.Identity.Client;
    using System;
    using System.Linq;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading.Tasks;
    
    namespace DaemonConsole
    {
        public class ClientCredentialsAuthProvider : IAuthenticationProvider
        {
            private readonly IConfidentialClientApplication msalClient;
            private readonly string[] scopes;
    
            public ClientCredentialsAuthProvider(AuthenticationConfig config)
            {
                scopes = new string[] { config.Scopes };
    
                msalClient = ConfidentialClientApplicationBuilder
                    .Create(config.ClientId)
                    .WithCertificate(ReadCertificateLocal(config.CertificateName))
                    .WithAuthority(AadAuthorityAudience.AzureAdMyOrg, true)
                    .WithTenantId(config.Tenant)
                    .Build();
            }
    
            public async Task<string> GetAccessTokenAsync()
            {
                try
                {
                    var result = await msalClient.AcquireTokenForClient(scopes)
                        .ExecuteAsync();
    
                    return result.AccessToken;
                }
                catch (Exception exception)
                {
                    Console.WriteLine($"Error getting access token: {exception.Message}");
                    return null;
                }
    
            }
    
            // This is the required function to implement IAuthenticationProvider
            // The Graph SDK will call this function each time it makes a Graph
            // call.
            public async Task AuthenticateRequestAsync(HttpRequestMessage requestMessage)
            {
                requestMessage.Headers.Authorization =
                    new AuthenticationHeaderValue("bearer", await GetAccessTokenAsync());
            }
    
            private X509Certificate2 ReadCertificateLocal(string certificateName)
            {
                X509Certificate2 cert = null;
    
                using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
                {
                    store.Open(OpenFlags.ReadOnly);
                    X509Certificate2Collection certCollection = store.Certificates;
                    X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
                    X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certificateName, false);
                    cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
                }
                return cert;
            }
        }
    }