local procedure InitializeConnection()
begin
SharePointClient.Initialize(SharepointSetup."Sharepoint URL", GetSharePointAuthorization());
end;
local procedure GetSharePointAuthorization(): Interface "SharePoint Authorization"
var
SharePointAuth: Codeunit "SharePoint Auth.";
begin
case SharepointSetup."Authorizaton Type" of
SharepointSetup."Authorizaton Type"::"Authorization Code":
exit(SharePointAuth.CreateAuthorizationCode(SharepointSetup.Tenant, SharepointSetup."Client Id",
SharepointSetup.GetSecret(Enum::"SSC Secret Type"::ClientSecret), SharepointSetup.Scope));
SharepointSetup."Authorizaton Type"::Certificate:
exit(SharePointAuth.CreateClientCredentials(SharepointSetup.Tenant, SharepointSetup."Client Id",
CertificateBase64, SharepointSetup.GetCertificatePassword(), SharepointSetup.Scope));
end;
end;
procedure DeleteFile(FilePath: Text): Boolean
var
TempSharepointFile: Record "SharePoint File" temporary;
Diagnostics: Interface "HTTP Diagnostics";
begin
InitializeConnection();
if SharepointClient.GetFileByServerRelativeUrl(FilePath, TempSharepointFile, false) then
if SharepointClient.DeleteFileByServerRelativeUrl(FilePath) then
exit(true);
Diagnostics := SharepointClient.GetDiagnostics();
if (not Diagnostics.IsSuccessStatusCode()) then
Error(DiagErr, Diagnostics.GetHttpStatusCode(), Diagnostics.GetErrorMessage(), Diagnostics.GetResponseReasonPhrase());
end;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Functions.Worker.Http;
using System.Net;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
namespace VLD.AcquireToken
{
public class AcquireToken
{
private readonly ILogger _logger;
public AcquireToken(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AcquireToken>();
}
[Function("AcquireToken")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
JObject jData = JsonConvert.DeserializeObject<JObject>(requestBody);
if (jData == null)
{
return await CreateErrorResponse(req, "Failed to read JSON data.");
}
// Extract data from JSON
string clientId = jData["clientId"]?.ToString();
string certificatePassword = jData["certificatePassword"]?.ToString();
string tenantId = jData["tenantId"]?.ToString();
string base64Cert = jData["base64Cert"]?.ToString();
string type = jData["type"]?.ToString();
if (type == "temp")
{
var emptyResponse = req.CreateResponse(HttpStatusCode.OK);
return emptyResponse;
}
string aud = $"https://login.microsoftonline.com/{tenantId}/v2.0/";
byte[] certBytes = Convert.FromBase64String(base64Cert);
X509Certificate2 certificate = new X509Certificate2(certBytes, certificatePassword);
var claims = new Dictionary<string, object>();
claims["aud"] = aud;
claims["sub"] = clientId;
claims["iss"] = clientId;
claims["jti"] = Guid.NewGuid().ToString();
var signingCredentials = new X509SigningCredentials(certificate);
var securityTokenDescriptor = new SecurityTokenDescriptor();
securityTokenDescriptor.Claims = claims;
securityTokenDescriptor.SigningCredentials = signingCredentials;
var tokenHandler = new JsonWebTokenHandler();
var clientAssertion = tokenHandler.CreateToken(securityTokenDescriptor);
var successResponse = req.CreateResponse(HttpStatusCode.OK);
successResponse.Headers.Add("Content-Type", "text/plain; charset=utf-8");
await successResponse.WriteStringAsync(clientAssertion);
return successResponse;
}
private async Task<HttpResponseData> CreateErrorResponse(HttpRequestData req, string message)
{
var response = req.CreateResponse(HttpStatusCode.BadRequest);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
await response.WriteStringAsync(message);
return response;
}
}
}
JObject.Add('clientId', ClientId);
JObject.Add('tenantId', EntraTenantId);
JObject.Add('certificatePassword', CertificatePassword);
JObject.Add('base64Cert', CertificateText);
JObject.WriteTo(TokenRequest);
APIMgt.SendRequest(TokenRequest, Enum::"Http Request Type"::POST,
StrSubstNo('%1%2', SharepointSetup."Azure Authrization URL", SharepointSetup."Azure Authrization Key"),
'', 0, ResponseTempBlob, DictionaryContentHeaders, DictionaryDefaultHeaders);
contentToSend.Append(StrSubstNo('client_id=%1', ClientId));
contentToSend.Append(StrSubstNo('&client_assertion=%1', TypeHelper.UriEscapeDataString(AssertionKey)));
contentToSend.Append(StrSubstNo('&scope=%1&', TypeHelper.UriEscapeDataString(Scope)));
contentToSend.Append(StrSubstNo('client_assertion_type=%1&', TypeHelper.UriEscapeDataString('urn:ietf:params:oauth:client-assertion-type:jwt-bearer')));
contentToSend.Append('grant_type=client_credentials');
APIMgt.SendRequest(contentToSend.ToText(), Enum::"Http Request Type"::POST, StrSubstNo(AuthorityTxt, EntraTenantId),
'application/x-www-form-urlencoded', 0, ResponseTempBlob, DictionaryContentHeaders, DictionaryDefaultHeaders);
ResponseTempBlob.CreateInStream(ResponseInstream);
while not ResponseInstream.EOS() do begin
ResponseInstream.Read(Buffer);
Response += Buffer;
end;
JObject.ReadFrom(Response);
JObject.Get('access_token', JToken);
AccessToken := JToken.AsValue().AsText();
codeunit 81772 "SSC SharePoint S2S Certificate" implements "SharePoint Authorization"
{
procedure Authorize(var HttpRequestMessage: HttpRequestMessage);
var
Headers: HttpHeaders;
begin
HttpRequestMessage.GetHeaders(Headers);
Headers.Add('Authorization', SecretStrSubstNo(BearerTxt, AccessToken));
end;
}
local procedure GetSharePointAuthorization(): Interface "SharePoint Authorization"
var
SharePointAuth: Codeunit "SharePoint Auth.";
SharepointCustomCertificate: Codeunit "SSC SharePoint S2S Certificate";
CertificateBase64: Text;
begin
case SharepointSetup."Authorizaton Type" of
SharepointSetup."Authorizaton Type"::"Authorization Code":
exit(SharePointAuth.CreateAuthorizationCode(SharepointSetup.Tenant, SharepointSetup."Client Id",
SharepointSetup.GetSecret(Enum::"SSC Secret Type"::ClientSecret), SharepointSetup.Scope));
SharepointSetup."Authorizaton Type"::Certificate:
exit(SharePointAuth.CreateClientCredentials(SharepointSetup.Tenant, SharepointSetup."Client Id",
CertificateBase64, SharepointSetup.GetCertificatePassword(), SharepointSetup.Scope));
SharepointSetup."Authorizaton Type"::"Custom Certificate":
begin
SharepointSetup.TestField("Azure Authrization URL");
SharepointSetup.TestField("Azure Authrization URL");
SharepointCustomCertificate.SetParameters(SharepointSetup.Tenant, SharepointSetup."Client ID", SharepointSetup.Scope, CertificateBase64,
SharepointSetup.GetCertificatePassword(), SharepointSetup);
exit(SharepointCustomCertificate);
end;
end;
end;
Preparation for testing involves several stages. The first is creating an App Registration with the appropriate Sharepoint permissions. For example Application permission Sites.FullControl.All Don't forget to grant admin consent for permissions.
We also need to generate a certificate with password, this is done using the Powershell script from the documentation. The output will be .pfx and .cer files. The .pfx file is used to obtain the JWT Token and is uploaded to Business Central, while the .cer file is uploaded to the App Registration.