using System; using System.IO; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Text; using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.JsonWebTokens; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace L2S_Test { // THIS IS FOR TESTING AND INFORMATIONAL USAGE ONLY // DO NOT USE IN PRODUCTION /// /// Class Program /// public class Program { #region Test Environment Variables // Authorization Endpoint private const string _tenantID = "6a2f7009-2835-4786-8853-2c5a5d637d13"; // Replace with own L2S environment ID / Azure Tenant ID // Client Application Credentials private const string _applicationID = "7048a0bb-6046-4c38-91da-f05361e5cbb3"; // Replace with own Authentication application client id private const string _clientID = "b602f916-1049-4df6-b2d8-e3078b55b07d"; // Replace with own company service principal client ID // Certificate Application Credentials private const string _privateCertificateFilename = "private-certificate.pfx"; // Rename your certificate file to `private-certificate.pfx` or change to your file name. Make sure certificate file is in same folder. private const string _privateCertificatePassword = ""; // Replace with own private certificate password // API Information private const string _apiHostURL = "https://apis-test-l2s.collabor8.no/"; // Replace with own L2S Integration API URL #endregion #region Constants private const string _tokenUrl = "https://login.microsoftonline.com/" + _tenantID + "/oauth2/v2.0/token"; private const string _scope = "api://" + _applicationID + "/.default"; private const string _assertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; private const string _grantType = "client_credentials"; #endregion /// /// Method Main runs all the functions and provides a good example of how the Class Program should be used /// /// static void Main(string[] args) { // Attempts to read certificate if (ReadCertificate(out X509Certificate2 certificate)) { string clientAssertion = GetClientAssertion(certificate); string accessToken = GetAccessToken(clientAssertion); CallTestEndpoint(accessToken); } Console.Read(); } #region Functionality /// /// Method ReadCertificate attempts to read a certificate from path ./_privateCertificateFilename. /// /// /// /// /// Boolean Success. /// Outputs X509Certificate2 representing the certificate. /// /// /// /// For example: /// /// bool success = ReadCertificate(out X509Certificate2 certificate) /// /// private static bool ReadCertificate(out X509Certificate2 certificate) { // Get Current Working Directory string cwd = Directory.GetCurrentDirectory(); // Get Project Directory / Base Directory string baseDir = cwd.Split(new string[] { "\\bin" }, StringSplitOptions.None)[0]; Console.WriteLine(String.Format("\nTrying to get private certificate from {0}...", Path.Combine(baseDir, _privateCertificateFilename))); try { // Try to read Certificate using X509Certificate2 by combining the projects base directorory (`baseDir`) // with the private certificate's file name (`_privateCertificateFileName`) and private certificate password (`_privateCertificatePassword`) certificate = new X509Certificate2(Path.Combine(baseDir, _privateCertificateFilename), _privateCertificatePassword); Console.WriteLine(String.Format("Got Certificate:\n{0}", certificate)); return true; } catch (System.Security.Cryptography.CryptographicException e) { switch (e.Message) { case "The specified network password is not correct.\r\n": Console.WriteLine( "\nThe Private Certificate Password was not correct,\n" + "Make sure you are using the correct certificate and password!\n" ); break; case "The system cannot find the file specified.\r\n": Console.WriteLine( "\nCould not find the certificate file,\n" + "Please place the private certificate file (file with extension .pfx) in" + baseDir + " and name it `" + _privateCertificateFilename + "`" ); break; default: Console.WriteLine( "An error occured during the reading of the certificate.\n" + "Error:\n" + e ); break; } certificate = null; return false; } } /// /// Gets Client Assertion /// /// /// /// /// string Client Assertion /// /// /// /// For Example: /// /// if (ReadCertificate(out X509Certificate2 certificate)) /// { /// string clientAssertion = GetClientAssertion(certificate) /// } /// /// gets a client assertion as long as ReadCertificate succeeds. /// private static string GetClientAssertion(X509Certificate2 certificate) { Console.WriteLine("\nGetting client assertion..."); Dictionary claims = new Dictionary() { { "aud", "https://login.microsoftonline.com/" + _tenantID + "/v2.0" }, { "iss", _clientID }, { "jti", Guid.NewGuid().ToString() }, { "sub", _clientID } }; SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor { Claims = claims, SigningCredentials = new X509SigningCredentials(certificate) }; JsonWebTokenHandler handler = new JsonWebTokenHandler(); string clientAssertion = handler.CreateToken(securityTokenDescriptor); Console.WriteLine(String.Format("Got Client Assertion:\n{0}", clientAssertion)); return clientAssertion; } /// /// Gets client assertion /// /// /// /// /// Task Access Token /// /// /// /// For Example: /// /// if (ReadCertificate(out X509Certificate2 certificate)) /// { /// string clientAssertion = GetClientAssertion(certificate); /// /// string accessToken = GetAccessToken(clientAssertion); /// } /// /// gets an access token if ReadCertificate() succeeds, it get's client assertion using the method GetClientAssertion() /// public static string GetAccessToken(string clientAssertion) { Console.WriteLine("\nGetting access token..."); var nvc = new List>(); nvc.Add(new KeyValuePair("scope", _scope)); nvc.Add(new KeyValuePair("client_id", _clientID)); nvc.Add(new KeyValuePair("client_assertion_type", _assertionType)); nvc.Add(new KeyValuePair("client_assertion", clientAssertion)); nvc.Add(new KeyValuePair("grant_type", _grantType)); var req = new HttpRequestMessage(HttpMethod.Post, _tokenUrl) { Content = new FormUrlEncodedContent(nvc) }; var client = new HttpClient(); var res = client.SendAsync(req).Result; if (!res.IsSuccessStatusCode) { throw new Exception(String.Format("Unsuccesful query: {0} {1}", res.StatusCode, res.ReasonPhrase)); } var data = res.Content.ReadAsStringAsync().Result; var details = JObject.Parse(data); string token = (string)details["access_token"]; Console.WriteLine(String.Format("Got Token:\n{0}", token)); return token; } #endregion #region Testing /// /// Calls the test endpoint /// /// /// /// /// Task<HttpResponseMessage> result /// /// /// /// For Example: /// /// if (ReadCertificate(out X509Certificate2 certificate)) /// { /// string clientAssertion = GetClientAssertion(certificate); /// /// string accessToken = GetAccessToken(clientAssertion); /// /// CallTestEndpoint(accessToken); /// } /// /// gets and access token then tests /// public static HttpResponseMessage CallTestEndpoint(string token) { Console.WriteLine("\nCalling Test Endpoint..."); HttpClient client = new HttpClient(); MediaTypeWithQualityHeaderValue contentType = new MediaTypeWithQualityHeaderValue("application/json"); client.DefaultRequestHeaders.Accept.Add(contentType); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); HttpResponseMessage result = client.GetAsync(_apiHostURL).Result; Console.WriteLine(String.Format("Test Endpoint called, result:\n{0}", result)); return result; } #endregion } }