Programming for Trusted Application API
- Authentication and Adhoc Online Meetings (posted 04/13/2017)
- Messaging and Webhook (Here)
In my previous post, I described what is Trusted Application API and how to use it with the simple example (online meeting examples).
In this post, I describe the peer to peer instant messaging by outgoing invitation with Trusted Application API. (Some other scenarios are not available in current preview.)
In this scenario, you must handle the incoming webhook (callback).
Preparation (Setting-up)
Before building your application, please be sure to register your trusted endpoint as I previously explained. (Here I don’t describe about steps and please see the previous post.)
In this scenario, make sure to setup the reachable callback endpoint (in which the application must be hosted) as reply url in Azure AD application settings. (If not, go to Azure Portal and add your reply url as follows.)
http://i1155.photobucket.com/albums/p551/tsmatsuz/20170420_Set_ReplyUrl_zpsch3bwew0.jpg
HTTP Flow
Now let’s start to see the http flow for the instant messaging (chat) scenario.
Later I describe how to build your application with ASP.NET Web API (C#) and SDK, but this flow could help you for programming with other languages like PHP, Ruby, Python, etc.
First your application must authenticate in Azure Active Directory (Azure AD) with your client credential (app id and secret) and get the resource endpoints. This HTTP flow is the same as my previous post, and I don’t describe these steps in this post.
Now I show you again the retrieved resource endpoints as follows. In this post, we use the following “startMessaging” (bold fonts) resource endpoint.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"_links": {
"self": {
"href": "/platformservice/v1/applications/3877116191?endpointId=sip%3atrustedapidemo01%40mod776816.onmicrosoft.com"
},
"service:anonApplicationTokens": {
"href": "/platformservice/v1/applications/3877116191/anonApplicationTokens?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
}
},
"_embedded": {
"service:communication": {
"_links": {
"self": {
"href": "/platformservice/v1/applications/3877116191/communication?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
},
"service:joinOnlineMeeting": {
"href": "/platformservice/v1/applications/3877116191/communication/onlineMeetingInvitations?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
},
"service:inviteUserToMeeting": {
"href": "/platformservice/v1/applications/3877116191/communication/userMeetingInvitations?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
},
"service:startMessaging": {
"href": "/platformservice/v1/applications/3877116191/communication/messagingInvitations?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
},
"service:startAudioVideo": {
"href": "/platformservice/v1/applications/3877116191/communication/audioVideoInvitations?modalities=AudioVideou0026endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
},
"service:startAudio": {
"href": "/platformservice/v1/applications/3877116191/communication/audioVideoInvitations?modalities=Audiou0026endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
}
},
"rel": "service:communication",
"etag": "4294967295"
},
"myOnlineMeetings": {
"_links": {
"self": {
"href": "/platformservice/v1/applications/3877116191/myOnlineMeetings?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
}
},
"rel": "myOnlineMeetings"
},
"service:adhocMeetings": {
"_links": {
"self": {
"href": "/platformservice/v1/applications/3877116191/adhocMeetings?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com"
}
},
"rel": "service:adhocMeetings"
}
},
"rel": "service:application"
}
With this resource endpoint, your application can invite the user for the conversation as follows. (Be sure that this application must be consented in your tenant beforehand.)
Note that the location header in the HTTP response is important, and we use this later.
POST https://ring2noammeetings.resources.lync.com/platformservice/v1/applications/3877116191/communication/messagingInvitations?endpointId=sip:trustedapidemo01@mod776816.onmicrosoft.com
Authorization: Bearer eyJ0eXAiOi...
Content-Type: application/vnd.microsoft.com.ucwa+json; charset=utf-8
{
"operationId": "5eb94c67-0162-4be9-8984-a511befea7fa",
"to": "sip:BenW@MOD776816.onmicrosoft.com",
"subject": "Hello ! I am bot.",
"callbackUrl": "https://example.com/callback"
}
HTTP/1.1 201 Created
Cache-Control: no-cache
Location: /platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/messagingInvitations/ff49fa23-3f8e-442b-9b9f-ef2274bd93f2?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com
When this HTTP request is sent, the user receives the following messaging invitation in the Skype for Business client.
http://i1155.photobucket.com/albums/p551/tsmatsuz/20170420_Receive_Invitation_zpsmzdmy9l3.jpg
After sending the invitation, your application’s callback (webhook) endpoint is asynchronously called by the trusted application platform as follows, and the bi-directional communication (strictly speaking, two legs on one-way communication by http) between your application and the trusted application platform starts !
As you can see, this callback is including the base uri (baseuri
) for this generated conversation. When your application wants to send some request to the user, this baseuri
must be used as host for HTTP request and previously returned location header (/platformservice/tgt-c468527635...
) must be used as path. (That is, the request url is https://webpoolbl20r04.infra.lync.com/platformservice/tgt-c468527635...
)
Note : Later I will explain about the following authorization header in this incoming HTTP request. (This header is used for your verification.)
POST https://example.com/callback
Authorization: Bearer eyJ0eXAiOi...
Content-Type: application/json; type="application/vnd.microsoft.com.ucwa+json"; charset=utf-8
{
"_links": {
"self": {
"href": "/eventsservice"
}
},
"sender": [
],
"baseuri": "https://webpoolbl20r04.infra.lync.com/platformservice/v1/applications"
}
HTTP/1.1 204 No Content
For example, the following is sending “Hello World!” message to the user.
POST https://webpoolbl20r04.infra.lync.com/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/messaging/messages?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com
Authorization: Bearer eyJ0eXAiOi...
Content-Type: text/plain; charset=utf-8
Hello World!
HTTP/1.1 201 Created
Your application frequently receives the conversation state or results as the webhook callback, and your application must handle these events properly. (Some of these events are important for your application.)
For example, the following is the “started” event for the messaging invitation. If the messaging invitation is successfully accepted by the user, “completed” (status “success”) event for the messaging invitation will be arrived.
The following is the example of one sender and one event in one HTTP request. But it might include several senders (communication, conversation, etc) and several types (“added”, “updated”, “completed”, etc) of events in one request, and your application must parse these events.
POST https://example.com/callback?eventChannelId=fd64c8e8-1bae-4691-b3cc-d17ffc46bd20&ack=1
Authorization: Bearer eyJ0eXAiOi...
Content-Type: application/json; type="application/vnd.microsoft.com.ucwa+json"; charset=utf-8
{
"_links": {
"self": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/events?ack=1u0026key=3971484462%7Esip%3Atrustedapidemo02%40mod776816.onmicrosoft.com%7Efd64c8e8-1bae-4691-b3cc-d17ffc46bd20"
},
"next": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/events?ack=2u0026key=3971484462%7Esip%3Atrustedapidemo02%40mod776816.onmicrosoft.com%7Efd64c8e8-1bae-4691-b3cc-d17ffc46bd20"
}
},
"sender": [
{
"rel": "service:communication",
"href": "/platformservice/v1/applications/3971484462/communication/id/ff49fa23-3f8e-442b-9b9f-ef2274bd93f2?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com",
"events": [
{
"link": {
"rel": "service:messagingInvitation",
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/messagingInvitations/ff49fa23-3f8e-442b-9b9f-ef2274bd93f2?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
},
"_embedded": {
"service:messagingInvitation": {
"direction": "Outgoing",
"state": "Connecting",
"operationId": "f5d5f0ca-7da2-438d-83c2-91a32343ef94",
"importance": "Normal",
"subject": "Hello ! I am bot.",
"to": "sip:BenW@MOD776816.onmicrosoft.com",
...
}
},
"type": "started"
}
]
}
],
"baseuri": "https://webpoolbl20r04.infra.lync.com"
}
HTTP/1.1 204 No Content
For example, when you have received the incoming instant message (chat message) from the user, the following event is received. Here we’re assuming that the user is sending the following html message.
<span style="font-size:10pt;text-align:left;">Hi Bot.I am fine.</span>
POST https://example.com/callback?eventChannelId=fd64c8e8-1bae-4691-b3cc-d17ffc46bd20&ack=5
Authorization: Bearer eyJ0eXAiOi...
Content-Type: application/json; type="application/vnd.microsoft.com.ucwa+json"; charset=utf-8
{
"_links": {
"self": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/events?ack=5u0026key=3971484462%7Esip%3Atrustedapidemo02%40mod776816.onmicrosoft.com%7Efd64c8e8-1bae-4691-b3cc-d17ffc46bd20"
},
"next": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/events?ack=6u0026key=3971484462%7Esip%3Atrustedapidemo02%40mod776816.onmicrosoft.com%7Efd64c8e8-1bae-4691-b3cc-d17ffc46bd20"
}
},
"sender": [
{
"rel": "service:conversation",
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com",
"events": [
{
"link": {
"rel": "service:message",
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/messaging/messages/3?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
},
"status": "Success",
"_embedded": {
"service:message": {
"direction": "Incoming",
"timeStamp": "/Date(1492596653859)/",
"_links": {
"self": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/messaging/messages/3?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
},
"service:participant": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/participants/benw@mod776816.onmicrosoft.com?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com",
"title": ""
},
"service:messaging": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/messaging?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
},
"htmlMessage": {
"href": "data:text/html;charset=utf-8,%3cspan+style%3d%22font-size%3a10pt%3btext-align%3aleft%3b%22%3eHi%20Bot.I%20am%20fine.%3c%2fspan%3e"
}
},
"_embedded": {
"service:senderParticipant": [
{
"uri": "sip:BenW@MOD776816.onmicrosoft.com",
"name": "",
"_links": {
"self": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/participants/benw@mod776816.onmicrosoft.com?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
},
"service:conversation": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
},
"service:participantMessaging": {
"href": "/platformservice/tgt-c4685276352c52b0a832c02a664d91ee/v1/applications/3971484462/communication/conversations/fd64c8e8-1bae-4691-b3cc-d17ffc46bd20/participants/benw@mod776816.onmicrosoft.com/messaging?endpointId=sip:trustedapidemo02@mod776816.onmicrosoft.com"
}
},
"rel": "service:participant"
}
]
},
"rel": "service:message"
}
},
"type": "completed"
}
]
}
],
"baseuri": "https://webpoolbl20r04.infra.lync.com"
}
HTTP/1.1 204 No Content
Verifying incoming webhook requests
When you receive the webhook, your application must verify the access token (see the following) in HTTP header.
If you ignore this verification process, your application might be called (executed) by the malicious code.
POST https://example.com/callback?...
Authorization: Bearer eyJ0eXAiOi...
Content-Type: application/json
...
How to verify ?
I show you the brief steps as follows. (I don’t describe the background of identity technologies here…)
- The authorization header is JWT formatted, i.e, which is the dot (.) delimited and base64 url encoded token.
First you should parse this token, and retrieve the claims and signature. - Next you must retrieve public key from
https://login.microsoftonline.com/common/discovery/keys
. (You can get this url by{issuer url}/.well-known/openid-configuration
. Here it’shttps://sts.windows.net/3bc5ea6c-9286-4ca9-8c1a-1b2c4f013f15/.well-known/openid-configuration
.) - Verify the signature (which is retrieved by step 1) with public key in step 2.
The following is the PHP example of this verification (validation) processing.
This code guarantees that access token is surely issued by Azure AD, and the included claims are not tampered by the malicious code.
<?php
echo "The result is " . validate_token("eyJ0eXAiOi...");
// return 1, if token is valid
// return 0, if token is invalid
function validate_token($access_token) {
$res = 0;
// 1 create array from token separated by dot (.)
$token_arr = explode('.', $access_token);
$header_enc = $token_arr[0];
$attr_enc = $token_arr[1];
$sig_enc = $token_arr[2];
// 2 base 64 url decoding
$header = json_decode(base64_url_decode($header_enc), TRUE);
$attr = json_decode(base64_url_decode($attr_enc), TRUE);
$sig = base64_url_decode($sig_enc);
// 3 period check
$dtnow = time();
if($dtnow <= $attr['nbf'] or $dtnow >= $attr['exp'])
return $res;
//
// 4 check signature
//
// 4-a get key list
$keylist = file_get_contents('https://login.microsoftonline.com/common/discovery/keys');
$keylist_arr = json_decode($keylist, TRUE);
foreach($keylist_arr['keys'] as $key => $value) {
// 4-b select one key
if($value['x5t'] == $header['x5t']) {
// 4-c get public key from key info
$cert_txt = '-----BEGIN CERTIFICATE-----' . "n" . chunk_split($value['x5c'][0], 64) . '-----END CERTIFICATE-----';
$cert_obj = openssl_x509_read($cert_txt);
$pkey_obj = openssl_pkey_get_public($cert_obj);
$pkey_arr = openssl_pkey_get_details($pkey_obj);
$pkey_txt = $pkey_arr['key'];
// 4-d validate signature
$res = openssl_verify($header_enc . '.' . $attr_enc, $sig, $pkey_txt, OPENSSL_ALGO_SHA256);
}
}
// others check (audience, tenant, etc)
// (This time, skip these code ...)
return $res;
}
// Helper functions
function base64_url_decode($arg) {
$res = $arg;
$res = str_replace('-', '+', $res);
$res = str_replace('_', '/', $res);
switch (strlen($res) % 4) {
case 0:
break;
case 2:
$res .= "==";
break;
case 3:
$res .= "=";
break;
default:
break;
}
$res = base64_decode($res);
return $res;
}
?>
Programming with .NET SDK and ASP.NET Web API
As I showed you in my previous post, you can use the .NET SDK for Trusted Application API programming.
But I note that currently there’s no web-compliant building blocks (some wrapper attributes, project templates, etc) in SDK. As a result, you must dispatch the ASP.NET request/response (webhook, etc) into the existing SDK objects by your own custom code.
Let’s see the simple example.
Note : Here I don’t use any config settings, the tricky technique (IoC, custom attributes, etc), or exception handlings for your easy understanding the main programming logic.
Please modify for the beauty of code and modularity for your real production. (Do not copy this code in your production.)
First, create ASP.NET Web API project with Visual Studio (select .NET Framework version 4.6.2.) and add the following NuGet package in your project.
Microsoft.SkypeforBusiness.TrustedApplicationAPI.SDK
Microsoft.SkypeforBusiness.TrustedApplicationAPI.ResourceContact
Add the following class in App_Start folder.
The following ConfigureTrustedEndpoint
is configuring and initializing SDK, and please replace the app id, secret, app sip uri, and redirect uri.
Note that Microsoft.SfB.PlatformService.SDK.ClientModel.Internal
in the namespace references is needed for setting custom callback url.
using Microsoft.SfB.PlatformService.SDK.Common;
using Microsoft.SfB.PlatformService.SDK.ClientModel;
using Microsoft.SfB.PlatformService.SDK.ClientModel.Internal;
...
public static class TrustedEndpointConfig
{
public static MyEventChannel trustedEndpointEvent { get; set; }
public static ApplicationEndpoint appendpoint { get; set; }
public static void ConfigureTrustedEndpoint()
{
// create platform event
trustedEndpointEvent = new MyEventChannel();
// initialize platform
var platsettings = new ClientPlatformSettings(
"p7bkSP0+P5...",
new Guid("655dbcda-8969-4e9b-b4a1-64bbd22fb42a"));
var platform = new ClientPlatform(
platsettings,
new MyLogger());
var endpointSettings = new ApplicationEndpointSettings(
new SipUri(@"sip:trustedapidemo02@mod776816.onmicrosoft.com"));
appendpoint = new ApplicationEndpoint(
platform,
endpointSettings,
trustedEndpointEvent);
appendpoint.InitializeAsync().Wait();
appendpoint.InitializeApplicationAsync().Wait();
// Set callback url
// (This value is sent to trusted application platform.)
platsettings.SetCustomizedCallbackurl(
new Uri(@"https://example.com/api/callback"));
}
}
public class MyEventChannel : IEventChannel
{
public event EventHandler<EventsChannelArgs> HandleIncomingEvents;
public void HandleIncomingHttpRequest(SerializableHttpRequestMessage reqmsg)
{
HandleIncomingEvents.Invoke(
this,
new EventsChannelArgs(reqmsg));
}
public Task TryStartAsync()
{
return TaskHelpers.CompletedTask;
}
public Task TryStopAsync()
{
return TaskHelpers.CompletedTask;
}
}
public class MyLogger : IPlatformServiceLogger
{
public bool HttpRequestResponseNeedsToBeLogged { get; set; }
public void Information(string message)
{
Debug.WriteLine($"[PLAT INFO]{message}");
}
public void Information(string fmt, params object[] vars)
{
Debug.WriteLine($"[PLAT INFO]{string.Format(fmt, vars)}");
}
public void Information(Exception exception, string fmt, params object[] vars)
{
Debug.WriteLine($"[PLAT INFO]{string.Format(fmt, vars)}");
}
public void Warning(string message)
{
Debug.WriteLine($"[PLAT WARN]{message}");
}
public void Warning(string fmt, params object[] vars)
{
Debug.WriteLine($"[PLAT WARN]{string.Format(fmt, vars)}");
}
public void Warning(Exception exception, string fmt, params object[] vars)
{
Debug.WriteLine($"[PLAT WARN]{string.Format(fmt, vars)}");
}
public void Error(string message)
{
var res = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent($"[PLAT ERR]{message}")
};
throw new HttpResponseException(res);
}
public void Error(string fmt, params object[] vars)
{
var res = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent($"[PLAT ERR]{string.Format(fmt, vars)}")
};
throw new HttpResponseException(res);
}
public void Error(Exception exception, string fmt, params object[] vars)
{
var res = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent($"[PLAT ERR]{string.Format(fmt, vars)}")
};
throw new HttpResponseException(res);
}
}
Please add the following line (bold fonts) in Global.asax.cs.
The previous config settings is triggered by this code.
...
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
TrustedEndpointConfig.ConfigureTrustedEndpoint();
}
...
Now let’s invite the user for messaging conversation.
Here we’ve created the following action in HomeController. If /Home/Invitation
is accessed, the invitation to BenW@MOD776816.onmicrosoft.com
will be sent.
Moreover, if this application receives the incoming instant messaging from BenW, the outgoing message “Received {received text}” to BenW will be sent back by this application. (Here we have used Html Agility Pack for parsing html string.)
using Microsoft.SfB.PlatformService.SDK.Common;
using Microsoft.SfB.PlatformService.SDK.ClientModel;
...
public class HomeController : Controller
{
public async Task<ActionResult> Invitation()
{
var invitation =
await TrustedEndpointConfig.appendpoint.Application.Communication.StartMessagingAsync(
"Hello ! I am bot.",
new SipUri(@"sip:BenW@MOD776816.onmicrosoft.com"),
null,
null);
var conv =
await invitation.WaitForInviteCompleteAsync();
conv.MessagingCall.IncomingMessageReceived += (s, e) =>
{
IMessagingCall msg = (IMessagingCall)s;
IncomingMessageEventArgs arg = e;
string receiveText = string.Empty;
if (arg.PlainMessage != null)
{
receiveText =
Encoding.UTF8.GetString(arg.PlainMessage.Message);
}
else
{
// retrieve plain text from html
var htmldoc = new HtmlAgilityPack.HtmlDocument();
string receiveHtml =
Encoding.UTF8.GetString(arg.HtmlMessage.Message);
htmldoc.LoadHtml(receiveHtml);
foreach (var node in htmldoc.DocumentNode.DescendantNodesAndSelf())
{
if (!node.HasChildNodes)
{
receiveText = node.InnerText;
}
}
}
msg.SendMessageAsync($"Received {receiveText}");
};
return View();
}
}
The previous event handler (IncomingMessageReceived
) must be triggered by the programming code. See the following CallbackController
.
This POST method notifies the incoming request to the previously created event handler (IEventChannel
), when this webhook (https://{this application}/api/callback
) is called by the trusted application platform. Eventually IncomingMessageReceived
(see above) is triggered by IEventChannel
.
using Microsoft.SfB.PlatformService.SDK.Common;
using Microsoft.SfB.PlatformService.SDK.ClientModel;
...
public class CallbackController : ApiController
{
public async Task Post(HttpRequestMessage request)
{
var reqmsg = new SerializableHttpRequestMessage();
await reqmsg.InitializeAsync(request, null);
TrustedEndpointConfig.trustedEndpointEvent.HandleIncomingHttpRequest(reqmsg);
}
}
Let’s run this application.
When you access to /Home/Invitation in your web browser, the instant messaging (chat) starts with BenW.
When BenW sends the messages to this application, the application replies (echos) like the following screenshot.
You can refer the several sample code for currently available scenarios of Trusted Application API in the following Github repo.
Reference : Trusted Application API Quick Start Samples (Github)
https://github.com/OfficeDev/skype-docs/tree/master/Skype/Trusted-Application-API/samples/QuickStartSamples