Access OneDrive files in V0-generated Next.js apps via the Microsoft Graph API. Register an Azure app for OAuth2 credentials, create a Next.js API route that calls Microsoft Graph endpoints for file listing and operations, and authenticate users with Microsoft OAuth2 flow. Store your Azure client secret as a Vercel environment variable and your client ID with the NEXT_PUBLIC_ prefix for the OAuth redirect.
Integrating OneDrive File Storage with V0-Generated Next.js Applications
OneDrive is the file storage backbone for Microsoft 365 organizations — documents, spreadsheets, PDFs, and project files all live in OneDrive for millions of enterprise users. For developers building V0-generated applications that serve Microsoft-centric organizations, OneDrive integration enables powerful workflows: letting users attach OneDrive files to records, displaying OneDrive folder contents in custom portals, building document approval workflows, and enabling seamless file sharing from within your application.
All OneDrive access goes through the Microsoft Graph API, a unified REST API endpoint (`graph.microsoft.com/v1.0`) that covers OneDrive files, SharePoint, Outlook, Teams, and all other Microsoft 365 services. Authentication uses OAuth2 with user delegated permissions — users sign in with their Microsoft account and grant your app permission to access their OneDrive. Your app receives an access token that it uses in Authorization headers for Microsoft Graph API calls.
In the V0 workflow, you generate the file browser or file picker UI using V0's chat interface, create Next.js API routes that proxy Microsoft Graph API calls using the user's access token, and handle the OAuth2 authentication flow to get that token. The Azure app registration (which takes about 10 minutes) provides the client credentials your application uses to identify itself to Microsoft's authentication system.
Integration method
OneDrive integration uses the Microsoft Graph API, accessed via OAuth2 user authentication flow. Your Next.js app initiates Microsoft login to get an access token, which your API routes use to call the Microsoft Graph API for file operations (list, upload, download, share). The OAuth2 client secret is stored as a server-only Vercel environment variable, while the client ID is public.
Prerequisites
- A Microsoft Azure account (free — sign up at portal.azure.com) to register your application
- A OneDrive account (Microsoft personal or Microsoft 365 work account) for testing
- Azure app registration with Microsoft Graph Files.ReadWrite.All delegated permission
- Azure app client ID and client secret from the Azure portal app registration
- A V0 account at v0.dev and a Vercel account with a deployed URL for the OAuth2 redirect
Step-by-step guide
Register Your App in Azure Portal
Register Your App in Azure Portal
Before writing any code, you need to register your application in Azure Active Directory to get the OAuth2 credentials that allow your app to request access to users' OneDrive accounts. This is a one-time setup that takes about 10 minutes. Go to portal.azure.com and sign in with your Microsoft account. Navigate to Azure Active Directory → App registrations → New registration. Give your app a name (e.g., 'My V0 App OneDrive'). Under 'Supported account types', select 'Accounts in any organizational directory and personal Microsoft accounts (Multitenant + personal)' — this allows both personal OneDrive and Microsoft 365 work accounts to connect. For 'Redirect URI', select 'Web' and enter your Vercel deployment URL followed by your callback path: `https://your-app.vercel.app/api/auth/callback/microsoft`. For local development, add a second redirect URI: `http://localhost:3000/api/auth/callback/microsoft`. After registration, note the Application (client) ID displayed on the overview page — this is your `NEXT_PUBLIC_AZURE_CLIENT_ID`. Then go to Certificates & secrets → New client secret. Give it a description and set an expiration (12 or 24 months). Copy the secret value immediately — it is only shown once. This is your `AZURE_CLIENT_SECRET`. Finally, go to API permissions → Add a permission → Microsoft Graph → Delegated permissions. Search for and add: `Files.ReadWrite` (read and write user's OneDrive files), `User.Read` (read user profile), and optionally `offline_access` (to get a refresh token for long-lived sessions). Click 'Grant admin consent' if this is a work account and you have admin rights.
Build a settings page with a Microsoft connection section showing a Microsoft logo, 'Connect OneDrive' heading, description 'Access your OneDrive files directly within this application', and a blue 'Sign in with Microsoft' button. When connected, show a green checkmark, the connected account's name and email, a 'Files accessible' badge, and a 'Disconnect' button.
Paste this in V0 chat
1// Azure App Registration summary:2// 1. portal.azure.com → Azure Active Directory → App registrations → New registration3// 2. Name: Your App Name4// 3. Supported account types: Multitenant + personal (allows both work and personal OneDrive)5// 4. Redirect URI: https://your-app.vercel.app/api/auth/callback/microsoft6// 5. After registration: note Application (client) ID7// 6. Certificates & secrets → New client secret → copy the VALUE (not the ID)8// 7. API permissions → Add → Microsoft Graph → Delegated:9// - Files.ReadWrite10// - User.Read11// - offline_accessPro tip: Set an appropriate client secret expiration (12 or 24 months) and add a calendar reminder to rotate it before it expires. Expired client secrets cause all user authentications to fail until you generate and deploy a new secret.
Expected result: An Azure app registration exists with your application's client ID and client secret. Microsoft Graph Files.ReadWrite and User.Read permissions are configured. Your Vercel redirect URI is registered in the app.
Implement the OAuth2 Authentication Flow
Implement the OAuth2 Authentication Flow
OneDrive access requires users to sign in with Microsoft and grant your app permission to access their files. Implement the OAuth2 authorization code flow with two API routes: one that redirects to Microsoft's login page, and one that handles the callback and exchanges the authorization code for an access token. The authorization URL for Microsoft OAuth2 is `https://login.microsoftonline.com/common/oauth2/v2.0/authorize` with parameters: `client_id`, `response_type=code`, `redirect_uri`, `scope` (space-separated list of permissions: `Files.ReadWrite User.Read offline_access`), and a `state` parameter (a random string for CSRF protection). Create `app/api/auth/microsoft/route.ts` that generates the authorization URL with a random state parameter (stored in a secure HTTP-only cookie for verification), and redirects the user to Microsoft's login page. Create `app/api/auth/callback/microsoft/route.ts` that handles the OAuth callback. It receives the authorization code and state parameter, verifies the state matches the cookie (CSRF protection), exchanges the code for tokens by calling `POST https://login.microsoftonline.com/common/oauth2/v2.0/token` with your client credentials, and stores the access token and refresh token securely (in an HTTP-only cookie or your database). For a simpler approach, use the `next-auth` library with the Microsoft Entra ID (Azure AD) provider — it handles the entire OAuth2 flow including CSRF protection, token storage, and refresh token rotation automatically. Ask V0 to generate the NextAuth configuration for Microsoft provider.
Add a 'Connect with Microsoft' button to the app that, when clicked, redirects to /api/auth/microsoft to start the OAuth flow. After successful connection, redirect back to the dashboard with a success toast: 'OneDrive connected successfully'. Show the connected Microsoft account email in the top navigation bar.
Paste this in V0 chat
1// app/api/auth/microsoft/route.ts — Initiate OAuth2 flow2import { NextResponse } from 'next/server'3import crypto from 'crypto'45export async function GET() {6 const clientId = process.env.NEXT_PUBLIC_AZURE_CLIENT_ID!7 const redirectUri = `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/callback/microsoft`8 const state = crypto.randomBytes(16).toString('hex')910 const authUrl = new URL('https://login.microsoftonline.com/common/oauth2/v2.0/authorize')11 authUrl.searchParams.set('client_id', clientId)12 authUrl.searchParams.set('response_type', 'code')13 authUrl.searchParams.set('redirect_uri', redirectUri)14 authUrl.searchParams.set('scope', 'Files.ReadWrite User.Read offline_access')15 authUrl.searchParams.set('state', state)16 authUrl.searchParams.set('response_mode', 'query')1718 const response = NextResponse.redirect(authUrl.toString())19 response.cookies.set('oauth_state', state, {20 httpOnly: true,21 secure: process.env.NODE_ENV === 'production',22 maxAge: 600, // 10 minutes23 path: '/',24 })25 return response26}2728// app/api/auth/callback/microsoft/route.ts — Handle OAuth callback29export async function GET(request: Request) {30 const { searchParams } = new URL(request.url)31 const code = searchParams.get('code')32 const state = searchParams.get('state')3334 const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {35 method: 'POST',36 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },37 body: new URLSearchParams({38 client_id: process.env.NEXT_PUBLIC_AZURE_CLIENT_ID!,39 client_secret: process.env.AZURE_CLIENT_SECRET!,40 code: code!,41 redirect_uri: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/callback/microsoft`,42 grant_type: 'authorization_code',43 }),44 })4546 const tokens = await tokenResponse.json()4748 const response = NextResponse.redirect(`${process.env.NEXT_PUBLIC_APP_URL}/dashboard`)49 response.cookies.set('ms_access_token', tokens.access_token, {50 httpOnly: true,51 secure: true,52 maxAge: tokens.expires_in,53 path: '/',54 })55 if (tokens.refresh_token) {56 response.cookies.set('ms_refresh_token', tokens.refresh_token, {57 httpOnly: true,58 secure: true,59 maxAge: 60 * 60 * 24 * 30, // 30 days60 path: '/',61 })62 }63 return response64}Pro tip: For production apps, store the Microsoft access token and refresh token in your database (associated with the user's account) rather than in cookies. This allows server-side token refresh and works with multiple browser sessions.
Expected result: Clicking the Microsoft sign-in button redirects to Microsoft's login page. After signing in and granting permissions, the user is redirected back to your app with the access token stored securely in an HTTP-only cookie.
Create OneDrive File Operation API Routes
Create OneDrive File Operation API Routes
With the OAuth2 flow working, create the Microsoft Graph API routes for OneDrive file operations. The Microsoft Graph base URL is `https://graph.microsoft.com/v1.0`. All requests use the user's access token in the Authorization header: `Authorization: Bearer {accessToken}`. Create a route for listing files at `app/api/onedrive/files/route.ts`. This route reads the access token from the request cookie, calls the Microsoft Graph `/me/drive/root/children` endpoint to list root folder contents, and returns file metadata. For subfolder navigation, accept a `folderId` query parameter and call `/me/drive/items/{folderId}/children` instead. Create a file upload route at `app/api/onedrive/upload/route.ts`. The Microsoft Graph API supports simple upload for files under 4MB using `PUT /me/drive/root:/{filename}:/content` with the file content in the request body. For larger files, use the resumable upload session API. Accept a FormData POST with the file and target path. Create a download URL route at `app/api/onedrive/download/route.ts`. Rather than proxying file content through your API (which adds latency for large files), fetch the `@microsoft.graph.downloadUrl` property from the file item, which is a pre-authorized direct download URL valid for a few hours. Create a share link route at `app/api/onedrive/share/route.ts` that calls `POST /me/drive/items/{itemId}/createLink` with a type of 'view' or 'edit' to generate shareable links for OneDrive files.
Build a file browser component that shows a toolbar with Back button (disabled at root), current folder path, Upload button, and New Folder button. Below, show a list of folders (yellow folder icon) followed by files (type-specific icon), each row showing name, size (files only), modified date, and action buttons: Download and Share. Selecting a folder navigates into it. Include an empty state with a message and upload button for empty folders.
Paste this in V0 chat
1// app/api/onedrive/files/route.ts2import { NextRequest, NextResponse } from 'next/server'34export async function GET(request: NextRequest) {5 const accessToken = request.cookies.get('ms_access_token')?.value6 if (!accessToken) {7 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 })8 }910 const { searchParams } = new URL(request.url)11 const folderId = searchParams.get('folderId')1213 const endpoint = folderId14 ? `https://graph.microsoft.com/v1.0/me/drive/items/${folderId}/children`15 : 'https://graph.microsoft.com/v1.0/me/drive/root/children'1617 const params = new URLSearchParams({18 '$select': 'id,name,size,lastModifiedDateTime,file,folder,@microsoft.graph.downloadUrl,webUrl',19 '$orderby': 'name',20 '$top': '100',21 })2223 const response = await fetch(`${endpoint}?${params}`, {24 headers: { Authorization: `Bearer ${accessToken}` },25 })2627 if (response.status === 401) {28 return NextResponse.json({ error: 'Token expired', code: 'TOKEN_EXPIRED' }, { status: 401 })29 }3031 const data = await response.json()32 return NextResponse.json({33 files: data.value || [],34 nextLink: data['@odata.nextLink'] || null,35 })36}3738// app/api/onedrive/upload/route.ts39export async function POST(request: NextRequest) {40 const accessToken = request.cookies.get('ms_access_token')?.value41 if (!accessToken) {42 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 })43 }4445 const formData = await request.formData()46 const file = formData.get('file') as File47 const folderPath = formData.get('folderPath') as string || ''4849 if (!file) {50 return NextResponse.json({ error: 'No file provided' }, { status: 400 })51 }5253 const fileBuffer = await file.arrayBuffer()54 const uploadPath = folderPath55 ? `/me/drive/root:/${folderPath}/${file.name}:/content`56 : `/me/drive/root:/${file.name}:/content`5758 const uploadResponse = await fetch(`https://graph.microsoft.com/v1.0${uploadPath}`, {59 method: 'PUT',60 headers: {61 Authorization: `Bearer ${accessToken}`,62 'Content-Type': file.type || 'application/octet-stream',63 },64 body: fileBuffer,65 })6667 const uploadedFile = await uploadResponse.json()68 return NextResponse.json({ file: uploadedFile }, { status: uploadResponse.ok ? 200 : 400 })69}Pro tip: For listing large directories (folders with hundreds of files), implement pagination using the @odata.nextLink URL that Microsoft Graph returns. Pass this URL directly to the next fetch call to get the next page of results.
Expected result: The file browser shows the user's OneDrive root folder contents. Clicking a folder navigates into it. The upload button accepts file selection and uploads to the current folder. The download and share buttons generate the correct links for each file.
Configure Vercel Environment Variables and Deploy
Configure Vercel Environment Variables and Deploy
Add all Azure app credentials to Vercel. Go to Vercel Dashboard → your project → Settings → Environment Variables. Add `NEXT_PUBLIC_AZURE_CLIENT_ID` with your Azure app's Application (client) ID. This needs the `NEXT_PUBLIC_` prefix because the OAuth2 authorization URL is constructed client-side or in your API route. Add `AZURE_CLIENT_SECRET` without any `NEXT_PUBLIC_` prefix — this server-only credential is used to exchange authorization codes for tokens and must never reach the browser. Add `NEXT_PUBLIC_APP_URL` with your Vercel deployment URL (e.g., `https://your-app.vercel.app`) — this is used as the base for OAuth redirect URIs. Ensure your Azure app registration includes your Vercel deployment URL as an allowed redirect URI. In the Azure portal → your app registration → Authentication → Redirect URIs, add `https://your-app.vercel.app/api/auth/callback/microsoft`. If you have preview deployments on different URLs, you can add those too, or use a single stable production URL. After deployment, test the full OAuth2 flow: click the Microsoft sign-in button, sign in with your Microsoft account, grant permissions when prompted, and verify you are redirected back to your app with access to your OneDrive files. The file browser should list your OneDrive root folder contents. For enterprise applications where users belong to a specific Microsoft 365 tenant (company), replace `common` in the OAuth2 URLs with your specific tenant ID for enhanced security and to restrict access to only your organization's accounts.
Add an upload progress indicator to the file browser. When uploading files, show a sticky bottom bar that lists each uploading file by name with an individual progress bar. When all uploads complete, show a green success banner that auto-dismisses after 3 seconds and refreshes the file list to show the newly uploaded files.
Paste this in V0 chat
1// Vercel Dashboard → Settings → Environment Variables:2// NEXT_PUBLIC_AZURE_CLIENT_ID = your-azure-app-client-id3// AZURE_CLIENT_SECRET = your-azure-client-secret-value4// NEXT_PUBLIC_APP_URL = https://your-app.vercel.app56// Azure Portal → App Registration → Authentication:7// Add redirect URI: https://your-app.vercel.app/api/auth/callback/microsoft8// Also add: http://localhost:3000/api/auth/callback/microsoft (for local dev)910// Microsoft Graph API limits:11// Simple upload (PUT): max 4 MB per file12// Large file upload sessions: up to 250 GB13// Rate limit: 10,000 requests per 10 minutes per app per tenantPro tip: For files larger than 4MB, use Microsoft Graph's large file upload session API: POST /me/drive/root:/{filename}:/createUploadSession, then PUT the file in chunks to the returned uploadUrl. V0 can generate the chunked upload logic if you ask for it explicitly.
Expected result: The full OneDrive integration is live on Vercel. Users can sign in with Microsoft, browse their OneDrive folders, upload files, download files, and generate share links — all within your V0-generated application.
Common use cases
Document Portal for Teams
A project management app lets team members browse their OneDrive folders to attach documents to tasks and projects. The V0-generated file picker shows folder tree navigation, file thumbnails, and a select button that adds the chosen OneDrive file as an attachment record.
Build a file picker modal component that shows a breadcrumb path at the top (Home > Projects > Q1 Reports), a folder/file list below with type icon, file name, size, and modified date columns, a search bar to filter files by name, and Back and Select buttons at the bottom. Selected files show a blue checkmark overlay on the file icon.
Copy this prompt to try it in V0
Business Report Dashboard with OneDrive Data
An analytics dashboard automatically reads Excel files from a shared OneDrive folder to display business metrics without manual data uploads. The Next.js API route lists recent files in a specific folder and fetches file metadata, while users can click to open files directly in Microsoft 365.
Create a reports dashboard showing a grid of document cards, each with a file type icon (PDF/Excel/Word), file name, last modified date, modified by user name, and file size. Include a folder selector dropdown at the top to switch between OneDrive folders. Each card has an Open in Office and Download button on hover.
Copy this prompt to try it in V0
Customer File Submission Portal
A client portal lets customers upload deliverables directly to a specific OneDrive folder designated for their account. The V0-generated upload form handles file selection and shows upload progress, while the API route uploads the file to the correct OneDrive folder using a service account.
Build a file submission form with a large drag-and-drop upload area with dotted border that accepts PDF, Excel, and Word documents up to 25MB, a file list below showing queued files with name, size, and a remove button, an upload progress bar for each file during upload, and a submitted files table at the bottom showing previously submitted files with date and status.
Copy this prompt to try it in V0
Troubleshooting
OAuth redirect shows 'AADSTS50011: The reply URL specified does not match' error
Cause: The redirect URI your app is sending to Microsoft does not match any of the redirect URIs registered in your Azure app registration. Even a trailing slash difference or HTTP vs HTTPS mismatch causes this error.
Solution: Go to Azure Portal → your app registration → Authentication → Redirect URIs and ensure the exact URL your application sends (including protocol, domain, port, and path) is listed. Your NEXT_PUBLIC_APP_URL must match exactly. For local development, add http://localhost:3000/api/auth/callback/microsoft.
Microsoft Graph API returns 401 InvalidAuthenticationToken after files worked previously
Cause: Microsoft Graph access tokens expire after 1 hour by default. The access token stored in the cookie has expired.
Solution: Implement token refresh logic using the refresh token. When a 401 is received from Graph API, call the token endpoint with grant_type=refresh_token to get a new access token. If no refresh token exists (offline_access scope was not requested), the user must sign in again.
1// Token refresh logic in a utility function2async function refreshAccessToken(refreshToken: string): Promise<string> {3 const response = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {4 method: 'POST',5 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },6 body: new URLSearchParams({7 client_id: process.env.NEXT_PUBLIC_AZURE_CLIENT_ID!,8 client_secret: process.env.AZURE_CLIENT_SECRET!,9 refresh_token: refreshToken,10 grant_type: 'refresh_token',11 scope: 'Files.ReadWrite User.Read offline_access',12 }),13 })14 const tokens = await response.json()15 return tokens.access_token16}Files upload succeeds via the API but shows as empty or 0 bytes in OneDrive
Cause: The file content is being sent as text or is being corrupted during the FormData parsing. The upload endpoint requires the raw binary file buffer, not a base64-encoded string or text content.
Solution: Read the File object from FormData as an ArrayBuffer using file.arrayBuffer() before sending to Microsoft Graph. Ensure the Content-Type header matches the actual file MIME type.
1// Correct file upload - use arrayBuffer()2const file = formData.get('file') as File3const fileBuffer = await file.arrayBuffer() // Get raw bytes4const uploadResponse = await fetch(uploadUrl, {5 method: 'PUT',6 headers: {7 Authorization: `Bearer ${accessToken}`,8 'Content-Type': file.type || 'application/octet-stream',9 'Content-Length': file.size.toString(),10 },11 body: fileBuffer, // Send raw bytes, not text12})Best practices
- Request only the Microsoft Graph permissions your application actually needs — start with Files.Read if you only need read access, and upgrade to Files.ReadWrite only when users need to upload or modify files.
- Implement access token refresh logic using the offline_access scope and refresh token — without this, users have to sign in again every hour when the access token expires.
- Never store the Azure client secret in client-side code or with the NEXT_PUBLIC_ prefix — this secret allows anyone who has it to impersonate your application in OAuth flows.
- Use the $select query parameter in Microsoft Graph requests to fetch only the file properties you need — this reduces response size and improves performance for file listings.
- Handle the 429 Too Many Requests response from Microsoft Graph by implementing exponential backoff — the Retry-After header tells you how many seconds to wait.
- For enterprise apps accessing organization OneDrive (SharePoint), replace 'common' in OAuth2 URLs with the specific tenant ID to restrict authentication to your organization.
- Store the access token in an HTTP-only secure cookie or server-side database — never in localStorage, as JavaScript access to the token creates XSS vulnerability.
Alternatives
Backblaze B2 uses S3-compatible API with much simpler authentication (API key, no OAuth2), making it faster to integrate for apps that don't specifically need Microsoft ecosystem integration.
Backblaze B2's native API offers the same object storage without OAuth2 complexity, better suited for programmatic file storage rather than user-connected personal cloud drives.
Microsoft Teams integration via Microsoft Graph also accesses OneDrive-backed file storage and extends to team messaging — useful when you need both file storage and collaboration features.
Frequently asked questions
Can I access OneDrive files without requiring users to sign in (service account access)?
Yes, using the client credentials OAuth2 flow (app-only authentication) instead of user-delegated authentication. This requires the Azure app to have Application permissions (not Delegated) on Microsoft Graph, and requires admin consent from a Microsoft 365 tenant admin. App-only access can access SharePoint/OneDrive sites but is limited compared to user-delegated access and requires proper admin approval.
What is the file size limit for OneDrive uploads via the API?
The simple upload endpoint (PUT to /me/drive/root:/{filename}:/content) handles files up to 4 MB. For larger files up to 250 GB, use the large file upload session API: create an upload session with POST /me/drive/root:/{filename}:/createUploadSession, then upload the file in chunks of 320 KB increments using PUT requests to the returned uploadUrl.
Can I access SharePoint document libraries through the same API?
Yes. Microsoft Graph provides access to both personal OneDrive (/me/drive) and SharePoint site document libraries (/sites/{siteId}/drive or /sites/{siteId}/drives/{driveId}). The file operation endpoints are identical — only the base path differs. You can list available sites with GET /sites and then navigate their drives using the same file operation patterns as personal OneDrive.
How do I get the correct Azure tenant ID for a Microsoft 365 organization?
For multi-tenant apps that support both personal Microsoft accounts and organizational accounts, use 'common' as the tenant in OAuth2 URLs. For single-tenant apps restricted to one organization, use the specific tenant ID — found in Azure Portal → Azure Active Directory → Overview → Tenant ID (a GUID). Using the specific tenant ID restricts authentication to only that organization's accounts.
Does V0 generate OneDrive integration code automatically?
V0 can generate Microsoft Graph API call patterns and React file browser components when you describe them specifically. Ask V0: 'Generate a file browser component that fetches from /api/onedrive/files and supports folder navigation' or 'Create a file upload zone component that POSTs FormData to /api/onedrive/upload with progress tracking'. V0 generates the component patterns; you handle the Azure registration and OAuth2 flow separately.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation