Top
Pawel Czerwinski Unsplash

Securing responses from Actionable Messages


In my previous post I guided you through a list of steps required to build, send and handle response from Adaptive Cards as Actionable Messages in Outlook. Let me now tell you, how to secure the response.

Basically, the response from Adaptive Card sent as Actionable Message can be easily “hacked”. In its definition we define the “Url” attribute that tells the form where to send data. If a message is forwarded to another person in tenant or if someone builds a form that submits data to the same endpoint as our message, it will accept anything when not having any kind of verification mechanism. Therefore our solution should be prepared to identify source and terminate further execution in case of attempted fraud.

If the card is used for real enterprise purposes, than you could use mechanism called “signing cards”, so that using your private key you need to sign payload and then using public verify the response. This uses DKIM and SPF standards: https://docs.microsoft.com/en-us/outlook/actionable-messages/security-requirements#action-authorization-header. What if we need something simpler?

See it in action!

Action-Authorization header to the rescue

Each response from Actionable Message contains a header key Action-Authorization. It looks like a regular Bearer token, but it’s not. This is JSON Web Signature (JWS).

Important! Action-Authorization Bearer token cannot be used to call eg. GraphAPI to get user’s profile. It will always return 401, because this is not a valid AAD Bearer token.

Contents of the key are base64 encoded JSON objects. Once you split it using “.”, there are three parts (according to RFC7515 standard):

  1. JWS header
  2. JWS payload
  3. JWS signature

JWS header

Decode it using the following expression: decodeBase64(split(replace(triggerOutputs()['headers']?['Action-Authorization'], 'Bearer ', ''), '.')?[0])

{"typ":"JWT","alg":"RS256","kid":"RfKI6h2uhxp6rHwux79UcFh","x5t":"RfKI6h2uhxp6rHwux79UcFh","issloc":"AM0PR10MB","sqid":63734169909382}

Defines the JWT (JSON Web Token) type used. This one is not very much useful for validation purposes.

JWS payload

Decode it using the following expression: decodeBase64(concat(split(replace(triggerOutputs()['headers']?['Action-Authorization'], 'Bearer ', ''), '.')?[1], '=='))

{
  "iat":1598827285,
  "ver":"STI.ExternalAccessToken.V1",
  "appid":"48af08dc-f6d2-435f-b2a7-069abd99c086",
  "sub":"WHO SUBMITTED CARD",
  "appidacr":"2",
  "acr":"0",
  "tid":"32443e19-8cfe-4e6b-a3fe-e75c0e09c7a8",
  "sender":"WHO SENT CARD",
  "oid":"e4e6b40f-f698-403e-bc8e-252a2d52f22c",
  "iss":"https://substrate.office.com/sts/",
  "aud":"https://prod-135.westeurope.logic.azure.com",
  "exp":1598828185,
  "nbf":1598827285
}

Basically when sending an Actionable Message to a user and waiting for their response you should store basic information somewhere to compare it once response is received, for example: CDS or SharePoint. Then you could compare:

  • appid – this is the ID of the app that issued token, in case of Outlook it should always be 48af08dc-f6d2-435f-b2a7-069abd99c086.
  • sub – this is going to be an e-mail of user who clicked to submit the response.
  • sender – this is going to be an e-mail of the account used to send Actionable Message.
  • aud – url of the Logic Apps instance used to execute action.
  • iat – when payload was signed (UNIX timestamp)
  • exp – when signature expires (UNIX timestamp)

So in this case you can compare if appid and sub values are as expected. More: https://docs.microsoft.com/en-us/outlook/actionable-messages/security-requirements#verifying-that-requests-come-from-microsoft.

CorrelationId and originator in Adaptive Card

What can also be used, even harder to hack, is to use CorrelationId + originator attributes in Adaptive Cards. You can generate your custom GUID (eg. using guid() expression in Power Automate), save it in CDS or SharePoint, then set it as a value of the CorrelationId attribute:

{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.0",
  "hideOriginalBody": true,
  "originator": "f6f1d226-d6bb-409e-853e-0747fd61234",
  "correlationId": "@{guid()}",

Once the response is received, simply compare values of the originator property with the registered provider guid and saved CorrelationId with Card-Correlation-Id value from header: triggerOutputs()['headers']?['Card-Correlation-Id'] (source: https://docs.microsoft.com/en-us/outlook/actionable-messages/adaptive-card#additional-properties-on-the-adaptivecard-type).

Service-specific tokens

What Microsoft is also recommending is to add custom generated tokens added to the target URL defined in Adaptive Card’s actions, eg. https://contoso.com/approve?requestId=abc&serviceToken=a1b2c3d4e5

More on this: https://docs.microsoft.com/en-us/outlook/actionable-messages/security-requirements#verifying-the-identity-of-the-user.

I hope that the above steps will help you build your Actionable Messages and Adaptive Cards based solution and secure them properly. If you have questions, feel free to ask them in the comments below.


Tomasz Poszytek

Hi, I am Tomasz. I am expert in the field of process automation and business solutions' building using Power Platform. I am Microsoft MVP and Nintex vTE.

No Comments

Post a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.