When
working on SharePoint Online as a new developer, there is always some confusion
how to connect and authenticate to the SharePoint site. I’ve summarized two ACL
options based implementation in previous blogs. Both implementations are using
users’ O365 use name and password to communicate with SharePoint online.
The
limitation of ACS
approach is you are using end user’s credential that is configured to have limited
access to restricted systems. It would be difficult leveraging multiple
services secured by Azure AD for some use case like background bulk process.
This blog will demonstrate the C# code to leverage Azure AD making app-only
calls into SharePoint Online. This provides a more secure way of performing
background operations against Office 365 services. In this blog you would need
to have a certificate deployed to Azure AD application for the authenticate, I’ll
have a different approach in future blog to leverage Azure AD without using
certificate.
In
order to leverage Azure AD to connect to SharePoint Online, you could need to create
a Azure AD application. You could follow the steps Richard diZerega’s blog on
the details. I’m using the procedure I’m using in the session “Application
registration in Azure AD” from Office 365 Management APIs starting guide.
Here
is the code sample to use the Azure AD application that is based on RicharddiZerega’s blog.
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using
Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace
RESTAzureToken
{
class Program
{
private static string CLIENT_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; // Azure AD application client ID. It should
be a ~35 character string
private static string
PRIVATE_KEY_PASSWORD = "MyCertPassword"; // I’ll have different example in the future
to eliminate the password
static void Main(string[] args)
{
doStuffInOffice365().Wait();
}
private async static Task
doStuffInOffice365()
{
//set the authentication context
string authority = "https://login.windows.net/mycompany.onmicrosoft.com/";
AuthenticationContext
authenticationContext = new AuthenticationContext(authority, false);
//read the certificate private key
from the executing location
var certPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
certPath = certPath.Substring(0, certPath.LastIndexOf('\\')) + "\\QCMAPICert.pfx";
// The Cert you uploaded to Azure AD application
var certfile = System.IO.File.OpenRead(certPath);
var certificateBytes = new byte[certfile.Length];
certfile.Read(certificateBytes, 0,
(int)certfile.Length);
var cert = new X509Certificate2(
certificateBytes,
PRIVATE_KEY_PASSWORD,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet
|
X509KeyStorageFlags.PersistKeySet);
ClientAssertionCertificate cac = new
ClientAssertionCertificate(CLIENT_ID, cert);
//get the access token to
SharePoint using the ClientAssertionCertificate
Console.WriteLine("Getting
app-only access token to SharePoint Online");
var authenticationResult = await authenticationContext.AcquireTokenAsync("https://
mycompany.sharepoint.com/", cac); // Do not use the site "https://
mycompany.sharepoint.com/sites/MyTestSite"
var token = authenticationResult.AccessToken;
Console.WriteLine("App-only
access token retreived");
//perform a post using the app-only
access token to add SharePoint list item in Attendee list
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization",
"Bearer " + token);
client.DefaultRequestHeaders.Add("Accept",
"application/json;odata=verbose");
const string siteURL = "https://mycompany.sharepoint.com/sites/MyTestSite";
// Get list from title
const string listAction = siteURL + "/_api/web/lists/getbytitle('TestList')";
HttpResponseMessage listResult = await
client.GetAsync(listAction).ConfigureAwait(false);
listResult.EnsureSuccessStatusCode();
string listJsonData = await
listResult.Content.ReadAsStringAsync();
Console.WriteLine(listJsonData);
// Get all items from list
const string itemAction = siteURL + "/_api/web/lists/getbytitle('TestList')/items";
HttpResponseMessage response1 = await
client.GetAsync(itemAction).ConfigureAwait(false);
response1.EnsureSuccessStatusCode();
string itemJsonData = await
response1.Content.ReadAsStringAsync();
Console.WriteLine(itemJsonData);
}
}
}
The result of the items isin json format and here is the screenshot of the data from json viewer.
If
you are new to O365 development, you should be aware of the following configurations
in order to avoid some common errors.
1. Do
not use the user friendly Microsoft new O365 URL like “mycompany.sharepoint.com”. Use the https://login.windows.net/mycompany.onmicrosoft.com/
as authority
string. Otherwise you will get the error below.
Tracing:TraceError:
"9/2/2015 11:57:02 PM: 542878bc-c518-4339-be97-41fe5f6ed523 - <RunAsync>d__0:
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException:
AADSTS90002: No service namespace named mycompany.sharepoint.com' was found in
the data store.
Trace ID:
ca2c8bba-655a-4b01-89b3-23367373c274
Correlation ID: 542878bc-c518-4339-be97-41fe5f6ed523
Timestamp: 2015-09-02
23:57:01Z ---> System.Net.WebException: The remote server returned an error:
(400) Bad Request.
2. Use the O365 tennant entry
point URL instead of the site to acquire token.
var
authenticationResult = await
authenticationContext.AcquireTokenAsync("https://qualcomm.sharepoint.com/",
cac);
If you pass the site URL 'https://mycompany.sharepoint.com/sites/SPDEV/',
you will get the error below.
Exception:Caught: "AADSTS50001:
Resource 'https://mycompany.sharepoint.com/sites/SPDEV/' is not registered for
the account.
Trace ID:
702ef6fd-c156-4d6c-99b6-f5bb18eae6c8
Correlation ID:
0b25a990-c74c-4244-934a-90d138f66b40
Timestamp: 2015-09-02
16:41:16Z" (Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException)
A
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException was
caught: "AADSTS50001: Resource
'https://qualcomm.sharepoint.com/sites/SPDEV/' is not registered for the
account.
Trace ID: 702ef6fd-c156-4d6c-99b6-f5bb18eae6c8
Correlation ID:
0b25a990-c74c-4244-934a-90d138f66b40
Timestamp: 2015-09-02
16:41:16Z"
Time: 9/2/2015 9:41:16 AM
Thread:Worker Thread[10072]
3. When you are generating the
X.509 certificate, make sure the key length is at least 2048. Shorter key
lengths are not accepted as valid keys.
4. Manifest cannot not be re-uploaded
unless you set variable $base64Value to null. If you change the certs, I'm not sure how to replace it to the exiating Azure AD application. There might be a powershell to do that but I've not find it.
5. The following packages you would need to ad to the references.
- Active Directory Application Library
- Json.NET package
- System.Net.Http package
6. Be aware of the O365 MFA configuration and you might need to run this app inside your company firewall as I explained in previous blog.
awasome
ReplyDelete