Monday, 30 March 2020

Building a Multi-tenant Microsoft 365 App Part 3: Integrate with payment provider

So this is the final part of the thinking out loud type of a guide to build a multi-tenant Microsoft 365 App, if your app is not free you can provide either a one-off license , per user license or per tenant license, I'm not going to go through all the options in details.

In the old world when I used to publish apps on Office SellerDashboard now AppSource via developer dashboard then now Partner centre. There is always two types of apps or actually three, from the store perspective 2 types (paid and free). Most ISVs will use the free option on Microsoft store and they implement their own license model which allow them to decouple it from Microsoft's existing model also they can offer different types of licenses etc..

For me I used to piggyback on Microsoft's licensing model. Why ? firstly I was building mainly free apps and putting it out there for people to use and I only have a single Outlook Add-in I have made paid for just doing a simple market test back in 2015, so I used Microsoft licensing model where I rely on Microsoft licensing etc.

now with Microsoft changing the model as in my humble opinion most ISVs already using their own license model and the current model is relatively a major overhead or maybe some other reason but this is my opinion. So relying on Microsoft to handle your M365 app is not an option, so you better off listing the app as Free and have your own integration with a payment gateway.


My intention was to use a provider that fits both recurring payments (subscription based model) and one off. and most importantly because I'm a very lazy developer something that requires minimum effort.

so I decided to use Stripe and Stripe checkout for the following reasons:

  1. Easy setup , I'm not going through how to setup products and plans, there are heaps of documentation on Stripe website that could provide more detailed and better explanation 
  2. Minimum code (JS mostly, you can even copy the code from Stripe dashboard) 
in less than 10 minutes you can have a stripe checkout integrated to your application, but is it that easy ? the answer is not really when the user click checkout it doesn't guarantee that the payments is correctly processed you have two options here:

  1.  activate your premium features asap, and when the payment is not confirmed remind the user and try again later (which is great allowing user to test your app and maybe pay later)
  2. Switch off your premium features until a successful payment is being processed.
So how can I link the user M365 tenant to the newly created stripe subscription, maybe i need to know if another user within the same tenant has purchased a tenant wide subscription. Stripe checkout offers minimal functionality to customise the checkout data passed, but you can pass tenant ID or user ID if you are offering per user subscription as clientReferenceId as below



the main question is : how to make sure that you know when Stripe has processed payment or not in this scenario ?

Stripe Web-hooks to the rescue 

You can register a set of strip web-hooks to make sure that you listen to Stripe events, in my case I have created an Azure Function (Http triggered) that queue Stripe requests in a queue that have another Azure Function that process the requests, the following is a snapshot listing the events I subscribed to


You might need to subscribe to other set of events, read the documentation carefully but this is what I've selected 

Why 2 Azure Functions?
You might need to sequentially process some of the events prior to the others and because you don't control the order that stripe call your registered webhook it's better to pass them all via a single pipeline.

In my case I was storing stripe events data in a table which lists subscription status and another one for invoices, these two tables are in the "common" storage account , you can decide to move them to specific storage account but you won't be able to completely wipe storage account data without archiving these.

I got side tracked, so I needed to process the CheckoutCompleted event first as it's the only event has the "ClientReferenceID" that allows me to later locate the Tenant and enable/disable the premium features.

in this case I rely on a record of the subscription to be created if I can't find it I'll simply throw exception that makes Azure function retry for 5 times, during this time I would be hopeful that the CheckoutCompleted event is processed.

Is that the best way. Absolutely not but it works and it's super easy the code is below.


Another Thing to Consider if you decided to put these in the common area make sure that you are not breaching any data sovereignty issues.

as you can see the code above ensure that a subscription record has been created first then when invoice processed successfully you can locate your tenant/user object using the clientReferenceID and update it's subscription tier and payment status.

Another thing you need to worry about when your user choosing to change tiers and then you will have to cancel their subscription and create another one, you can choose to continue the billing cycle using the old subscription or prorate the fee to the exact day of switching.

I've been distracted for the past couple of months due to reasons we all know, I hope wherever you are to be safe and I hope we get out of this stronger.

Be Safe
Amr


Saturday, 22 February 2020

Building a Multi-tenant Microsoft 365 App Part 2: Data Partitioning


In this part we will discuss how we can partition different tenant data, in general you should have 2 types of data:
  1. Common Data that should live in a common data storage for this you can use a common database/Azure Storage account, example of this data is Tenant Configuration, admin data like invoicing and subscription status in case you've decided to make your app paid or freemium.
  2. Tenant Specific Data, In this case you can either go with logical segregation (using single azure storage account and use partitioning within the table) However, I'm always in favour of having a physical partitioning. This allows admin to choose data location upon registration. Your tenant registration will not only create some data configuration items in the common data store but it will also attempt to create a separate data store for the newly registered tenant. This can be achieved by adding a message to a storage queue which triggers an Azure Function which will provision the necessary artefact for each tenant.
The main two questions/concerns would be 
  1. Where shall I store access to these various storage accounts? and  
  2. How can i distinguish between tenant requests if I will be using the same services? (physical segregation for the API layer is an overkill and not necessary in my opinion)
The answers for the above questions are pretty simple. First you will need to store the keys for each tenant in a key store like Azure key Vault. For the second one we are very fortunate that tenantId is passed as part of the user claims and you can easily extract it from the claims using the below line of code. 
You can use the tenantId to locate your tenant record , get the tenant storage account key from Azure Key Vault then once you get a reference to the tenant storage account you can do the operation you desire.

Very Important: Don't rely on the client app to send the tenant Id , always get it from the user claims 


In the following parts I'll address some of the concerns that I've faced and how I overcome them, I'm keen to hear your thoughts some of the ideas are pretty basic and I'm open for more sophisticated approaches:
  1. What you shall do when a user decided to delete the registration?  The answer is do you want to keep the organisation data for a while so the admin can restore the data specially if the deletion was by mistake.
  2. What shall I do for data backup and storage account tiers? You can include the storage tier in your tenant registration tiers for example if you offer a free/Basic tier you can use LRS for a higher tiers you can GRS option.
  3. How can my client App access my tenant blob files?  the need for this might arise if you store some static files for each tenant blob and you need you client app to access these files (custom logos/custom images etc..) one easy way is to use a shared access signature another way is to allow your Azure AD to control access to your blob storage.
  4. How can I stop users from abusing the shared storage?  as far as I know and until the time I've last checked (couple of weeks back) I know that there is no storage quota limit per storage account or per blob container. What I was successful to do is to get used capacity per storage account using Azure monitor client this is of course a delayed result but you can get the updated value then store it in your tenant record and always check if the each tenant still have available quota or not. Of course your decision will be delayed as it rely on Azure monitor metric but better than nothing.

In  a nutshell this is a very basic multi-tenant application that uses the simplest form of storage (Azure Storage Account) to segregate each tenant data in a separate physical container if your app is more complex you can use other storage options like cosmos db.

In the Next post I'll go through the simplest method to hook up a payment gateway like stripe to your application.

Monday, 13 January 2020

Building a Multi-tenant Microsoft 365 App Part 1: Authorisation & Authentication


In this series , I'll talk about building a standalone web app that connects to some of your Microsoft 365 workloads, as an example we will assume that we are building a Mutli-tenant app that connects to Azure AD, SharePoint and Planner.

In this post I'll focus on one fundamental part which is authorisation&authentication. From an architectural standpoint we don't have a lot of options to assess specially if we are looking to use some of the existing platform capabilities
  1. Use Azure AD as your main Authentication/Authorisation provider , to get more details about how please read my detailed previous post(http://www.sharepointtweaks.com/2019/11/protecting-your-webapi-using-azure-AD.html)  in which I explained how to use Azure AD to not only connect to existing Microsoft Graph APIs but I also used it to protected a custom API I've built.
  2. The second important thing to consider is permission scope elevation. In other words, when requesting permissions you should start with least privileged set. This is not only a good user experience but it will allow you to  provide partial functionality to normal users even if your full app functionality requires an admin consent. An example, if your app is trying to get users images, it will require Read.Users.All scope which needs an admin consent. If you try to request this scope the app will keep showing the users that it requires admin consent which will make it not usable at all. 
    The solution to this problem could be very simply, don't initially request the scopes that requires admin consent until an admin "configures" your app. The trick would be how you can make sure that your app is configured without risking requesting some write permission to the directory which will make the admin very reluctant to try out your app.  You can do this by creating a configuration record in your app metadata (you can store this configuration anywhere Cosmos DB or even Azure Storage Table), This configuration record can be created as part of multi-step process in which you can ask the admin manually to consent to the add and maybe throwing a button to ease the process. You can simply assume that the admin has configured the app correctly so when your app loads app it checks if there is configuration record it requests the full set of scopes.
  3. The other challenge is how your app would tell whether the current user is an admin or not. This indeed is a tricky one, if you rely on user claims and given that the user could be a member of a lot of groups. Group associations won't appear as part of the token claims . You will require to query user groups maybe multiple times to just check whether the current user is and admin or not. 
    In my case I used a very simplistic approach. I used Azure AD app Roles, I've created a single App Role called "Admin" and I've asked the Microsoft 365 admin to add himself and maybe other people to this group and by adding the attribute  [Authorize(Roles = "Admin")] I can make sure that this endpoint is only available for users with Admin roles.  The drawback of this approach is you have to use direct user/role associations unless the organisation has P1 or P2 Azure AD license.
  4. Using Client-Side Authentication Library like MSAL will definitely make your life easier to implement authentication including login, token acquisition and token refresh. If you are using MSAL within a single page app it might be a bit challenging, if you want to read more about some issues with msal and SPA you can get an idea reading this github issue https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/697  

This would be a wrap for this post, in part 2 we will discuss how to do logically or physically partition tenant data along with some challenges that comes with an easy to provision cloud storage vs. associated consumption cost.

Wednesday, 27 November 2019

Retro: Organising a Multi-city Developer Bootcamp




So, last year was a very special year for me, exactly a year ago I've been thrilled to share with more than 11 MVPs across APAC region the honor of hosting 6 Office Developer Bootcamp, what made last year special is that we continuously kept raising the bar.  Starting in 2017 with the first Global Office 365 Developer Bootcamp we had only three cities: Sydney, Melbourne and Auckland for the first year. For 2018 we decided to increase the count by 100% adding Brisbane, Hong Kong and Kuala Lumpur to the mix.  You might wonder why HK & KL, I will let you guys know a bit later.

I thought - with a little nudge from Shiva to share some of the lesson I've learnt in organizing Office 365 Developer Bootcamp across such diverse cities/communities, it goes as below in no particular order:
1.       Pull an awesome team together:  Having Ashish, Cameron, John, Paul and Chris and many more to support this event was a key to success in both 2017 & 2018.
2.       Plan your trip right: if you decided to do a lot of travelling, you got to plan it right, I remember booking a multi-city trip from Sydney->Auckland->Brisbane then back to Sydney. It helps with the budget and kept the MRs less annoyed.
3.       Remote Event Planning is stressful:  I think we could all agree that it's easier to run an event in your city, for last year I believe the toughest event of all was Kuala Lumpur. The main reason that I had no co-speakers and I had to arrive to Microsoft Malaysia very early in the morning having flew in the night before (the people who knows me very well I'm not a morning person and I can't function properly before 10 am!). I also had to change the room setup to match the desired setup (classroom). I still feel bad for the attendees as I ran the whole event from 8am to 3pm.
This year I'm fortunate to have a whole team in Kuala Lumpur to support the event and almost 200 registrations for the event, I even decided not to travel and do a remote session, that would keep someone very happy!
4.       Keep a dynamic Event Format   This is something I've done completely wrong in the first year, what might be your typical event in one place of the world is completely different in another place as the attendees’ expectation might be completely different. You will have to tailor your content to fit the location, having a local team will definitely point you to the right direction
5.        Do your best to understand the culture differences for almost every city, Friday was our first choice for the event day as we discovered that a weekend full day training events usually have a very high dropout rate. 
That wasn't the case for Kuala Lumpur, although I'm a Muslim with two Mohammeds in my name, I completely forgot that Malaysia is a Muslim majority country and they either have Friday off or half working day, I had to move the date to Tuesday and change my hotel and tickets.


So why Kuala Lumpur and Hong Kong, the reason is very simple, it was easier for me to travel to these cities visa free as I still hold an Egyptian passport!

 I'll leave you guys with some photos from the events





Friday, 8 November 2019

Protecting your your WebAPI using Azure AD


In this post, I will discuss yet another useful feature you can use Azure AD specially if you are building something that uses your Microsoft 365 Identity platform.
let's assume you're building an app that uses some of Microsoft 365 capability and integrates with it via the Graph API also you have your own custom APIs that connects to your custom application, you want to expose your custom application functionality via REST APIs to your app. The trick here is you want to protect your custom build APIs and hopefully manage to have a consistent experience.

In the past I've used IdentityServer to provide this functionality when I used to build fully custom solutions, but for this time I was thinking I was already using Azure AD to connect to MS Graph APIs, what if I can use it to protect my own custom endpoint.

The answer is pretty straight forward, you can easily protect your custom built API using Azure AD or event Azure B2C if you are  building a consumer type app.

  1. You'll need to create an Azure App registration by navigating to your portal.azure.com then going into Azure AD and create a new app.
  2. In the process of creating your new app you can choose whether it's for a single tenant vs. Multi-Tenant or it could be accessed via a consumer account (Microsoft Personal Accounts), in this case for simplicity I'll choose an single tenant option.
  3. After you create the new app, you will find an option called (Expose an API)
  4. In this screen you can define your API scopes and also ensure that your client application which you already have been using to access Microsoft Graph or any other Microsoft cloud endpoints is added as authorised client application.
  5. If you have done all of that you have completed the configuration part, now what you need to do to make your API protected by Azure AD is simple and very well explained in this Github repo https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2
Now your API will be protected by Azure AD, the other challenging part is to figure out scopes/Roles to provide a more mature endpoint authorization, as I mentioned before you can define scopes for your API in Azure AD and then you can use these scopes to protect either a whole controller or a specific action using the AuthorizeForScopes attribute


A very easy straight forward approach I found is to define Application Roles within Azure AD app registration and use Authorize Attribute with Roles. you can add users to specific application role

using the "Enterprise Applications" section of Azure AD and it could be either a direct assignment or your can assign a security group to a role if you have Azure AD P1 or P2 subscription.

Friday, 27 September 2019

Setting up Microsoft Graph Security API Sample


Following up on my previous post which was a very quick intro to building security apps using Microsoft Graph, to get things up and running there is no easier way than finding an app built by someone else that demonstrate some use cases and see it for yourself. a very good start is https://github.com/Microsoft/securitydev which has a sample app that displays your organisation score and list alerts and actions of these alerts.

First let's discuss the components of this sample:
  • An Angular SPA: represents the front end and it does trigger the authentication flow for the user 
  • A set of APIs:  connects to MS Graph security endpoints to collect alert, action and secure score data
  • notification end point: to set up remote endpoint for MS Graph webhook subscriptions and a SignalR enabled web page to display notifications in an interactive manner.

What you need to be able to run this sample?
  1. Azure AD application, can be easily registered following the guide https://docs.microsoft.com/en-us/graph/auth-register-app-v2 please note that the app need to have permission to MS Graph security endpoints as application permission as the security information (alerts, actions and secure store) is accessed by the API endpoint not as user identity
  2. need to replace the client ID and client secret in both appSettings.json and environments.ts files 
  3. If you are running this app locally (development environment) you can either run ng build  manually or preferably added to your visual studio build pipeline, (note if you don't have angular-cli installed you need to install it by simply
  4. Now you can run the app and launch it on the browser, you will be prompted to login in using your Azure AD credentials and you can have a go with the app various pages, there is a security dashboard, alerts, actions, subscriptions and secure scores

Thursday, 29 August 2019

Introduction to Microsoft Graph Security APIs


I don't remember being so excited about something in the past three months since Liverpool won the Champions League Final apart from the announcement of the Microsoft Graph Security APIs, I'm still trying to figure out what is the potential of the APIs but I think having this APIs would open up possibilities to ISVs and independent developer and partners to start simplifying the way the admins/users deal with security alerts and more importantly streamline the alert process across different providers, whether it comes from Microsoft 365 security centre or cloud app security (Azure) or even via a Microsoft vendor/Partners.

I decided to take the APIs for a spin and play around with what they currently offer both in GA (v1.0) and beta, I'm not going to go full-blown approach, so I'll just use the Microsoft Graph explorer to play around with these endpoints, steps are pretty simple
  • Navigate to Graph explorer https://developer.microsoft.com/en-us/graph/graph-explorer and login
  • Make sure you edit permissions and add at least SecurityEvents.Read.All , this will prompt you to re-login and consent to the newly added scopes of "Graph Explorer" Azure AD multi-tenant app.
  • The browser will redirect you back to the graph explorer
  • In the URL textbox type the endpoint under /v1.0/security/alerts , you will get a list of aggregated alerts like the below
    • Unique identifier and also highlight the azure Tenant and subscription, if it's an alert generated by Office 365 security centre 
    • Set of tags based on the configuration of the source system
    • Vendor information
    • User information
    • Severity of the alert (as configured by the originating source)
now let's create a new custom alert policy and see for ourselves how long it will take till the graph security API pick it up
  • Login to your M365 admin portal and click on security, you will either land on protection.office.com or security.microsoft.com based on your subscription  for example if you have a E3 developer Office 365 account you won't be able to use CloudApp security or even add it to your subscription and you will always get redirected to protection.office.com
  • For simplicity we will choose Office 365 alerts if we have landed on security.microsoft.com by clicking on policies then Office 365 alerts 
  • For some other bizarre reason even if you been redirected to protection.office.com via clicking on office 365 alerts you have to choose alerts and alert policies from the left side navigation !

  • Now let's create a new alert policy as below

  • Now I'll navigate to SharePoint site and share it with an external user 
  • after almost a minute or so I've got email notification that the site has been shared , it took longer for Microsoft Graph API to get the alert but not sure about the actual time limit from the alert origination till the aggregation of all alerts
At the end, I know it's a very simple endpoint but the value that this endpoints represents is priceless as it allows developers to enable cross-product scenarios using same code-base through different use cases like security management, threat detection and  information protection