Use Azure AD and Microsoft Graph API to Manage Files in OneDrive
In this post, I describe how to authenticate to OneDrive using any Azure AD account or personal Microsoft account and upload and download files. At the end of this post, you will have an understanding of how to leverage Azure, Microsoft Identity, the Microsoft Graph API, and OneDrive for storage in your application. This post requires a Microsoft account and OneDrive subscription. The free one will work. It also requires an Azure subscription with permissions to modify Active Directory. I have not been charged yet with this example but Azure costs may apply depending on usage. The example is a WPF application written in .NET Core 3.1 using Visual Studio 2019. The full example is available on GitHub.
The Application
The application is a WPF app that lets the user sign-in and sign-out of OneDrive and upload and download a file given a local file path and OneDrive file path.
Create App Registration in Azure AD
The first step is to create the app registration in Azure AD. Sign-in to the azure portal and locate the Azure Active Directory resource.
Click on
App registrations
from the menu on the left side. This presents a list of applications registered with Azure Active Directory.


Click on
New registration
at the top of the page.

Give the new app registration a name. Next select the 3rd option in the Supported account types section labelled Accounts in any organizational directory and personal Microsoft accounts. Finally under the Redirect URI section, select the
Public client/native (mobile & desktop)
option and give it a value of http://localhost
If you're developing the application for .NET Framework, that value needs to be something different. In dotnet core, it was changed to use the browser to sign-in users to desktop apps. Click the Register button at the bottom to create the app registration.

After the registration is created, you will be taken to the app registration overview page. The next step is to add the necessary API permissions and we'll do that in the next section.
Add Microsoft Graph API Permissions
We need to provide the application the necessary permissions within the Microsoft Graph API to manage files in OneDrive. This includes theFiles.ReadWrite
and Files.ReadWrite.All
permissions.
From the app registration overview page, click on
API permissions
under the Manage section in the menu on the left side.

Click
Add a permission
. Select Microsoft Graph under the Microsoft APIs section. Choose delegated permissions. Search for files
in the search box under Select permissions. This will bring up a list of file permissions. This is the required API for managing OneDrive files. Since we want to read and write files, select Files.ReadWrite
and Files.ReadWrite.All
for the permissions. Click the Add permissions button.



After adding the permissions the list of configured permissions should look like the screenshot below.

Configure the WPF App
We're now ready to configure the application and try it out. Clone the repository on GitHub. Open theClientInfo.cs
class and change the ClientId
placeholder to the client ID of the azure app registration created above. That's it! That's the only required configuration for the app. You're now ready to test out the functionality, which I'll detail in the sections that follow.
Sign-In
The application uses the MSAL library to authenticate the user. Upon clickingSign-In
, the app tries to acquire a token silently. If that fails, a MsalUiRequiredException
exception is thrown. This means, we need to acquire the token interactively. This will open a browser window and present a Microsoft sign in prompt. Sign in with your Microsoft account. If successful, a message will be displayed to close the browser window and the application will display token information in the token info
section.




The code of the
Login_Click
method is below. It first tries to acquire a token silently. If that fails, it prompts the user to login. If login is successful, it initializes the graphClient and displays the results.
private async void Login_Click(object sender, RoutedEventArgs e)
{
AuthenticationResult authResult = null;
var app = App.PublicClientApp;
IAccount firstAccount;
var accounts = await app.GetAccountsAsync();
firstAccount = accounts.FirstOrDefault();
InteractiveAuthenticationProvider i = new InteractiveAuthenticationProvider(app, scopes);
try
{
authResult = await app.AcquireTokenSilent(scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
// A MsalUiRequiredException happened on AcquireTokenSilent.
// This indicates you need to call AcquireTokenInteractive to acquire a token
System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
authResult = await app.AcquireTokenInteractive(scopes)
.WithAccount(firstAccount)
.WithParentActivityOrWindow(new WindowInteropHelper(this).Handle) // optional, used to center the browser on the window
.WithPrompt(Microsoft.Identity.Client.Prompt.SelectAccount)
.ExecuteAsync();
}
catch (MsalException msalex)
{
ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
}
}
catch (Exception ex)
{
ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
return;
}
if (authResult != null)
{
_graphClient = new GraphServiceClient(i);
ResultText.Text = await GetHttpContentWithToken(graphAPIEndpoint, authResult.AccessToken);
DisplayBasicTokenInfo(authResult);
this.SignOutButton.Visibility = Visibility.Visible;
this.SignInButton.Visibility = Visibility.Collapsed;
}
}
Upload to OneDrive
The next function is to upload a file to OneDrive. In thefile to upload
path, add the path to a file on your local device. Add a path to where you want to upload it to in OneDrive in the textbox labelled OneDrive path
. If you want to upload it to the root of OneDrive, leave this textbox blank.Click the Upload
button and a message will display in the API Call Results
with how many bytes are uploaded and an item ID when the upload is complete. Below is the method for uploading a file to OneDrive.
private async void UploadButton_Click(object sender, RoutedEventArgs e)
{
ResultText.Text = "";
if (_graphClient == null)
{
ResultText.Text = "Please sign-in first";
return;
}
try
{
string fileName = Path.GetFileName(FilePathUpload.Text);
using (var fileStream = System.IO.File.OpenRead(FilePathUpload.Text))
{
// Use properties to specify the conflict behavior
// in this case, replace
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "@microsoft.graph.conflictBehavior", "replace" }
}
};
// Create the upload session
// itemPath does not need to be a path to an existing item
var uploadSession = await _graphClient.Me.Drive.Root
.ItemWithPath(OneDrivePath.Text + "/" + fileName)
.CreateUploadSession(uploadProps)
.Request()
.PostAsync();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask =
new LargeFileUploadTask<DriveItem>(uploadSession, fileStream, maxSliceSize);
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog =>
{
ResultText.Text = $"Uploaded {prog} bytes of {fileStream.Length} bytes";
});
try
{
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
// The ItemResponse object in the result represents the
// created item.
ResultText.Text = $"Upload complete, item ID: {uploadResult.ItemResponse.Id}";
}
else
{
ResultText.Text = "Upload failed";
}
}
catch (ServiceException ex)
{
ResultText.Text = $"Error uploading: {ex.ToString()}";
}
}
}
catch (Microsoft.Graph.ServiceException ex)
{
ResultText.Text = ex.Error.ToString();
}
catch (MsalUiRequiredException ex)
{
ResultText.Text = "Please sign-in first";
}
catch (Exception ex)
{
ResultText.Text = ex.Message;
}
}
Download From OneDrive
We can also download a file from OneDrive. Add the full path to a file in OneDrive in the textbox labelledOneDrive file
. If the file is in the OneDrive root, only the file name needs to be entered. Next, provide a destination folder where you want to download the file to in the Local file path
textbox and click the Download
button. The API Call Results
field will show a status of Downloading... and Download Complete when it's done. Browse to the local destination folder and the file, with the same file name as OneDrive will be available.
The code for the download method is below.
private async void DownloadButton_Click(object sender, RoutedEventArgs e)
{
ResultText.Text = "";
if (_graphClient == null)
{
ResultText.Text = "Please sign-in first";
return;
}
try
{
var driveResult = await _graphClient.Me.Drive.Request().GetAsync();
var itemResult = await _graphClient.Me.Drive.Root.ItemWithPath(FilePathDownload.Text).Request().GetAsync();
ResultText.Text = "Downloading...";
var stream = await _graphClient.Me.Drive.Items[itemResult.Id].Content.Request().GetAsync();
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
System.IO.File.WriteAllBytes(LocalFilePath.Text + "\\" + itemResult.Name, memoryStream.ToArray());
}
ResultText.Text = "Download Complete";
}
catch (Microsoft.Graph.ServiceException ex)
{
ResultText.Text = ex.Error.ToString();
}
catch (MsalUiRequiredException ex)
{
ResultText.Text = "Please sign-in first";
}
catch (Exception ex)
{
ResultText.Text = ex.Message;
}
}
Sign-out
The final feature of the app is the ability to sign out. Once signed in theSign-In
button changes to a Sign-Out
button. To sign out the Microsoft Identity public application object removes the account and you will be required to sign-in again. The sign out method is below.
private async Task SignOut()
{
var accounts = await App.PublicClientApp.GetAccountsAsync();
if (accounts.Any())
{
try
{
await App.PublicClientApp.RemoveAsync(accounts.FirstOrDefault());
_graphClient = null;
this.ResultText.Text = "User has signed-out";
this.SignOutButton.Visibility = Visibility.Collapsed;
this.SignInButton.Visibility = Visibility.Visible;
}
catch (MsalException ex)
{
ResultText.Text = $"Error signing-out user: {ex.Message}";
}
}
}