0

I'm trying to get an Authentication token from Graph Endpoint using a cer/pfx certificate that I have created using self signed. I have placed the certificate on the file system. Here is the code I'm using:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

namespace DAL_HTTP
{
    public class MSGraphAuth
    {
        public string _tenantId;
        public string _clientId;
        public string _certificateThumbprint;
        public string _debugCertificatePath;
        public string _debugCertificatePassword;

        public MSGraphAuth()
        {
            string TenantName = "testtenant.onmicrosoft.com";
            string TenantId = "<tenantid>";
            string ClientID = "<appid>";
            string AuthenticationContextURL = "https://login.microsoftonline.com/testtenant.onmicrosoft.com";
            string AcquireTokenURL = "https://graph.microsoft.com";
            string ClientKey = "*********";
            string certificateFilePath = @"D:\folder\test.onmicrosoft.com.pfx";
            string certificatePassword = "*******";

            _tenantId = TenantId;
            _clientId = ClientID;
            _certificateThumbprint = GetCertificateThumprint(certificateFilePath, certificatePassword);
            _debugCertificatePath = certificateFilePath;
            _debugCertificatePassword = certificatePassword;

        }

        public string GetCertificateThumprint(string certificateFilePath, string certPassword)
        {
            X509Certificate2 cert = new X509Certificate2(certificateFilePath, certPassword);

            return cert.Thumbprint;
        }

        public async Task<string> GetAccessTokenAsync(string url)
        {
            url = GetTenantUrl(url);

            var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{_tenantId}/oauth2/token"); // you can also use the v2.0 endpoint URL

            return (await authContext.AcquireTokenAsync(url, GetCertificate(_clientId, _certificateThumbprint))).AccessToken;
        }

        public static string GetTenantUrl(string url)
        {
            const string suffix = "sharepoint.com";
            var index = url.IndexOf(suffix, StringComparison.OrdinalIgnoreCase);
            return index != -1 ? url.Substring(0, index + suffix.Length) : url;
        }

        public ClientAssertionCertificate GetCertificate(string clientId, string thumbprint)
        {
            var certificate = GetCertificateFromDirectory(_debugCertificatePath, _debugCertificatePassword);
            return new ClientAssertionCertificate(clientId, certificate);
        }

        public static X509Certificate2 GetCertificateFromDirectory(string path, string password)
        {
            return new X509Certificate2(System.IO.Path.GetFullPath(path), password, X509KeyStorageFlags.DefaultKeySet);
        }

        private static X509Certificate2 GetCertificateFromStore(string thumbprint)
        {
            var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);

            store.Open(OpenFlags.ReadOnly);

            var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);

            store.Close();

            return certificates[0];
        }
    }
}

However, I get an error on this line:

return (await authContext.AcquireTokenAsync(url, GetCertificate(_clientId, _certificateThumbprint))).AccessToken;

The error message reads:

An unhandled exception of type 'Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException' occurred in mscorlib.dll

Additional information: AADSTS90002: Tenant 'token' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.

Trace ID: c9d3eac6-77e0-4f56-8c96-ac924ff90700

Correlation ID: e90027e1-cbcb-4291-9630-984c5c23757d

Timestamp: 2020-08-14 17:11:34Z

I've been following these two articles, still I cant get it to work.:

https://laurakokkarinen.com/authenticating-to-office-365-apis-with-a-certificate-step-by-step/comment-page-2/#comment-697

How to use Microsoft Graph API with Certificates (INTUNE)

What am I doing wrong ? Please help.

gtrivedi
  • 435
  • 1
  • 7
  • 18

1 Answers1

0

It seems that you didn't get the token that you want, try this official sample with certificates.

It uses the client credentials flow to acquire an access token, which can be used to call the Microsoft Graph and access organizational data. The sample uses both certificates and client secret, you could just use certificates.

private static async Task RunAsync()
    {
        AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");

        // Even if this is a console application here, a daemon application is a confidential client application
        IConfidentialClientApplication app;

        X509Certificate2 certificate = ReadCertificate(config.CertificateName);
        app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
             .WithCertificate(certificate)
             .WithAuthority(new Uri(config.Authority))
             .Build();
        

        // With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the 
        // application permissions need to be set statically (in the portal or by PowerShell), and then granted by
        // a tenant administrator. 
        string[] scopes = new string[] { $"{config.ApiUrl}.default" }; 
        
        AuthenticationResult result = null;
        try
        {
            result = await app.AcquireTokenForClient(scopes)
                .ExecuteAsync();
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Token acquired");
            Console.ResetColor();
        }
        catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
        {
            // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
            // Mitigation: change the scope to be as expected
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Scope provided is not supported");
            Console.ResetColor();
        }

        if (result != null)
        {
            var httpClient = new HttpClient();
            var apiCaller = new ProtectedApiCallHelper(httpClient);
            await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users", result.AccessToken, Display);
        }
    }
unknown
  • 6,778
  • 1
  • 5
  • 14