Getting attachments from an O365 SharePoint list item Cross-Domain

In this article I will demonstrate the nuances of being able to get Office 365 SharePoint attachments in the same domain and in the Cross-Domain scenarios.

Getting information about a list item

When requesting information from a list in SharePoint, the detail returned does not by default contain any information about attachments other than “yep I got some”. The image below shows a typical response from a simple request to show the first value in the list

var path = "https://psclistensdev.sharepoint.com/sites/demo/";
$.ajax({
  url: path+"_api/web/lists/GetByTitle('Company')/Items?$top=1",
  type: "GET",
  headers: {
    "Accept": "application/json",
    "Authorization": "Bearer " +  app.getCookie("OAuthToken")
  },
  contentType: "application/json;odata=verbose"
}).done(function(res){
    console.log(res)
})

s1

To be able to see information about the Attachments we add the $expand=AttachmentFiles parameter to the URL

var path = "https://psclistensdev.sharepoint.com/sites/demo/";
$.ajax({
  url: path+"_api/web/lists/GetByTitle('Company')/Items?$expand=AttachmentFiles&$top=1",
  type: "GET",
  headers: {
    "Accept": "application/json",
    "Authorization": "Bearer " +  app.getCookie("OAuthToken")
  },
  contentType: "application/json;odata=verbose"
}).done(function(res){
    console.log(res)
})

s2

Looking at the Attachment information

There are multiple URLs which are returned in the JSON – looking at each of them we find that some open/download the file

  1. //psclistensdev.sharepoint.com/sites/demo/Lists/Company/Attachments/1/quickstart_guide.pdf
    • works great file downloads
  2. //psclistensdev.sharepoint.com/sites/demo/_api/Web/Lists(guid’eaf7c922-649e-4447-b695-df9030e85072′)/Items(1)/AttachmentFiles(‘quickstart_guide.pdf’)
    • Creates a file called “AttachmentFiles(‘quickstart_guide.pdf’)” which when opened in notepad is actually an XML reference document…

So when downloading the file attachment in the same domain – requesting the file via the first URL – works just fine

s3

But if we move to a different domain (which has previously been added as a trusted App through Azure AD) we find that the Cross Domain headers are not provided and therefore the download fails…..

s7

I believe (although I cannot be sure) that this is because the direct URL for the attachment does not use the “_api” it is not being picked up by the Azure AD App permission process and the headers are not being added.

Doing it the other way

Going back to URL #2 (which if you remember returns an XML file)

  • //psclistensdev.sharepoint.com/sites/demo/_api/Web/Lists(guid’eaf7c922-649e-4447-b695-df9030e85072′)/Items(1)/AttachmentFiles(‘quickstart_guide.pdf’)

If we try to request this, it does actually work…..but returns the JSON version of the XML file (cos I asked it to in the headers)

s5

The trick here is to add “$value” to the end of the URL. This triggers SharePoint to send out the attachment itself

  • //psclistensdev.sharepoint.com/sites/demo/_api/Web/Lists(guid’eaf7c922-649e-4447-b695-df9030e85072′)/Items(1)/AttachmentFiles(‘quickstart_guide.pdf’)/$value

s6

Conclusion

In this article we have seen how there are multiple ways to collect the reference to a file attachment from a list item in O365 SharePoint. While both URLs work when requested from the same domain, only one of them triggers the Azure AD App registration model to add the appropriate HTTP Headers to the request, allowing a Cross-Domain request to be successfully executed.

 

 

 

 

 

 

Advertisements

How the example O365 Authorized CORS application works.

In this article we will look into how the example provided on the Microsoft HowTo site works and then discuss some practical implementations thereof.

Introduction

In the previous article (https://xomino365.com/2016/02/01/using-azure-ad-to-allow-authorized-cors-access-to-o365-data/) we looked at how to create an Azure AD application and securely access O365 data from within another web domain. The Microsoft How to site provides and example application on how to get data from your One Drive. But in this case we are going to look at how to get data from a SharePoint site collection.

The basic site

The site does have a good working example, you can copy and paste the code. What I wanted to show here was the end result and how it works. I modified mine slightly to pull data from a SharePoint list rather than OneDrive but it all works.

Starting at the site root (which is anonymously accessible), we click the Get Token button.

a13

The application then makes a request to the Azure authentication pages


aa2

Combining the values added to the example application we get the following values added to the Query_String of the address. These values must coincide with the values in the application manifest.

client_id=xxxxxxxx-xxxx-xxxx-xxxx-dca7fd30e1cb
redirect_uri=https://xomino365.azurewebsites.net/o365/demo.html
resource=https://xomino.sharepoint.com
response_type=token

If the user is not signed in then they are asked to do soaa1

Once successful authentication has been achieved the login end point returns the user to the “redirect_uri” and tacks the OAuth token onto the end of the returned URL

aa3

The application also sets a cookie – this is the Authentication cookie which says that you are logged into O365 – but that is very different from the Authorization (OAuth) token which is currently in the URL. You will also note if you look carefully that there is an expiry time in the Query_String parameters (3599) so assuming this is in second and not minutes – the OAuth token is only good for 60 minutes….

The example application waits for the response and if it find an access_token in the URL it parses it, and then moves it into the OAuth Token field

aa4

In this example the Endpoint is a hard coded URL which represents the Root folder of the root site in my O365 site. It is an out of the box REST URL.

https://xomino.sharepoint.com/_api/web/lists/getByTitle(@TargetLibrary)/RootFolder/files?@TargetLibrary=’Documents’

If we pull that up in a new tab we get the following

aa5

The reason I am getting this is because I have not actually authenticated with xomino.sharepoint.com at this time – I have logged into Azure AD. This is really interesting because if I run the example right now it will work – AzureAD has given me enough permission to make the request but not to go directly to the site. I say interesting, but it is actually slightly annoying from a developers perspective. Every time a user moves around O365 they have to go through the Single Sign on process – which is very cool, in that it works, but not the ideal user experience.

If I open https://xomino.sharepoint.com in a new tab – I get the cycle login screen and my SharePoint sites open automagically – SSO works. When I go back to the REST endpoint again now I get an authorized page.

aa6

The only reason I even bring this up is to show that Authorization is not the same as authentication. The Access_Token actually grants us permission to retrieve data and interact with the SharePoint site, even though technically we are not logged into it (yet). Pretty smart to be honest!

But this is XML – old school ATOM service. – we want new school JSON and the example app obliges.

Getting JSON Data

When the demo site realizes there is an access_token in the URL it hides the GET TOKEN button and shows the Make CORS Request button.

Clicking the Make CORS request button creates a very specific AJAX request – let’s look at the code directly.

  var xhr = new XMLHttpRequest();
  xhr.open("GET", getEndpointUrl());
  xhr.setRequestHeader("authorization", "Bearer " + token);
  xhr.setRequestHeader("accept", "application/json");
  xhr.send();

In this case the token is the very long access_token from the URL and the getEndpointUrl() is the xomino.sharepoint value in the EndpointURL field.

Once the AJAX call is made you can see what happens in the transfer using the browser developer tools (in this case firebug). The Accept Header is what will cause the O365 server to respond with JSON and not ATOM XML.

The Request Headers

This is what is going out to the O365 servers – as you can see the Accept Header and the Authorization header have been set.

aa7

The Response Headers 

The critical thing to note here is that the Access-Control-Allow-Origin Header = “*” this allows the xomino365.azurewebsites.net domain to successfully request data from the xomino.sharepoint.com domain.

aa8

The response itself

The response is a JSON data object

aa9

making it more readable we can see the object is an array of “results” within d. Each item within the array represents the verbose detail of each file within the site collection.

aa10

Conclusion

 

In this article we have looked in some detail at how the Microsoft HowTo website example works and how the authorization/authentication mechanisms play with the AJAX requests to retrieve data from one domain into another.

In a future article we will look closer at what happens when we start to play with these requests and try and break the system…

 

Using Azure AD to allow Authorized CORS Access to O365 data

In this article I will show how to to create an Authenticated way to access O365 data from an Azure website. This method creates the necessary CORS headers to allow cross domain AJAX data traffic.

This whole article is based on the following site Create JavaScript web apps using CORS to access Office 365 APIs. I wrote this article because it was not at all clear to me from the Microsoft posting how everything fit together and I had to go through it a number of times to figure it out.

The article and screenshots are accurate as of 31 Jan 2016 and may change over time.

Setting up Azure AD as your O365 directory

For a more in depth description of how to use Azure AD as your directory for O365 check out this excellent support article. This is also a very helpful link – What is Azure AD Directory? it will help you describe how to assign your Azure AD to your MS Cloud account.

Once you have the Azure AD directory set up you can manage it though the Azure portal. In there you can find your directory by scrolling to the bottom on the left and selecting Active Directory.

a1

 

Adding a new application

Select Application at the top of the screen and select Add at the bottom

a2

Add an application my organization is using

a3

Give it a name

a4

Give it a sign in URL and and APP ID URI (“The App ID URI is a unique identifier for Azure AD to identify your app“)

a5

For the sake of this demo the above values are arbitrary and make more sense in context of the overall configuration.

a6

woo!

Select Configure at the top and look at what was created

App configuration

The configuration section shows all the information you need, and you application needs to be able to access O365 data from your application. This does NOT have to be an azure hosted application. I am just using Azure for the sake of convenience.

The name and Sign-on URL are carried over – the Sign in URL is actually merely for convenience, you don’t need to use it as far as I can tell.

a7

The Client ID – this is the single most important piece of this puzzle – this key holds the application together. You send it as part of the request for Authorization token. With it O365 determines the what who why when for security.

a8

In the Single Sign on section below we see the App ID and the Reply URL (which is the Login page URL)

a9

We are going to change the reply URL to be the App ID for convenience. Don’t forget to save!

a10

We have to give the demo.html application access permissions inside of O365

Adding permissions to applications

Selecting Add Application brings up all the applications being managed by Azure AD directory.

a11

We are going to change permissions to access SharePoint online. Select and continue..

Select the delegation permissions. In this case I blanket added all permissions just for the sake of not having to explain everything. You probably want to make sure that you understand what permissions you are exactly giving the user. Remember once you create the authorization, users can write their own JavaScript and do not have to do what you want them to, they can do what you authorize them to do.

a12

and don’t forget to save…..remember this, otherwise you will waste time wondering why you are an idiot….

Changing the manifest

The manifest file is available at the bottom of the screen – selecting the link will download the text file.

a15

Looking at the manifest we see the true textual representation of the configuration. The important part here is to allow the application to allow the OAuth token to be returned to the user so it can then be used for further data requests. If you do not make the following change the example application (see below) will return the following…

error=unsupported_response_type&error_description=AADSTS70005:+response_type+’token’+is+not+enabled+for+the+application
Trace+ID:+f4354ef3-de41-4909-a829-0811b0bc0407
Correlation+ID:+93f60ed1-f703-4b5d-b0fb-bebc94192f3b
Timestamp:+2016-01-31+03:36:57Z”

To correct fo this issue, change the following line to true

“oauth2AllowImplicitFlow”: true,

Save the manifest and upload it again.

Creating an application

Following the methodology from the previous article, I created an Azure HTMl5 app with source control integration and loaded the example files up to the demo.html page.

The demo Application

The end product looks like this.

a16

Click the button to get the OAuth Tokena17

The Token is returned as part of the URLa18

Clicking the CORS Request button collects data from the default site collection on the https://xomino.sharepoint.com O365 account.

We do not need to make this a 2 step process though, in later article we will show how to create the results in the form of an angular ui-grid

a19

Conclusion

It is a little convoluted to create and set up an “Application your company is working on” and make it use O365 Authorization and Authentication. But once you go through it the first time it becomes relatively east to remember.

We were able to request O365 data on https://xomino.sharepoint.com from a site not in the same web domain, https://xomino365.azurewebsites.net

This is the first example and I will be writing more about how it all fits together in the future.