Office E5 OneDrive API usage guide: registration + key acquisition + temporary upload link + fragmentation

Journey of Imagination: My original blog is completely handcrafted, absolutely not ported, and there is no possibility of repetition on the entire network; I have no team, and I only share it for technology enthusiasts, and all content does not involve advertisements. All my articles are only published on CSDN, Nuggets and personal blog (must be the domain name of Fantastic Journey), otherwise all are pirated articles!

This article mainly explains that I have registered an E5 developer account, hoping to use E5’s OneDrive as the file storage of the website. If you want users to get their OneDrive files after they log in to their Microsoft accounts, this article is for reference only.

This is probably the most tiring article I have written, because I started researching this thing and the website was online for half a year. The original intention of writing this article is that I really don’t want to see everyone being discouraged by Microsoft’s obscure documents, so the content may not be comprehensive or there may be errors, but it is guaranteed to be available.

In fact, Cloudreve’s Add Storage Policy page also helped me a lot in my research

In particular, although the whole process can be completed smoothly in theory, if you encounter problems, you can try to cast a little magic on your network.

Create an application

First open this web page, and log in to your Microsoft account that will be used to store files (it should be an account with OneDrive enabled in your E5, not a personal account):

https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview

If it is the 21Vianet version, the link is
https://portal.azure.cn/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview
The 21Vianet account has not been tested, and the method in this article is not guaranteed to be available

The page opened after login should look like this, if not, please click the above link again

Select App Registrations on the left, then select New Registration:

In the newly opened page, name it whatever you like, Supported account types select the third account in any organizational directory (any Azure AD directory - multi-tenant) and a personal Microsoft account (eg , Skype, Xbox), **Redirect URI (optional)** select Web, fill in http://localhost/ for the value

Click the “Register” button, and after the loading is complete, you will enter the home page of the application we just registered. We copy the Application (Client) ID and save it.

Click “Certificate and Password”, and then select “New Client Password”. The deadline can be selected according to your own needs (currently Microsoft does not provide a permanent valid option. In order to prevent frequent updates, it is recommended to directly select up to 24 months), and the instructions are optional.

Then we can see the created value and secret ID, Please be sure to save it well, after closing the page, you can no longer see the “value”.

At this point, our application registration process is complete, and now we have the application’s Client ID and Client Password (the “value” seen in the previous step)

Get Authentication Token

Official document: https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online

The official has explained that there are two ways of token flow and code flow in this step. Although the former is simpler in process, the obtained access_token is only valid for 8 hours. After the expiration, you need to manually log in to the Microsoft account again to continue calling the API (regardless of your wanting to use a crawler to operate Microsoft Login), which is obviously not suitable for our script.

Construct and visit this link in a browser:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id={client_id}
 &scope={scope}
 &response_type=code
 &redirect_uri=http://localhost/

Among them, client_id is the client ID we obtained in the previous step, and scope refers to the scope. The official OneDrive detailed introduction is here

It is recommended to fill in files.readwrite.all offline_access like me! If not, I don’t guarantee that your next steps will be smooth.

So the URI I constructed is (the last part is omitted in client_id)

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=2dde8638-6489-4fc2-bfec-xxxxxxxxxxxx & amp;scope=files.readwrite.all offline_access & amp;response_type= code & redirect_uri=http://localhost/

After accessing in the browser, a classic Microsoft account login interface opens, just log in to the account we want to use to store files normally

After authorization, it will jump to localhost. At this time, the webpage should not be opened because it does not exist, but it doesn’t matter. We only need to copy the content in the address bar. The structure should look like this:

http://localhost/
?code=xxxxx
 &session_state=xxxxx#

Here we record the code value and construct such an access:

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

client_id={client_id} & amp; redirect_uri=http://localhost/ & amp; client_secret={client_secret}
 &code={code} &grant_type=authorization_code

Corresponding Python code:

from requests import *

r = post('https://login.microsoftonline.com/common/oauth2/v2.0/token',
         data={<!-- -->
             'client_id': '{client_id}',
             'redirect_uri': 'http://localhost/',
             'client_secret': '{client_secret}',
             'code': '{code}',
             'grant_type': 'authorization_code'
         })

print(r. text)

We have already obtained the three values that need to be replaced before, so I won’t repeat them here.

If the group is normal, you will get a JSON format similar to the following:

{<!-- -->
    "token_type":"bearer",
    "expires_in": 5126,
    "ext_expires_in": 5126,
    "scope":"wl.basic onedrive.readwrite",
    "access_token":"EwCo...AA==",
    "refresh_token":"eyJh...9323"
}

It may also be slightly different, it doesn’t matter, the most important access_token and refresh_token are there.

At this point, we can happily call the API with the access_token. But as you can see, this token has a validity period. If it expires, we need to exchange it for a new token with refresh_token

Therefore, the refresh_token needs to be saved by the program for future use, and for the sake of program efficiency and to prevent unnecessary troubles, it is recommended to save the time when each access_token is obtained, and Use the time information to check whether the token has expired before each request is sent, and do not wait for the API to report an error before reapplying for the token

The request format for redeeming a new access_token is as follows

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

client_id={client_id} & amp; redirect_uri=http://localhost/ & amp; client_secret={client_secret}
 &refresh_token={refresh_token} &grant_type=refresh_token

The response format is still a JSON similar to the above, including the new access_token and refresh_token

Upload files

From here, under normal circumstances, requests that require access_token should be sent by the server to ensure security.

OneDrive provides two upload modes. The first one is to directly use access_token to authenticate and upload. This method is not suitable for users and only supports a maximum of 4MB files, so I won’t go into details. You can do it yourself if necessary to see.

Here we introduce the second method, that is, first use access_token to obtain an upload session, and then the client can directly upload without authentication after getting the upload session.

Construct the following request:

POST https://graph.microsoft.com/v1.0/me/drive/root:{path}:/createUploadSession
Authorization: Bearer {access_token}
Content-Type: application/json

{
    "item": {
        "@microsoft.graph.conflictBehavior": "rename"
    }
}

Here you need to replace the two parameters path and access_token, the latter is commonplace, the former is the storage path of our files on OneDrive, note that there needs to be a slash in the front , not used later, for example /test.txt

For more parameters that can be specified in the body, please refer to the official documentation. The "@microsoft.graph.conflictBehavior": "rename" we specified here means that if there is a file with the same name, it will be Newly uploaded files are renamed.

Example Python:

path = '/test.txt'
access_token = 'xxxxxx'

r = post(
    f'https://graph.microsoft.com/v1.0/me/drive/root:{<!-- -->path}:/createUploadSession',
    headers={<!-- -->
        'Authorization': 'Bearer' + access_token,
        'Content-Type': 'application/json'
    },
    data=json.dumps({<!-- -->
        'item': {<!-- -->
            '@microsoft.graph.conflictBehavior': 'rename',
        },
    }),
)
print(r.json())

Normally you will receive something like this in return:

{<!-- -->
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.uploadSession",
    "expirationDateTime": "2023-03-25T07:41:03.482Z",
    "nextExpectedRanges": [
        "0-"
    ],
    "uploadUrl": "https://yxzl-my.sharepoint.com/..."
}

The following things should be understood by reading the official documents, and you can read them yourself. The uploadUrl you just got in the JSON is the uploadUrl value you received in the createUploadSession response as stated in the documentation.

In order to prevent everyone from staring, I paste a very important sentence in the document:

Note: If an app splits a file into multiple byte ranges, the size of each byte range must be a multiple of 320 KiB (327,680 bytes). Using a fragment size not divisible by 320 KiB caused an error when committing some files.

Here is a code example for uploading files with the Python client:

from requests import *

file = open('D:/Desktop/1.txt', 'rb').read()

length = len(file)

r = put(
    'https://yxzl-my.sharepoint.com/...',
    data=file,
    headers={<!-- -->
        'Content-Length': f'{<!-- -->length}',
        'Content-Range': f'bytes 0-{<!-- -->length - 1}/{<!-- -->length}'
    })

print(r)
print(r. text)

Upload example with JavaScript Axios:

import {<!-- --> Axios } from "axios";

const uploadFile = async (file, uploadUrl) => {<!-- -->
    /*
    file: the file object to upload
    uploadUrl: the obtained upload session
    */
    const size = file.size; // file size
    const piece = 1024 * 1024 * 10; // piece size
    let start = 0; // the start byte of the current fragment
    let end = Math.min(piece, size); // the end byte of the current piece
    let cnt = Math.ceil(size / piece); // number of pieces

    while (start < size - 1) {<!-- -->
        await Axios.put(uploadUrl, this.uploadFile.slice(start, end), {<!-- -->
            headers: {<!-- -->
                "Content-Range": `bytes ${<!-- -->start}-${<!-- -->end - 1}/${<!-- -->size}`,
            },
        });
        cnt--;
        if (cnt === 0) {<!-- -->
            alert("uploaded successfully");
            return;
        }
        start = end;
        end = Math.min(start + piece, size);
    }
};

The successful upload returns as follows (if the fragment is the return of the last fragment), but it is useless

{<!-- -->
    "@odata.context": "https://yxzl-my.sharepoint.com/.../$metadata#items/$entity",
    "@content.downloadUrl": "https://yxzl-my.sharepoint.com/.../download.aspx?UniqueId=...",
    "createdBy": {<!-- -->
        "application": {<!-- -->
            "id": "...",
            "displayName": "OneDriveTest"
        },
        "user": {<!-- -->
            "email": "[email protected]",
            "id": "...",
            "displayName": "Wang Zihan"
        }
    },
    "createdDateTime": "2023-03-25T07:26:03Z",
    "eTag": ""{F51D59DC-4D2F-466F-9CF0-E9895FF154C2},3"",
    "id": "...",
    "lastModifiedBy": {<!-- -->
        "application": ...,
        "user": ...
    },
    "lastModifiedDateTime": "2023-03-25T07:39:52Z",
    "name": "a.txt",
    "parentReference": {<!-- -->
        "driveType": "business",
        "driveId": "b!...",
        "id": "...",
        "path": "/drive/root:"
    },
    "webUrl": "https://yxzl-my.sharepoint.com/.../a.txt",
    "cTag": ""c:{F51D59DC-4D2F-466F-9CF0-E9895FF154C2},2"",
    "file": {<!-- -->
        "hashes": {<!-- -->
            "quickXorHash": "..."
        },
        "irmEffectivelyEnabled": false,
        "irmEnabled": false,
        "mimeType": "text/plain"
    },
    "fileSystemInfo": {<!-- -->
        "createdDateTime": "2023-03-25T07:26:03Z",
        "lastModifiedDateTime": "2023-03-25T07:39:52Z"
    },
    "size": 150
}

If you want to say that it is useful, maybe the donwloadUrl is a little bit, but that thing is not valid for a long time

Get file download link

This is very simple. The method introduced here is to first obtain the detailed information of a file, and then download it through the returned @microsoft.graph.downloadUrl.

Construct request:

GET https://graph.microsoft.com/v1.0/me/drive/items/root:{path}:',
Authorization: Bearer {access_token}

The path here should be the same as the one used when creating the upload session above (there must be a slash in front), and there is no need to mention the access_token.

This request returns a huge JSON, which gives you almost all the information of this file, but we only need to get the item @microsoft.graph.downloadUrl. Its value is a URL. Accessing this URL can download files without authentication, but the validity period is only one hour.

Reference link:

  • Get a file or folder – OneDrive API – OneDrive dev center | Microsoft Learn
  • DriveItem – OneDrive API – OneDrive dev center | Microsoft Learn (this is a description of the returned JSON values)