Skip to content

Commit 4c9a486

Browse files
committed
authentification via secret OK, authentification via certificat in progress
1 parent 47ae9af commit 4c9a486

15 files changed

+454
-16
lines changed

CertUtil/ExpoertKeysFromPfx.ps1

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Write-Host "Exporting keys from PFX file..."
2+
3+
$pfxFile = "bc-erabliereapi-connector-certificate.pfx"
4+
$keyFile = "bc-erabliereapi-connector-certificate.key"
5+
$certFile = "bc-erabliereapi-connector-certificate.crt"
6+
$password = "1234"
7+
8+
openssl pkcs12 -in $pfxFile -nocerts -out $keyFile -nodes -password pass:$password
9+
openssl pkcs12 -in $pfxFile -clcerts -nokeys -out $certFile -password pass:$password
10+
11+
Write-Host "Exporting keys from PFX file... Done"

CertUtil/GenerateCert.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
openssl genrsa -out certificateprivate.key 2048
2+
openssl req -new -key certificateprivate.key -out certificate.csr
3+
openssl x509 -req -days 365 -in certificate.csr -signkey certificateprivate.key -out certificate.crt
4+
openssl pkcs12 -export -out certificate.pfx -inkey certificateprivate.key -in certificate.crt
5+
openssl rsa -in certificateprivate.key -pubout -out certificatepublickey.pem
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Bag Attributes
2+
localKeyID: 01 00 00 00
3+
1.3.6.1.4.1.311.17.3.71: 4C 00 41 00 50 00 54 00 4F 00 50 00 2D 00 46 00 52 00 45 00 44 00 00 00
4+
subject=CN = localhost
5+
6+
issuer=CN = localhost
7+
8+
-----BEGIN CERTIFICATE-----
9+
MIIDGDCCAgCgAwIBAgIQahlZTYCiGbtFhqdk/8uZoTANBgkqhkiG9w0BAQUFADAU
10+
MRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjQwODE1MDIyNDUwWhcNMjUwODE1MDI0
11+
NDUwWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
12+
DwAwggEKAoIBAQDiTNRDz8hGr+DwOHcwB8pmCxLlI5WTGMkanHRGYgI3hgn4bUeo
13+
qVttwVdOhsMUSnOo837LfS4eEVsWQaTrLnqHqcNjrgjeGADcySA8fuckyRB9vuEl
14+
7d76n60hIyqkPVXY2F+vGvYdFAYKL2TKFyIN+fm7Z+adAfaHSIjqjcB81ECwig6+
15+
mt18JmabfxkG+wd3MG7zhA1ypqHLsKzTKxxx0AzsHzYecPwF7aIzcmLMOcP2wFUE
16+
D5+WzM1wuPw3O1EBcki4HwNDup+1cs1ffvr/UI10QXzejIuMH+yAoD6EFGoynmO1
17+
VQ3T4OoaPLKvFAwew4ki+F/uQNByGJgWLPAxAgMBAAGjZjBkMA4GA1UdDwEB/wQE
18+
AwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwFAYDVR0RBA0wC4IJ
19+
bG9jYWxob3N0MB0GA1UdDgQWBBQoFawSkraSN9FoDowkPKtAqy5fAjANBgkqhkiG
20+
9w0BAQUFAAOCAQEAftfm4Quni6FIsQdN3DG2CTd48RVmRBttHLRfvbmwPZUJ+nBa
21+
5UXfCszTdd2ZVW5EmgP7saL6KdnKRUDE30M3m8cNULkTF7fy9BPhvU1aVfRBs7OS
22+
RBIN9RxUS4gKFvukMSsmxeetyezgxxotDKpfyUItqzQHpxa+VYWkOzfjxzwhnqHO
23+
pINOes3LNpxvYGJlcqjBigdaai4tzJLnmBDuCLFRTzB90VgQ37lcdQzz+w5/SM81
24+
kIYeQ7P4wN2EUvdGpUklc/BF8raSYmPBsoXGQo/JIDa/3/dgz5STmsSJz0RruZXW
25+
tdJXayKW5Us89uqyYQbw/rsKiQhvcksE5z8PvQ==
26+
-----END CERTIFICATE-----
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Bag Attributes
2+
localKeyID: 01 00 00 00
3+
friendlyName: te-6d866b05-d9bc-4952-b160-8f021585acf1
4+
Microsoft CSP Name: Microsoft Strong Cryptographic Provider
5+
Key Attributes
6+
X509v3 Key Usage: 10
7+
-----BEGIN PRIVATE KEY-----
8+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDiTNRDz8hGr+Dw
9+
OHcwB8pmCxLlI5WTGMkanHRGYgI3hgn4bUeoqVttwVdOhsMUSnOo837LfS4eEVsW
10+
QaTrLnqHqcNjrgjeGADcySA8fuckyRB9vuEl7d76n60hIyqkPVXY2F+vGvYdFAYK
11+
L2TKFyIN+fm7Z+adAfaHSIjqjcB81ECwig6+mt18JmabfxkG+wd3MG7zhA1ypqHL
12+
sKzTKxxx0AzsHzYecPwF7aIzcmLMOcP2wFUED5+WzM1wuPw3O1EBcki4HwNDup+1
13+
cs1ffvr/UI10QXzejIuMH+yAoD6EFGoynmO1VQ3T4OoaPLKvFAwew4ki+F/uQNBy
14+
GJgWLPAxAgMBAAECggEBAKyN0Lz0Ts43hdkl9RvWlOpCP2IhRAgpug5khfS0/uO/
15+
fRLEoQNmP6Ts69mgwFdUfeSx5ljbVrLuoPnTjCEYC64uMCJtra1LuDyhz8bRLQbL
16+
mZuIVL1LJ98KqkS+P+GEM1Vph2xJrqh1gDV79epywTDPDzFrBFlsCcMV9/CBreh/
17+
sNGnMkJs5/1+5alMEI/Zaq+aT7gxa5bO4QGXvcbLM0nqiG06A4r9bpGn3UO6jFu9
18+
0o9eoNm9GKtWhDREPhoco4fCW4a1OV8rE8W3uuE5NXYdcHFDWhFHm9NPcWmnyd6w
19+
yZsZS7c6iECPrQjaeTLYS2Ab8r5925F0XK8E5j7gn5ECgYEA8nAvylHDEhzb/h4N
20+
bk+u2AhH1U3eV70CnprTz8Qir+kRP+c6AQdWZj3IAoGQRjIJQv4HmK/Z7TQ1HOf4
21+
H7Y+UoMynYkb152BDXoC0H05+W5ftEJ4knsJUvyAvlkKa+T46IdBZW8ZvwNfs5Io
22+
7ojNyBm90mPPWA7bs54KV97AjXsCgYEA7vWJxFhbEihw0EICiDV0FvYQtJnb3rD5
23+
+aOxcf+OjOzrNuEazyPYNIixYZZX+m5ok8GSro6J/IVkJ+c8vZx0CopEfONQItYC
24+
OqsEHh28QLQxGLmFe0fcaifrDngcT4ubP7U5rAoT4orLCR6mrfiSIl7cQ/YixTJo
25+
Au4nWK8460MCgYEA6vc6Ci8WDY701CQSRlBqF6xm2l++13Azgr5x/NKN/8m2UyXq
26+
PKb84NiN6Yfi6XWDLm9/s0bzwoav11UnUKzTWCsZuj7xQha/xezzn1dPAeUsUkt+
27+
ChG+5rQcnt8zT4C6kLrN8d4sqMk/To2gqBbkwkPYinj7ss+rtTi9s44LNtMCgYBa
28+
eAOMnpb8LOtpLVSgFPy6pLZ2abngechqRxsrOcHSgPAceuUXf06ftRDTDYSJf7uA
29+
FU1fYP/E5wugP9+zOcSFKQv87GKuja+SXqTUchWPuajM35A1uGMunaZUeAzf4M8K
30+
M6Z+B+K3ZiywazZXr0BEWv2xjUJkvky6r0eeP9ig2QKBgQDW6XdOL9F7y1wa9aoC
31+
ssRFOm7WN4yBbj0F+1/q+SvLVeyVOgNsV6NpeAJDysEeNO55V93DhKFnXDOstd6K
32+
E9KiSzo4OdDm6OD1u7E21zeJx6GxohnnbZ6Z7CC7dLMTtReHVsA1uQjtLtVe8iSb
33+
/vggr5LURJZV+dg/UY7O50gcnw==
34+
-----END PRIVATE KEY-----
2.56 KB
Binary file not shown.

CreateSelfSignedCert.ps1

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Write-Host "Generate a new self-signed certificate"
2+
$cert = New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My -DnsName "localhost" -KeySpec KeyExchange
3+
4+
# Export the certificate as a PFX file
5+
$certPath = "C:\certs\bc-erabliereapi-connector-certificate.pfx"
6+
$certPassword = ConvertTo-SecureString -String "1234" -Force -AsPlainText
7+
Export-PfxCertificate -Cert $cert -FilePath $certPath -Password $certPassword
8+
9+
# Export the certificate as a CER file
10+
$certPath = "C:\certs\bc-erabliereapi-connector-certificate.cer"
11+
Export-Certificate -Cert $cert -FilePath $certPath

erabliereapi/APIWebService.al

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,52 +47,71 @@ codeunit 50126 "API Web Service"
4747
RequestMessage: HttpRequestMessage;
4848
RequestContent: HttpContent;
4949
TokenOutStream: OutStream;
50+
Cert: Record "Isolated Certificate";
51+
ClientAssertion: Text;
52+
HttpAuthUtils: Codeunit "HttpAuthUtils";
5053
begin
5154
//Create webservice call
5255
RequestMessage.Method := 'POST';
56+
57+
if ApiSetup.Audiance = '' then
58+
Error('Please set the audiance parameter in the EAPI Setup Page. Ex: https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token');
59+
5360
RequestMessage.SetRequestUri(ApiSetup.Audiance);
5461

5562
//Create webservice header
5663
RequestMessage.GetHeaders(RequestHeader);
5764

58-
//Payload needed? This might as well be a different implementation!
59-
//It's just an example where the credentials are stored as a json payload
65+
if ApiSetup."Client Id" = '' then
66+
Error('Please set the Client Id parameter in the EAPI Setup Page');
67+
68+
if (ApiSetup."Client Secret" = '') and (ApiSetup."Client Certificate" = '') then
69+
Error('Please set the Client Secret or Client Certificate parameter in the EAPI Setup Page');
70+
71+
if (ApiSetup.Scope = '') then
72+
Error('Please set the Scope parameter in the EAPI Setup Page');
73+
74+
if ApiSetup."Authentication Method" = ApiSetup."Authentication Method"::"Client Secret" then begin
75+
AuthPayload := 'grant_type=client_credentials&client_id=' + ApiSetup."Client Id" + '&client_secret=' + ApiSetup."Client Secret" + '&scope=' + ApiSetup."Scope";
76+
end
77+
else begin
78+
Cert := HttpAuthUtils.GetCertificate(ApiSetup."Client Certificate", ApiSetup."Client Certificate Password");
6079

61-
//Create json payload
62-
JObjectRequest.Add('client_id', ApiSetup."Client Id");
63-
JObjectRequest.Add('client_secret', ApiSetup."Client Secret");
64-
JObjectRequest.WriteTo(AuthPayload);
80+
ClientAssertion := HttpAuthUtils.ComputeClientAssertion(ApiSetup, Cert);
81+
82+
AuthPayload := 'grant_type=client_credentials&client_id=' + ApiSetup."Client Id" + '&Scope=' + ApiSetup.Scope + '&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=' + ClientAssertion
83+
end;
6584

66-
//Get Request Content
6785
RequestContent.WriteFrom(AuthPayload);
6886

6987
RequestContent.GetHeaders(RequestHeader);
88+
RequestHeader.Add('charset', 'UTF-8');
7089
RequestHeader.Remove('Content-Type');
71-
RequestHeader.Add('Content-Type', 'application/json');
90+
RequestHeader.Add('Content-Type', 'application/x-www-form-urlencoded');
7291

7392
RequestMessage.Content := RequestContent;
7493

7594
//Send webservice query
7695
WebClient.Send(RequestMessage, ResponseMessage);
7796

78-
if ResponseMessage.IsSuccessStatusCode() then begin
79-
ResponseMessage.Content().ReadAs(ResponseText);
97+
ResponseMessage.Content().ReadAs(ResponseText);
8098

99+
if ResponseMessage.IsSuccessStatusCode() then begin
81100
if not JObjectResult.ReadFrom(ResponseText) then
82-
Error('Error Read JSON');
101+
Error('API Web Service Error. Error Read JSON');
83102

84103
TokenResponseText := GetJsonToken(JObjectResult, 'access_token').AsValue().AsText();
85-
TokenExpiry := GetJsonToken(JObjectResult, 'expiry_date').AsValue().AsDateTime();
104+
TokenExpiry := CurrentDateTime() + GetJsonToken(JObjectResult, 'ext_expires_in').AsValue().AsInteger();
86105

87106
end else
88-
Error('Webservice Error');
107+
Error('API Web Service Error. Reason: %1 Message: %2', ResponseMessage.ReasonPhrase, ResponseText);
89108

90109
exit(TokenResponseText);
91110
end;
92111

93112
local procedure GetJsonToken(JsonObject: JsonObject; TokenKey: Text) JsonToken: JsonToken;
94113
begin
95114
if not JsonObject.Get(TokenKey, JsonToken) then
96-
Error(StrSubstNo('Token %1 not found', TokenKey));
115+
Error(StrSubstNo('API Web Service Error. Token %1 not found', TokenKey));
97116
end;
98117
}

erabliereapi/EAPISetup.al

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,45 @@ table 50125 "EAPI Setup"
2020
{
2121
DataClassification = CustomerContent;
2222
}
23+
field(13; "ErabliereAPI Client Id"; Text[39])
24+
{
25+
DataClassification = CustomerContent;
26+
}
2327
field(12; "Scope"; Text[150])
2428
{
2529
DataClassification = CustomerContent;
2630
}
2731
field(15; "Client Secret"; Text[150])
2832
{
2933
DataClassification = CustomerContent;
34+
35+
trigger OnValidate()
36+
begin
37+
if (Rec."Client Certificate" = '') and (Rec."Client Secret" <> '') then
38+
Rec."Authentication Method" := Rec."Authentication Method"::"Client Secret";
39+
end;
3040
}
3141
field(16; "Client Certificate"; Text[150])
3242
{
3343
DataClassification = CustomerContent;
44+
45+
trigger OnValidate()
46+
var
47+
CertificateManagement: Codeunit "Certificate Management";
48+
Cert: Record "Isolated Certificate";
49+
begin
50+
if (Rec."Client Certificate" = '') and (Rec."Client Secret" = '') and Cert.FindFirst() then
51+
Rec."Client Certificate" := Cert.Code;
52+
end;
53+
}
54+
field(17; "Client Certificate Password"; Text[150])
55+
{
56+
DataClassification = CustomerContent;
57+
}
58+
field(18; "Authentication Method"; Option)
59+
{
60+
DataClassification = CustomerContent;
61+
OptionMembers = "Client Secret","Client Certificate";
3462
}
3563
field(20; "API Token"; Blob)
3664
{

erabliereapi/EAPISetupList.al

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
page 50134 "EAPI Setup List"
2+
{
3+
PageType = List;
4+
UsageCategory = Lists;
5+
SourceTable = "EAPI Setup";
6+
CardPageId = "EAPI Setup List";
7+
8+
layout
9+
{
10+
area(content)
11+
{
12+
repeater("EAPI Setup")
13+
{
14+
field(Code; Rec.Code)
15+
{
16+
ApplicationArea = All;
17+
ToolTip = 'The code of the EAPI setup.';
18+
}
19+
20+
field("API URL"; Rec."API URL")
21+
{
22+
ApplicationArea = All;
23+
}
24+
}
25+
}
26+
}
27+
}

erabliereapi/EAPISetupPage.al

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
page 50132 "EAPI Setup Page"
2+
{
3+
PageType = Card;
4+
ApplicationArea = All;
5+
UsageCategory = Tasks;
6+
SourceTable = "EAPI Setup";
7+
8+
layout
9+
{
10+
area(Content)
11+
{
12+
group(General)
13+
{
14+
field(Code; Rec.Code)
15+
{
16+
ApplicationArea = All;
17+
ToolTip = 'The code of the EAPI setup.';
18+
ShowMandatory = true;
19+
}
20+
field("API URL"; Rec."API URL")
21+
{
22+
ApplicationArea = All;
23+
ShowMandatory = true;
24+
25+
trigger OnValidate()
26+
begin
27+
if Rec."API URL".EndsWith('/') then
28+
Rec."API URL" := Rec."API URL".Substring(0, StrLen(Rec."API URL") - 1);
29+
end;
30+
}
31+
field("Client Id"; Rec."Client Id")
32+
{
33+
ApplicationArea = All;
34+
ShowMandatory = true;
35+
}
36+
field("Audiance"; Rec."Audiance")
37+
{
38+
ApplicationArea = All;
39+
ShowMandatory = true;
40+
}
41+
field("Scope"; Rec."Scope")
42+
{
43+
ApplicationArea = All;
44+
ShowMandatory = true;
45+
}
46+
field("Client Secret"; Rec."Client Secret")
47+
{
48+
ApplicationArea = All;
49+
}
50+
field("Client Certificate"; Rec."Client Certificate")
51+
{
52+
ApplicationArea = All;
53+
}
54+
field("Client Certificate Password"; Rec."Client Certificate Password")
55+
{
56+
ApplicationArea = All;
57+
}
58+
field("Authentication Method"; Rec."Authentication Method")
59+
{
60+
ApplicationArea = All;
61+
}
62+
field("API Token"; Rec."API Token")
63+
{
64+
ApplicationArea = All;
65+
}
66+
field("Token valid until"; Rec."Token valid until")
67+
{
68+
ApplicationArea = All;
69+
}
70+
}
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)