-->

12/05/2018

Sharepoint Online Authentication for API Access using POSTMAN

Objective: We know most of the collaboration part of sharepoint has been pushed to Teams, with its planner, conversations, a dedicated site for document uploads and many other features.
So the sharepoint site is now used mostly for document management. Being said that how we can accomodate SPO API for other applications.

In this post we will use SP Online OOTB API to upload and download a file. We dont need to write a seperate application for this, instead we will use POSTMAN to interpret the calls.

When API is avilable OOTB why do we need this post?
Well Service API is available OOTB, but there is specific way we need to authenticate to use that API. That is what we are going to show here.

Let me give a highlevel overview of how authentication works in Office365 Sharepoint site in this case.





Step 1: Know your Tenant ID and Resource ID
It is very important to know your tenant ID for triggering any kind of service calls.
You can get your the Tenant ID in following ways:
1. Using Powershell
2. Making a call to "_vti_bin/client.svc"
3. This is the most easiest way browse "/_layouts/15/appprincipals.aspx"

We use 3rd way as it is the easiest way. When you browse that url under any SP Online site from your tenant. The part after "@" is your tenant ID and the part before @ is Resource ID. Make a note of it.



Step 2: Register a new app
You need to register a new addin/app in your Sharepoint site, this will generate a ClientID and a Client Secret, which we will use to authenticate. Lets see how to do it.

Go to "_layouts/15/appregnew.aspx" under the SP Online site which you want to use as document repository.

Use the "Generate" buttons to generate a unique ClientID and Client Secret. Give appropriate title to your app. You can make localhost as your domain name and redirect url.  Click Create.

Note: Please copy and paste those ClientID and Client Secret in a sepearte notepad file as you cannot retrieve them after saving this information.

Step 3: Grant permissions
New Client app has been created in SP Online site, now its time to decide what permissions this app should have on your site.  You can grant Site collection, web or even at list level read or write permissions.

Go to "/_layouts/15/appinv.aspx" and serach with ClientID we generated earlier. The application will fetch all other details based on your ClientID.
 Add the below XML snippet specifying what kind of permissions you want this app to have on your site.  I want to upload a document so i granted "Write" permission.

<AppPermissionRequests AllowAppOnlyPolicy="true">
    <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="Write" />
</AppPermissionRequests>

More details about the permissions are here.

Click "Create" button and you will be prompted if you trust the app to run with said permissions. Click Trust.

Note: App registration can also be done using Azure App services. But it requires a storage account and proper azure subscription and you need to pay for maintiaining app service in azure.
So we choose the cheap and easiest way.

Step 4: Get Access Token for the Office365 Tenant.
Open Postman and make a request for access token as below.

Header: 
Content-Type : application/x-www-form-urlencoded
Body:
grant_type : client_credentials
client_id : ClientID@TenantID
client_secret : Client Secret
resource: ResourceID/<TenantName>.sharepoint.com@TenantID

ResourceID is common for all of SPO.
Now click send and you will receive an access token.
Copy that access token as we need to send it in headers for every API request.
Please note that every access token will be valid for 3600 seconds or 1 hour.


Step 5: Make a call to Sharepoint REST API
Its time to test the access to REST API using the OAuth access token.

First we will make a call to get the title of the site using REST API. Below is the url we need to make a call to get Title.
https://1yearsub.sharepoint.com/sites/DEV/_api/web?$select=Title

Make a postman request shown as below with below headers.

Header:
Accept : application/json;odata=verbose
Authorization : Bearer <Access Token>


We see the title fetched by a REST API call using POSTMAN.

Upload Document Using REST API:
Headers will be same as above and it will be a POST call this time. Attach the file in the body tab of the POSTMAN call.

Header:
Accept : application/json;odata=verbose
Authorization : Bearer <Access Token>

REST Request Url : https://1yearsub.sharepoint.com/sites/DEV/_api/web/GetFolderByServerRelativeUrl('/sites/dev/shared documents')/Files/add(url='testfile.pdf',overwrite=true)



Here is the result:
Download the Document using REST API:
Headers will be same as above and its a Get call now.

Header:
Accept : application/json;odata=verbose
Authorization : Bearer <Access Token>

REST Request Url: https://1yearsub.sharepoint.com/sites/DEV/_api/web/GetFolderByServerRelativeUrl('/sites/dev/shared documents')/Files('testfile.pdf')/$value



Looking at the response we see that the file is downloaded in binary.

Hope this post gave some better idea on Accessing REST API of Sharepoint Online site.

Happy Coding !

68 comments:

  1. Thank you for detailed steps. I followed it, it worked!

    ReplyDelete
    Replies
    1. Hi,

      I have Sharepoint Onprimises, and ReactNative App(for Mobile).My query is How i can get sharepoint onprimises data into ReactNative App or mobile App.

      Please suggest me best solution for this.I am not able to achive this from 1 month.

      Thanks

      Delete
    2. For sharpeoint Onprem . .. you dont need all this hassle. API calls are simple using what ever authentication the site prefers.

      Delete
    3. Could you please explain this in detail, Thanks

      Delete
    4. I would need this too on a on prem server.

      Explain it plz.

      thx

      Delete
  2. Thanks for detailed steps... I guess my request is failing at step 7 & 8. I got token and its working if i enter a wron one it says invalid token

    Here is the error "Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown."

    Any pointers ?

    ReplyDelete
    Replies
    1. Please know that the OAUTH token will be expired in 60 Mins, so its better to generate based on a timer watch.

      Delete
    2. You will get this error if there is a typo in the resource. I had a typo in the tenant name which led to me receiving this error.

      Delete
    3. I love you so much!

      Delete
  3. I can't get it to recognize the client secret. Every time I try I get this:
    "error": "invalid_client",
    "error_description": "AADSTS7000215: Invalid client secret is provided. Trace ID: 15b8019a-f3be-4a99-8174-1ee409392100 Correlation ID: 9044679d-b380-4166-9318-63270f2fa19b Timestamp: 2019-03-03 18:56:48Z",

    I've recreated the app credentials three times now; it just won't take the client secret. Have you ever run into this problem?

    ReplyDelete
    Replies
    1. If its saying "Invalid client" while sending request, you need to check that you use same tenant ID in both the URI request and the parameter in the request body. Also did you registered and trusted the App as shown in Step3. Let me know how it goes.

      Delete
    2. Also make sure you use UrlEncode for client secret. I use SPFx to send it as json to Flow Url with 'secretId': encodeURIComponent('/za1J....WtXQfc5rfvs//JsiPKW8='),

      Delete
  4. Hi I get API like postman on android app but I got:
    {"error":"invalid_request","error_description":"AADSTS900144: The request body must contain the following parameter: 'grant_type'.\r\nTrace ID: 31842e84-cc80-4a12-9047-f42a262b2f00\r\nCorrelation ID: 0a4090f5-388f-4733-a178-b7874f557e69\r\nTimestamp: 2019-03-20 16:40:40Z","error_codes":[900144],"timestamp":"2019-03-20 16:40:40Z","trace_id":"31842e84-cc80-4a12-9047-f42a262b2f00","correlation_id":"0a4090f5-388f-4733-a178-b7874f557e69"}
    2019-03-20 23:40:46.985 22006-22200/securechannels.com.sharepointchallenge D/OkHttp: <-- END HTTP (437-byte body)

    ReplyDelete
    Replies
    1. Getting the same error... did you solve it?

      Delete
    2. I solved it! I was placing the parameters as headers and not within the body! The only header should be the content-type one

      Delete
    3. i too got the same error but iam passing the parameters in body. did any solve this

      Delete
  5. It seems like your request body parameters are not as shown in the step 4. I ran the project code one more time today and it works. Please recheck that you provided the same tenat ID in both request URI and the Request body.

    ReplyDelete
  6. Followed these instructions to the letter but I can't seem to authenticate to get a token. Getting the following error and everything I google keeps pointing me to Azure AD stuff.

    AADSTS500011: The resource principal named was not found in the tenant named . This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant

    Any ideas?

    ReplyDelete
    Replies
    1. Did you made your SP admin to approve and trust the App in App Console. I am asking stupid question.. but you used your own Tenant ID and Tenant name and other unique identifiers... right?

      Delete
    2. Yes, we went to /.../sites/..../appinv.aspx to give it permissions unless there is another place it needs permission. We did this on the main sharepoint.com site and then each on both sites and teams pages we wanted to have access.

      When looking at enabled list of apps for each site/team page the one we created is there, but the odd thing is that everything on both apps all use the same resource id.

      I know we're missing something I just dont know what. And I know nothing of sharepoint nor do I even have access to it. I have to get our IT people involved, and they dont know anything about it either.

      Delete
    3. I had this same issue. I realized I was using the Resource ID for my specific app instead of 00000003-0000-0ff1-ce00-000000000000 (the actual value highlighted in step 1). This is the principal id for SharePoint. https://docs.microsoft.com/en-us/archive/blogs/kaevans/inside-sharepoint-2013-oauth-context-tokens

      Delete
  7. Is there a way to display the Title of the App in the 'Modified By ' Column instead of 'Sharepoint App'?

    ReplyDelete
  8. Hello , is there any way to the refresh token or everytime i have to do the request ?

    ReplyDelete
    Replies
    1. Those are two seperate calls . . . you dont need to do it for every call, token is valid for 1 hour by default. You can have the callee method to handle and retry if the token expires

      Delete
  9. Hi, Thank you so much for the detail steps. I followed each step But unfortunately it generate the below error response.
    Could not get any response
    There was an error connecting to .
    Why this might have happened:
    The server couldn't send a response:
    Ensure that the backend is working properly
    Self-signed SSL certificates are being blocked:
    Fix this by turning off 'SSL certificate verification' in Settings > General
    Proxy configured incorrectly
    Ensure that proxy is configured correctly in Settings > Proxy
    Request timeout:
    Change request timeout in Settings > General

    The environment is O365. Any help for these Please?

    ReplyDelete
    Replies
    1. I got this too. Help would be greatly appreciated.

      Delete
    2. Disabling the "Accept" header will show, this is a 401 Unauthorized response, though the the token is new and got it just fine. (I'm not the author of the original question, but the comment is mine)

      Delete
  10. Hi Pratap,

    Thanks for sharing this. Its really helpful for us to understand how sharepoint API works. I followed all the steps and however i am unable make a call for step 7 and 8 to upload a file in sharepoint. I am getting the following error though i have used the fresh token.

    {"error_description":"Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown."}

    Could you please help me in resolving this.

    Regards,
    Khadar.

    ReplyDelete
    Replies
    1. You may need to check your tenant ID in the request Url. That exception says you are hitting other tenant intead of your.

      Delete
    2. Hi Pratap,

      I used the tenant id to get token but i didn't use any tenant id while uploading a document to sharepoint. i don't see tenant id in your attachment also.

      https://sharepoint/sites/dev/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')/Files/add(url='test.xlsx',overwrite=true)

      could you please help me where exactly i am missing it.

      Regards,
      Khadar.

      Delete
    3. You are missinf the site path in the GetfolderByServerRelativeUrl(), you should give starting from "/sites/dev/Shared Documents". Try that and let me know.

      Delete
    4. Still it is same Pratap :(

      Delete
    5. Can you chek are you able to get site title using the same token.

      Delete
  11. Hi Pratap,

    Thanks for this steps. I am new to Spring rest and sharepoint api ,I am working on requirement to upload and download files in sharepoint using above mentioned APIs through spring rest + java. i have referred several post where i didnt find solution. can you guide in this

    ReplyDelete
    Replies
    1. Hi The above project will help you orchestrate the API calls any falvor of code. This post will help you to see which url to be called , which parameters need to be passed and how to authenticate.

      Delete
  12. Hello Pratap, thank you very much for this post. But, I'm having trouble getting the token because I don't understand this part: "resource: ResourceID/.sharepoint.com@TenantID"
    What is ResourceID? Where do I get it?

    ReplyDelete
    Replies
    1. you get the resource id from ""/_layouts/15/appprincipals.aspx""

      Delete
    2. I dont understand you, I dont find the ResourceID

      Delete
  13. Hi Pratap, Thanks for sharing this article. Do you have similar example for creating folder in sharepoint?

    ReplyDelete
  14. Hello Pratap,We are trying to fetch the Token from Sharepoint.I followed the POSTMAN steps above , from our end, and entered the credential given by Sharepoint folks , but I am getting below error:

    {
    "error": "unauthorized_client",
    "error_description": "AADSTS700016: Application with identifier '617fb736-c1a0-4a13-bc74-daf29daca596' was not found in the directory '68283f3b-8487-4c86-adb3-a5228f18b893'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.\r\nTrace ID: 77fc7f47-0cf9-479e-b2ae-048eb69c0800\r\nCorrelation ID: 3fc25fe1-9988-4a70-b0f4-60233384c60d\r\nTimestamp: 2019-11-07 06:06:38Z",
    "error_codes": [
    700016
    ],
    "timestamp": "2019-11-07 06:06:38Z",
    "trace_id": "77fc7f47-0cf9-479e-b2ae-048eb69c0800",
    "correlation_id": "3fc25fe1-9988-4a70-b0f4-60233384c60d",
    "error_uri": "https://accounts.accesscontrol.windows.net/error?code=700016"
    }
    Could you please help, at which step, the problem might be?

    ReplyDelete
  15. Hi Pratap,

    I have a SharePoint site that's On premise as well . Can u guide me as well how to retrieve the data.

    ReplyDelete
  16. I tried your approach and was able to get the access token, but in the next request, when I try to hit the welcome page of the site, I get this error:

    {"error_description":"Invalid issuer or signature."}

    ReplyDelete
  17. Hi, Thanks for this blog..
    I face one issue when trying to upload I get:
    {"error":{"code":"-2147024891, System.UnauthorizedAccessException","message":{"lang":"en-US","value":"Access denied. You do not have permission to perform this action or access this resource."}}}

    download worked ok....what am I missing?

    ReplyDelete
  18. Hi Pratap, I was able to upload the PDF file to SharePoint in the Binary mode by attaching the file and it worked well. I need to convert the PDF file to Base64 format and upload the same in the 'raw' mode in the Body.
    Can you please help me with the Body structure for the 'raw' mode ?

    ReplyDelete
  19. How to find the ResourceId ?

    ReplyDelete
  20. public static void main(String[] args) throws Exception{
    /**
    * This function helps to get SharePoint Access Token. SharePoint Access
    * Token is required to authenticate SharePoint REST service while performing Read/Write events.
    * SharePoint REST-URL to get access token is as:
    * https://accounts.accesscontrol.windows.net//tokens/OAuth/2
    *
    * Input required related to SharePoint are as:
    * 1. shp_clientId
    * 2. shp_tenantId
    * 3. shp_clientSecret
    */

    String accessToken = "";
    String shp_clientId="f83e1c66-f64d-49a1-85e2-207e47092918";
    String shp_tenantId="42150afd-062a-4a0f-81b9-0323526054cc";
    String shp_clientSecret="28t3QzULzpYlhJhHqt8BTGn+xY/WmQQcokb1SxKryNI=";
    try {


    // AccessToken url
    String wsURL = "https://accounts.accesscontrol.windows.net/" + shp_tenantId + "/tokens/OAuth/2";

    URL url = new URL(wsURL);
    URLConnection connection = url.openConnection();
    HttpURLConnection httpConn = (HttpURLConnection) connection;

    // Set header
    httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    httpConn.setRequestProperty("Content-Length", "0");
    httpConn.setRequestProperty("Host", "");
    httpConn.setDoOutput(true);
    httpConn.setDoInput(true);
    httpConn.setRequestMethod("POST");

    // Prepare RequestData



    String jsonParam = "grant_type=client_credentials"
    + "&client_id=" + shp_clientId + "@" + shp_tenantId
    + "&client_secret=" + shp_clientSecret
    + "&resource=00000003-0000-0ff1-ce00-000000000000/naveedtest.sharepoint.com@" + shp_tenantId;
    //Here, is "Origanisations's Sharepoint Host"

    // Send Request
    DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
    wr.writeBytes(jsonParam);
    wr.flush();
    wr.close();

    // Read the response.
    InputStreamReader isr = null;
    if (httpConn.getResponseCode() == 200) {
    isr = new InputStreamReader(httpConn.getInputStream());
    } else {
    isr = new InputStreamReader(httpConn.getErrorStream());
    }

    BufferedReader in = new BufferedReader(isr);
    String responseString = "";
    String outputString = "";

    // Write response to a String.
    while ((responseString = in.readLine()) != null) {
    outputString = outputString + responseString;
    System.out.println(outputString);
    }

    // Extracting accessToken from string, here response (outputString)is a Json format string
    if (outputString.indexOf("access_token\":\"") > -1) {
    int i1 = outputString.indexOf("access_token\":\"");
    String str1 = outputString.substring(i1 + 15);
    int i2 = str1.indexOf("\"}");
    String str2 = str1.substring(0, i2);
    accessToken = str2;
    }
    } catch (Exception e) {
    accessToken = "Error: " + e.getMessage();
    }
    }


    I am getting error while trying to get access token

    {"error":"invalid_client","error_description":"AADSTS7000215: Invalid client secret is provided.\r\nTrace ID: 86067e1a-5f5a-4783-8f56-29a1daa99700\r\nCorrelation ID: edadff7e-2c28-4f6b-bc4c-15b727762919\r\nTimestamp: 2020-06-19 18:03:20Z","error_codes":[7000215],"timestamp":"2020-06-19 18:03:20Z","trace_id":"86067e1a-5f5a-4783-8f56-29a1daa99700","correlation_id":"edadff7e-2c28-4f6b-bc4c-15b727762919","error_uri":"https://accounts.accesscontrol.windows.net/error?code=7000215"}

    Using postman with the same configuration i.e. client secret,tenant id i am able to get the access token properly.

    ReplyDelete
    Replies
    1. Encode your shp_clientSecret ( e.g. replace + by %2B , = by %3D etc)

      Delete
  21. Thanks very much for the guide. followed step by step, got access token, but received error below when try to access the API:

    {"error":"invalid_request","error_description":"Token type is not allowed."}

    Any help would be great appreciated!

    ReplyDelete
  22. Hi,
    I followed the same steps which are mentioned in this blog. I am able to generate the access token. But I am getting "{"error":"invalid_request","error_description":"Token type is not allowed."}" while trying to access Sharepoint data using the access token. I have all kind of admin access to Sharepoint Site.I am the owner of this site. Can anyone help me on resolving this error.

    ReplyDelete
    Replies
    1. Hi Me too getting the same error "{"error":"invalid_request","error_description":"Token type is not allowed."}" is there any solution please share.

      Delete
    2. Same error here. Please help...

      Delete
    3. I cannot figure out without a screenshot of the request. Please send me the request so that i can help.

      Delete
  23. Hi I am too getting the same error please help

    {"error":"invalid_request","error_description":"Token type is not allowed."}

    ReplyDelete
  24. {"error":"invalid_request","error_description":"Token type is not allowed."} same error getting

    ReplyDelete
  25. Do you have any postman collection for this?

    ReplyDelete
    Replies
    1. Hi heller
      Yes i do, but cant share it as it has all the IDs to missuse my DEV tentant. Sorry.

      Delete
  26. why I cant run this link on my share point site..
    3. This is the most easiest way browse "/_layouts/15/appprincipals.aspx"

    it display a message :

    There are no apps having explicit access to the site collection

    ReplyDelete
  27. I can't this to work. I tried to different posts, multiple times, but I always get the Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException exception whatever I do.

    I manage to get to the token, but can't do a request to https://siteURL/sites/siteName/_api/web?$select=Title

    Anything I could be overlooking?

    Thanks

    ReplyDelete
    Replies
    1. Make sure you don't have a typo anywhere in tenant url.

      Delete
  28. Hi, thanks for the great tutorial! Everything works for me except uploading the document. I receive the following

    {
    "error": {
    "code": "-2147024891, System.UnauthorizedAccessException",
    "message": {
    "lang": "en-US",
    "value": "Access denied. You do not have permission to perform this action or access this resource."
    }
    }
    }

    Any ideas are much appreciated! Thanks

    ReplyDelete
  29. Hi , I am able to get the token. But the Rest call is failing.
    {"error":"invalid_request","error_description":"Token type is not allowed."}

    ReplyDelete
    Replies
    1. Hi I have same error, have you figured out the fix? thanks

      Delete
  30. Hi Pratap,

    I am getting below error

    "error": "invalid_request",
    "error_description": "AADSTS90002: Tenant 'baa501b8-c1cf-4376-ad5f-a02c9e7fac86' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.\r\nTrace ID: 63b66b28-e489-4b4b-a92f-de8d2fdf3102\r\nCorrelation ID: 741c5664-4379-49f2-b83f-40d27c643b98\r\nTimestamp: 2021-07-07 10:40:46Z",
    "error_codes": [
    90002
    ],


    I am accessing SharePoint 2019 On-premises site. I explicitly want to access SharePoint site using this approach in postman(Client requirement). Followed all steps provided from your article. Note: I dont wnat to use NTLM approach because of user name, password security. Please let me where I am wrong also let me know how to acces SharePoint on-premises in Postman using your approach.

    ReplyDelete
  31. Hi, I also get the error like others:
    {"error":"invalid_request","error_description":"Token type is not allowed."}

    I think it's related to some change at Microsoft side for new Sharepoint site.
    But I still trying to figure out how to fix this.

    ReplyDelete

  32. Thanks for sharing this post with us.Wikivela

    ReplyDelete