Office 365 API vs SharePoint app model
#SPSBE02
Lieven Iliano, U2U
April 18th, 2015
Pla
tin
um
Go
ldSilver
Thanks to our sponsors!
Agenda
Introducing Office 365 API
Developing apps consuming Office 365 API
Registering Office apps in Azure AD
Azure AD Authentication & Authorization
.Net Client Library
Office 365 apps vs SharePoint apps
U2U Site Provisioning
Set of REST services:
Microsoft Exchange Online: Mail, Contacts & Calendars
Microsoft OneDrive for Business: My Files
Microsoft SharePoint Online: Sites
Microsoft Azure Active Directory: Authentication, Directory Graph
Office 365 API
Office 365 API
Directly via REST
.NET Client Library: Windows apps, ASP.NET, WPF, Xamarin…
JavaScript Client Library
Open Source SDK for iOS and Android
Choice of client and development
How does it work?
Applications using Office 365 API need to be registered in AzureActive Directory
Done manually or from within Visual Studio
2 Types of applications can be registered:
• Web Application (Web API, MVC, Web Forms)
• Native Client (Mobile, Windows apps, Desktop App)
Azure Active Directory
Extensions and Updates:Microsoft Office 365 API Tools (Part of
Office Developer Tools)
Nuget packages:
Office 365 apps in Visual Studio
O365 Service Desktop App/ Store App/
ASP.NET App
Xamarin Cordova
Users and Graphs Microsoft.Azure.ActiveDirectory.GraphClient Microsoft.Azure.ActiveDirectory.GraphClient.JS
Outlook Services Microsoft.Office365.OutlookServices
SharePoint
Services
Microsoft.Office365.SharePoint
Discovery Client Microsoft.Office365.Discovery
Any Service Microsoft.Office365.OAuth.Xamarin Microsoft.Office365.ClientLib.JS
Add Office 365 API to your project
Office 365 apps in Visual Studio
Configure permissions:
Will automatically configure application in AAD
Adds nuget packages and configuration settings
Office 365 apps in Visual Studio
Your apps are registered in Azure AD
Azure Active Directory
Specify the service and permissions
Office 365 Exchange Online serviceAccess to Mail, Calendar, Contacts
Office 365 SharePoint Online serviceAccess to Files in SharePoint Online or OneDrive for Business
Azure Active Directory
OAuth 2.0 Authorization Code Grant flow
App uses access token on your behalf
Oauth 2.0 Client Credentials Grant Flow
App runs with own permissions
Only supported by contacts, calendar & mail
Authentication and Authorization
App redirects user to an AAD authentication endpoint
Authentication for Office 365 Apps
User authenticates and grants consent
Azure AD issues an authorization code
Authentication for Office 365 Apps
User authenticates and grants consent
Authentication for Office 365 Apps
App passes authorization code to AAD
Azure AD returns access and refresh tokens
Authentication for Office 365 Apps
App uses access/refresh tokens to access Office 365 API
Authentication for Office 365 Apps
1. Authenticate by using Active Directory Authentication Library (ADAL)
2. Discover available App capabilities. Returns only services App has access to.
3. Connect through Outlook/SharePoint Services Client
Programming with Office 365 API
Get resource endpoints from discovery service
Programming with Office 365 API
End Point (i.e)
Discovery https://api.office.com/discovery/v1.0/me
Contacts
Calendar
https://{server_name}/api/{version}/{user_context}
https://outlook.office365.com/api/v1.0/me
OneDrive for
Business
https://{tenant}-my.sharepoint.com/_api/v1.0/me
Sites https://{tenant}.sharepoint.com/{site-path}/_api/v1.0
Discovery client from WebApp
// Get user and object ID from claimsvar signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
// Authority: "https://login.windows.net/<tenant id>"AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
// Create Discovery Client// DiscoveryServiceEndpointUri: "https://api.office.com/discovery/v1.0/me/"// DiscoveryServiceResourceId: "https://api.office.com/discovery/"DiscoveryClient discClient = newDiscoveryClient(SettingsHelper.DiscoveryServiceEndpointUri,
async () =>{
var authResult = await authContext.AcquireTokenSilentAsync(
SettingsHelper.DiscoveryServiceResourceId,new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;});
Discovery client from Native App
authenticationContext = newAuthenticationContext("https://login.windows.net/Common/");
AuthenticationResult authenticationResult =authenticationContext.AcquireToken("https://graph.windows.net", ClientId, new
Uri(RedirectUri));DiscoveryClient discoveryClient = new DiscoveryClient(new
Uri("https://api.office.com/discovery/v1.0/me/"),async () => { return await
GetAccessTokenForServiceAsync("https://api.office.com/discovery/"); });
private async Task<String> GetAccessTokenForServiceAsync(string serviceResourceId){
AuthenticationResult authResult = awaitthis.authenticationContext.AcquireTokenSilentAsync(serviceResourceId, ClientId);
return authResult.AccessToken;}
Outlook Services Client
// Discover if resource is availableCapabilityDiscoveryResult dcr = await discClient.DiscoverCapabilityAsync(”Mail”);
// Get the OutlookServicesClient: this gives access to mail, contacts, calendarreturn new OutlookServicesClient(dcr.ServiceEndpointUri,
async () =>{
var authResult = await authContext.AcquireTokenSilentAsync(
dcr.ServiceResourceId,new ClientCredential(SettingsHelper.ClientId,
SettingsHelper.AppKey),new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;});
Gives access to Mail, Calendar and Contacts
Outlook Services Client
Send email// Initialize variablesstring subject = "Mail sent by using Office 365 APIs";string recipients = "[email protected];[email protected]";string bodyContent = "This email was created from code and was sent using the Office 365 APIs";
// Prepare list of recipientsList<Recipient> toRecipients =
recipients.Split(new char[]{';'}, StringSplitOptions.RemoveEmptyEntries).Select(
recipient => new Recipient{
EmailAddress = new EmailAddress { Address = recipient, Name = recipient }
}).ToList<Recipient>();
// Create draft messageMessage draft =
new Message(){
Subject = subject,Body = new ItemBody { ContentType = BodyType.Text, Content = bodyContent},ToRecipients = toRecipients
};
Send email// Add the message to the draft folder. This results in a call to the service. // Returns full item but unfortunately you dont have access to it.awaitoutlookServicesClient.Me.Folders.GetById("Drafts").Messages.AddMessageAsync(draft);
// Gets the full draft message, including the identifier needed to issue a send mail request.// This results in a call to the service. IMessage updatedDraft = awaitoutlookServicesClient.Me.Folders.GetById("Drafts").Messages.GetById(draft.Id).ExecuteAsync();
// Issues a send command so that the draft mail is sent to the recipient list.// This results in a call to the service. awaitoutlookServicesClient.Me.Folders.GetById("Drafts").Messages.GetById(updatedDraft.Id).SendAsync();
Get contacts
Add contact
Delete contact
Contacts
// Get paged collection of contactsIPagedCollection<IContact> contactsPage =
await (outlookServicesClient.Me.Contacts.OrderBy(c => c.FileAs)).Skip((pageNo - 1) * pageSize).Take(pageSize).ExecuteAsync();
// Create contactContact newContact = new Contact();...
// This results in a call to the service.await outlookServicesClient.Me.Contacts.AddContactAsync(newContact);
// Get the contact to deletevar contactToDelete = await outlookServicesClient.Me.Contacts[contactId].ExecuteAsync();
// Delete the contactawait contactToDelete.DeleteAsync();
Get events
Add event
Delete Event
Events
// Get paged collection of eventsIPagedCollection<IEvent> eventsPage =
await (outlookServicesClient.Me.Calendar.Events.Where(e => e.Start >= DateTimeOffset.Now.AddDays(-30) && e.Start <= DateTimeOffset.Now.AddDays(30))
.OrderBy(e => e.Start))
.Skip((pageNo - 1) * pageSize).Take(pageSize).ExecuteAsync();
// Create new event and addEvent newEvent = new Event();await outlookServicesClient.Me.Events.AddEventAsync(newEvent);
// Get the event to deleteIEvent eventToDelete = awaitoutlookServicesClient.Me.Calendar.Events[selectedEventId].ExecuteAsync();
// Delete the eventawait eventToDelete.DeleteAsync(false);
Gives access to Files and OneDrive
SharePoint Services Client
// Discover if resource is availableCapabilityDiscoveryResult dcr = await discClient.DiscoverCapabilityAsync(”MyFiles”);
// Get the SharePointClient: this gives access to OneDrive and Filesreturn new SharePointClient(dcr.ServiceEndpointUri,
async () =>{
var authResult = await authContext.AcquireTokenSilentAsync(
dcr.ServiceResourceId,new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;});
Get files
// Do paging when fetching int pageNo = 1;int pageSize = 25;
// Get the SharePoint clientSharePointClient sharePointClient = awaitAuthenticationHelper.EnsureSharePointClientCreatedAsync("MyFiles");
// Get the files (and folders)IPagedCollection<IItem> filesPage =
await (sharePointClient.Files.Where(i => i.Type == "File")).Skip((pageNo - 1) * pageSize).Take(pageSize).ExecuteAsync();
// Get current pageIReadOnlyList<IItem> fileItems = filesPage.CurrentPage;
// Cast to file objectsIEnumerable<File> files = fileItems.Cast<File>();
Add file// Filename + SharePoint Clientstring filename = "sample.txt";var spClient = await AuthenticationHelper.EnsureSharePointClientCreatedAsync("MyFiles");
// Check if the file exists, delete it if it doestry{
IItem item = await spClient.Files.GetByPathAsync(filename);await item.DeleteAsync();
}catch (ODataErrorException){
// fail silently because it doesn't exist.}
// In this example, we'll create a simple text file and write the current timestamp into it. string createdTime = "Created at " + DateTime.Now.ToLocalTime().ToString();byte[] bytes = Encoding.UTF8.GetBytes(createdTime);
using (MemoryStream stream = new MemoryStream(bytes)){
// If file already exists, we'll get an exception. File newFile = new File { Name = filename };// Create the empty file.await spClient.Files.AddItemAsync(newFile);// Upload the file contents.await spClient.Files.GetById(newFile.Id).ToFile().UploadAsync(stream);
}
Type of application
Office 365 apps vs SharePoint apps
Office 365 apps SharePoint apps
External/standalone app Integrated in SharePoint
Accessibility
Office 365 apps vs SharePoint apps
Office 365 apps SharePoint apps
From any link
From the Office 365 App Launcher From the
Office 365 “My apps” page
from a SharePoint/SharePoint Online site
Registration
Office 365 apps vs SharePoint apps
Office 365 apps SharePoint apps
Registration in Azure AD Registration in _layouts/15/appregnew.aspx
Deployment
Office 365 apps vs SharePoint apps
Office 365 apps SharePoint apps
Deployed once to the hosting platform
Mobile/Web/Desktop
*.app package deployed to app catalog and
installed in various SharePoint sites
Accessing CSOM from Office 365 appprivate static async Task<string> GetAccessToken(string resource){
// Redeem the authorization code from the response for an access token and refresh token.var principal = ClaimsPrincipal.Current;
var nameIdentifier = principal.FindFirst(ClaimTypes.NameIdentifier).Value;var tenantId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
AuthenticationContext authContext = new AuthenticationContext(string.Format("{0}/{1}", SettingsHelper.AuthorizationUri, tenantId),new ADALTokenCache(nameIdentifier));
var objectId = principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var result = await authContext.AcquireTokenSilentAsync(resource,new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),new UserIdentifier(objectId, UserIdentifierType.UniqueId)
);return result.AccessToken;
}
SharePoint url
Set Authorization header for every request
Start accessing lists, items, …
Permissions are checked
Accessing CSOM from Office 365 app
ClientContext clientContext = new ClientContext(spSiteUrl);clientContext.ExecutingWebRequest +=
(sender, e) =>{
e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + accessToken;
};
Thank you!