- Part 1 - Introduction to creating a multi-tenant Office 365 add-in using VS2015
- Part 2 - Creating the initial project and add-in registrations in Azure AD
- Part 3 - What did the wizard leave behind?
- Part 4 - Connecting Office 365 services to the project
- Part 5 - Adding code to send e-mails
- Part 6 - Switching to the Office 365 unified API (preview)
- Part 7 - Publishing the App to Azure
- Part 8 - Troubleshooting and tips
Azure ArtefactsTo see all that has happened after the VS2015 wizard has cast all its magical spells, let’s log into the Azure Management portal at https://portal.azure.com/, hit browse all, resource groups, and click the new resource group you created in the wizard step.
First there is a Visual Studio component (which I actually didn’t check on the ASP.NET creation page, but no biggie), then there is the database server with the application database. Next is the service plan entry and the Azure Web Application I will publish to in the end.
Next navigate to the old Azure Management portal at https://manage.windowsazure.com/, pick Active Directory, pick the AD instance for your demo tenant, and click the Applications heading.
The application page should show the entry for the newly created application.
Click the application, and click the Configure heading.
You might have noticed the auto-generated name on the consent page, and this screen is the place to change it to something more readable. I’m replacing Pzl.SampleMultiTenancy_20150918180222 with My Cool Sample Multi-tenant App.
Scroll a bit down, and you may want to change the APP ID URI as well. I’m changing mine to https://techmikael.onmicrosoft.com/PzlSampleMultiTenancy.
The Client ID listed is the App Principal Id, and inside web.config you’ll see it sitting there with the general Azure login URL and client secret. Also note the SIGN-ON URL and REPLY URL fields, which right now are pointing to your local dev. I’ll change these later when I’m ready for production in Part 7! At the bottom of the above screenshot you see two delegated permissions, which are the once checked off in the creating wizard and which displayed at the consent screen shown.
Now I have a basic application up and running which can operate against the https://graph.windows.net/ service end-point in Office 365.
Authorization codeWhen I checked the Read directory data box during the authorization part of the project creation a couple of things happened. In the file StartupAuth.cs located in the App_Start folder a piece of code got injected to make sure you get authenticated and retrieve an authorization token from the graph.windows.net resource for the resources the application is allowed to use.
In the Models folder two files got added, AdalTokenCache.cs and ApplicationDbContext.cs. These two files and the injection into StartupAuth.cs is the automagic I was looking for to make my adventure a smooth ride into the sunset. Let someone else write the plumbing!
A reference to AdalTokenCache can be found in line 65 of StartupAuth.cs.
AuthenticationContext authContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
What happens in these lines of code is that on application start the application will retrieve an authorization code, and cache the token for the resource specified, in this case https://graph.windows.net which contains operations against Azure AD. See Azure AD Graph API Common Queries for a full list of operations available.
Take a look at ActiveDirectoryClientThe home page does not take advantage of or need the consented rights, but another page has been added to the project, UserInfo.aspx. This page uses the ActiveDirectoryClient to load up the users display name, first name and last name from the Azure Active Directory user profile.
Uri servicePointUri = new Uri(graphResourceId); Uri serviceRoot = new Uri(servicePointUri, tenantID); ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await GetTokenForApplication()); // use the token for querying the graph to get the user details IUser user = activeDirectoryClient.Users .Where(u => u.ObjectId.Equals(userObjectID)) .ExecuteAsync().Result.CurrentPage.ToList().First();
You can tell this is demo code, as the same values can actually be retrieved from the user’s claim, saving yourself one extra http call.
var displayName = ClaimsPrincipal.Current.FindFirst("name").Value; var givenName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.GivenName).Value; var surname = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Surname).Value;