Authenticating to the SharePoint REST API as an Entra app (no user sign-in)?

Max Cutlyp 0 Reputation points
2024-11-20T02:17:27.36+00:00

I'm trying to use the SharePoint REST API to interact with SharePoint in ways that the Graph API doesn't support (specifically, adding members to a site). I have an Entra app registered as cross-tenant with Sites.FullControl.All, admin consented in the target tenant. It's not possible to have an interactive user sign-in flow as the app runs headless as a daemon. I need access to all sites, including ones that will be created in the future, so granting permission to each of them individually is not an option.

In the below samples, I've set {client_id} as the application ID of the Entra app, {client_secret} as the secret generated for it, and {tenant_id} as the tenant ID of the target tenant (the one with the SharePoint sites I'm trying to access, and the one that has granted admin consent to the Entra app). I've used contoso as a stand-in for the actual SharePoint subdomain.

I've tried two options, neither have worked.

1:

POST https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={client_id}&
client_secret={client_secret}&
scope=https://contoso.sharepoint.com/.default&
grant_type=client_credentials

This gives me a response with a valid-looking access_token. However, when I try to make any request - for example:

GET https://contoso.sharepoint.com/sites/MySite/_api/web/lists
Authorization: Bearer {access_token}
Accept: application/json;odata=verbose

I get 401 with {'error_description': 'ID3035: The request was not valid or is malformed.'}. I seem to get the same error regardless of which endpoint I try. However, there are a couple of interesting headers:

WWW-Authenticate: Bearer realm="{tenant_id}",client_id="00000003-0000-0ff1-ce00-000000000000",trusted_issuers="00000001-0000-0000-c000-000000000000@*,D3776938-3DBA-481F-A652-4BEDFCAB7CD8@*,https://sts.windows.net/*/,https://login.microsoftonline.com/*/v2.0,00000003-0000-0ff1-ce00-000000000000@90140122-8516-11e1-8eff-49304924019b",authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize"

x-ms-diagnostics: 3001000;reason="There has been an error authenticating the request.";category="invalid_client"

These seem promising but the authorization_uri seems to be used for interactive authentication as far as I can tell, and as I've mentioned, that's not an option for me. If there's a way I can use this with just API calls I would love to hear it.

2: From https://stackoverflow.com/a/63335898

POST https://accounts.accesscontrol.windows.net/{tenant_id}/tokens/Oauth/2
Content-Type: application/x-www-form-urlencoded

client_id={client_id}@{tenant_id}&
client_secret={client_secret}&
resource=00000003-0000-0ff1-ce00-000000000000/contoso.sharepoint.com@{tenant_id}&
grant_type=client_credentials

This also gives me a seemingly-valid access_token. However, making the same API call as before, I get {'error': {'code': '-2147024891, System.UnauthorizedAccessException', 'message': {'lang': 'en-US', 'value': 'Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))'}}}. There's also an interesting header: X-MSDAVEXT_Error: 917656; Access+denied.+Before+opening+files+in+this+location%2c+you+must+first+browse+to+the+web+site+and+select+the+option+to+login+automatically.. If that header is taken at face value then, as I've mentioned, it's not doable because it involves an interactive process, presumably for each site.

I wanted to try https://stackoverflow.com/a/63386756 but I couldn't find a way to get a refresh_token without using an interactive/user-driven flow. If it's possible to get a refresh token another way I'd be receptive, since I can already get a working MS Graph access token using the Python MSAL library (ConfidentialClientApplication).

It may be worth noting that checking both of the access tokens in jwt.ms shows that neither of them contain a roles claim. If that's the reason the API requests are failing, please let me know how I can fix it, since I include scopes in the first and while I didn't include it in the second sample, I did try it with scopes and it gave me the same issue.

Thanks!

Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
12,503 questions
SharePoint Development
SharePoint Development
SharePoint: A group of Microsoft Products and technologies used for sharing and managing content, knowledge, and applications.Development: The process of researching, productizing, and refining new or existing technologies.
3,119 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. RaytheonXie_MSFT 36,651 Reputation points Microsoft Vendor
    2024-11-20T08:04:24.37+00:00

    Hi @Max Cutlyp

    I would recommend you to get the acccess token in PostMan by Azure AD for a testing purpose.

    Callback URL:        https://www.getpostman.com/oauth2/callback
    Auth URL :           https://login.microsoftonline.com/common/oauth2/authorize?resource=https://<tenant_name>.sharepoint.com  
    Access Token URL :   https://login.microsoftonline.com/common/oauth2/token  
    Client ID :          <Application_ID>  
    Client Secret :      <KEY>  
    Grant Type :         Authorization Code 
    
    
    

    Will pop up a login window to sign in and then generate the access token and get the SharePoint groups:

    enter image description here

    Here is a nice article for reference

    Use Postman and Azure AD to send REST request to SharePoint Online


    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Max Cutlyp 0 Reputation points
    2024-11-27T06:09:08.2966667+00:00

    Update: I figured it out. There were two issues. The first was that I had used the Microsoft Graph Sites.FullControl.All permission, rather than the SharePoint Sites.FullControl.All permission. See below:

    Incorrect:

    Entra API permissions showing Sites.FullControl.All under the Microsoft Graph category

    Correct:

    Entra API permissions showing Sites.FullControl.All under the SharePoint category

    The second issue was that I was using a client secret rather than a client certificate to authenticate. For some reason the SharePoint API only works with a certificate, not with a secret. If you try with a secret you'll get "Unsupported app only token." Created and uploaded a self-signed cert, now the SharePoint API is working for me.

    https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-self-signed-certificate

    0 comments No comments

  3. RaytheonXie_MSFT 36,651 Reputation points Microsoft Vendor
    2024-11-29T02:12:55.5933333+00:00

    Hi @Max Cutlyp

    I'm glad to hear you solve the problem ,if you have any issue about SharePoint, you are welcome to raise a ticket in this forum.

    By the way, since the Microsoft Q&A community has a policy that "The question author cannot accept their own answer. They can only accept answers by others." and according to the scenario introduced here: Answering your own questions on Microsoft Q&A, I would make a brief summary of this thread:

    [Authenticating to the SharePoint REST API as an Entra app (no user sign-in)?]

    Issue Symptom:

    Connect SharePoint using Rest api with access token, but unable to generate the access token contains correct roles.

    Solution:

    We should register SharePoint Sites.FullControl.All permission instead of Graph in

    Request API Permission

    User's image

    And the SharePoint API only works with a certificate to authenticate, we could refer to following document

    https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-self-signed-certificate


    You could click the "Accept Answer" button for this summary to close this thread, and this can make it easier for other community member's to see the useful information when reading this thread. Thanks for your understanding!


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.