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
}
}