Browse Source

Merge pull request #12904 from hurxxxx/feat/onedrive-orgs

feat: support Organization and School accounts in OneDrive
Tim Jaeryang Baek 3 months ago
parent
commit
fa8132903e

+ 7 - 0
backend/open_webui/config.py

@@ -1763,6 +1763,13 @@ ONEDRIVE_CLIENT_ID = PersistentConfig(
     os.environ.get("ONEDRIVE_CLIENT_ID", ""),
 )
 
+ONEDRIVE_SHAREPOINT_URL = PersistentConfig(
+    "ONEDRIVE_SHAREPOINT_URL",
+    "onedrive.sharepoint_url",
+    os.environ.get("ONEDRIVE_SHAREPOINT_URL", ""),
+)
+
+
 # RAG Content Extraction
 CONTENT_EXTRACTION_ENGINE = PersistentConfig(
     "CONTENT_EXTRACTION_ENGINE",

+ 6 - 1
backend/open_webui/main.py

@@ -100,6 +100,7 @@ from open_webui.config import (
     # OpenAI
     ENABLE_OPENAI_API,
     ONEDRIVE_CLIENT_ID,
+    ONEDRIVE_SHAREPOINT_URL,
     OPENAI_API_BASE_URLS,
     OPENAI_API_KEYS,
     OPENAI_API_CONFIGS,
@@ -240,6 +241,7 @@ from open_webui.config import (
     GOOGLE_DRIVE_CLIENT_ID,
     GOOGLE_DRIVE_API_KEY,
     ONEDRIVE_CLIENT_ID,
+    ONEDRIVE_SHAREPOINT_URL,
     ENABLE_RAG_HYBRID_SEARCH,
     ENABLE_RAG_LOCAL_WEB_FETCH,
     ENABLE_WEB_LOADER_SSL_VERIFICATION,
@@ -1327,7 +1329,10 @@ async def get_app_config(request: Request):
                     "client_id": GOOGLE_DRIVE_CLIENT_ID.value,
                     "api_key": GOOGLE_DRIVE_API_KEY.value,
                 },
-                "onedrive": {"client_id": ONEDRIVE_CLIENT_ID.value},
+                "onedrive": {
+                    "client_id": ONEDRIVE_CLIENT_ID.value,
+                    "sharepoint_url": ONEDRIVE_SHAREPOINT_URL.value,
+                },
                 "license_metadata": app.state.LICENSE_METADATA,
                 **(
                     {

+ 2 - 2
src/lib/components/chat/MessageInput.svelte

@@ -1063,9 +1063,9 @@
 													);
 												}
 											}}
-											uploadOneDriveHandler={async () => {
+											uploadOneDriveHandler={async (authorityType) => {
 												try {
-													const fileData = await pickAndDownloadFile();
+													const fileData = await pickAndDownloadFile(authorityType);
 													if (fileData) {
 														const file = new File([fileData.blob], fileData.name, {
 															type: fileData.blob.type || 'application/octet-stream'

+ 59 - 87
src/lib/components/chat/MessageInput/InputMenu.svelte

@@ -229,94 +229,66 @@
 			{/if}
 
 			{#if $config?.features?.enable_onedrive_integration}
-				<DropdownMenu.Item
-					class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
-					on:click={() => {
-						uploadOneDriveHandler();
-					}}
-				>
-					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="w-5 h-5" fill="none">
-						<mask
-							id="mask0_87_7796"
-							style="mask-type:alpha"
-							maskUnits="userSpaceOnUse"
-							x="0"
-							y="6"
-							width="32"
-							height="20"
+				<DropdownMenu.Sub>
+					<DropdownMenu.SubTrigger class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full">
+						<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="w-5 h-5" fill="none">
+							<mask id="mask0_87_7796" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="6" width="32" height="20">
+								<path d="M7.82979 26C3.50549 26 0 22.5675 0 18.3333C0 14.1921 3.35322 10.8179 7.54613 10.6716C9.27535 7.87166 12.4144 6 16 6C20.6308 6 24.5169 9.12183 25.5829 13.3335C29.1316 13.3603 32 16.1855 32 19.6667C32 23.0527 29 26 25.8723 25.9914L7.82979 26Z" fill="#C4C4C4"/>
+							</mask>
+							<g mask="url(#mask0_87_7796)">
+								<path d="M7.83017 26.0001C5.37824 26.0001 3.18957 24.8966 1.75391 23.1691L18.0429 16.3335L30.7089 23.4647C29.5926 24.9211 27.9066 26.0001 26.0004 25.9915C23.1254 26.0001 12.0629 26.0001 7.83017 26.0001Z" fill="url(#paint0_linear_87_7796)"/>
+								<path d="M25.5785 13.3149L18.043 16.3334L30.709 23.4647C31.5199 22.4065 32.0004 21.0916 32.0004 19.6669C32.0004 16.1857 29.1321 13.3605 25.5833 13.3337C25.5817 13.3274 25.5801 13.3212 25.5785 13.3149Z" fill="url(#paint1_linear_87_7796)"/>
+								<path d="M7.06445 10.7028L18.0423 16.3333L25.5779 13.3148C24.5051 9.11261 20.6237 6 15.9997 6C12.4141 6 9.27508 7.87166 7.54586 10.6716C7.3841 10.6773 7.22358 10.6877 7.06445 10.7028Z" fill="url(#paint2_linear_87_7796)"/>
+								<path d="M1.7535 23.1687L18.0425 16.3331L7.06471 10.7026C3.09947 11.0792 0 14.3517 0 18.3331C0 20.1665 0.657197 21.8495 1.7535 23.1687Z" fill="url(#paint3_linear_87_7796)"/>
+							</g>
+							<defs>
+								<linearGradient id="paint0_linear_87_7796" x1="4.42591" y1="24.6668" x2="27.2309" y2="23.2764" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#2086B8"/>
+									<stop offset="1" stop-color="#46D3F6"/>
+								</linearGradient>
+								<linearGradient id="paint1_linear_87_7796" x1="23.8302" y1="19.6668" x2="30.2108" y2="15.2082" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#1694DB"/>
+									<stop offset="1" stop-color="#62C3FE"/>
+								</linearGradient>
+								<linearGradient id="paint2_linear_87_7796" x1="8.51037" y1="7.33333" x2="23.3335" y2="15.9348" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#0D3D78"/>
+									<stop offset="1" stop-color="#063B83"/>
+								</linearGradient>
+								<linearGradient id="paint3_linear_87_7796" x1="-0.340429" y1="19.9998" x2="14.5634" y2="14.4649" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#16589B"/>
+									<stop offset="1" stop-color="#1464B7"/>
+								</linearGradient>
+							</defs>
+						</svg>
+						<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive')}</div>
+					</DropdownMenu.SubTrigger>
+					<DropdownMenu.SubContent 
+						class="w-[calc(100vw-2rem)] max-w-[280px] rounded-xl px-1 py-1 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-sm"
+						side={$mobile ? "bottom" : "right"}
+						sideOffset={$mobile ? 5 : 0}
+						alignOffset={$mobile ? 0 : -8}
+					>
+						<DropdownMenu.Item
+							class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
+							on:click={() => {
+								uploadOneDriveHandler('personal');
+							}}
 						>
-							<path
-								d="M7.82979 26C3.50549 26 0 22.5675 0 18.3333C0 14.1921 3.35322 10.8179 7.54613 10.6716C9.27535 7.87166 12.4144 6 16 6C20.6308 6 24.5169 9.12183 25.5829 13.3335C29.1316 13.3603 32 16.1855 32 19.6667C32 23.0527 29 26 25.8723 25.9914L7.82979 26Z"
-								fill="#C4C4C4"
-							/>
-						</mask>
-						<g mask="url(#mask0_87_7796)">
-							<path
-								d="M7.83017 26.0001C5.37824 26.0001 3.18957 24.8966 1.75391 23.1691L18.0429 16.3335L30.7089 23.4647C29.5926 24.9211 27.9066 26.0001 26.0004 25.9915C23.1254 26.0001 12.0629 26.0001 7.83017 26.0001Z"
-								fill="url(#paint0_linear_87_7796)"
-							/>
-							<path
-								d="M25.5785 13.3149L18.043 16.3334L30.709 23.4647C31.5199 22.4065 32.0004 21.0916 32.0004 19.6669C32.0004 16.1857 29.1321 13.3605 25.5833 13.3337C25.5817 13.3274 25.5801 13.3212 25.5785 13.3149Z"
-								fill="url(#paint1_linear_87_7796)"
-							/>
-							<path
-								d="M7.06445 10.7028L18.0423 16.3333L25.5779 13.3148C24.5051 9.11261 20.6237 6 15.9997 6C12.4141 6 9.27508 7.87166 7.54586 10.6716C7.3841 10.6773 7.22358 10.6877 7.06445 10.7028Z"
-								fill="url(#paint2_linear_87_7796)"
-							/>
-							<path
-								d="M1.7535 23.1687L18.0425 16.3331L7.06471 10.7026C3.09947 11.0792 0 14.3517 0 18.3331C0 20.1665 0.657197 21.8495 1.7535 23.1687Z"
-								fill="url(#paint3_linear_87_7796)"
-							/>
-						</g>
-						<defs>
-							<linearGradient
-								id="paint0_linear_87_7796"
-								x1="4.42591"
-								y1="24.6668"
-								x2="27.2309"
-								y2="23.2764"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#2086B8" />
-								<stop offset="1" stop-color="#46D3F6" />
-							</linearGradient>
-							<linearGradient
-								id="paint1_linear_87_7796"
-								x1="23.8302"
-								y1="19.6668"
-								x2="30.2108"
-								y2="15.2082"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#1694DB" />
-								<stop offset="1" stop-color="#62C3FE" />
-							</linearGradient>
-							<linearGradient
-								id="paint2_linear_87_7796"
-								x1="8.51037"
-								y1="7.33333"
-								x2="23.3335"
-								y2="15.9348"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#0D3D78" />
-								<stop offset="1" stop-color="#063B83" />
-							</linearGradient>
-							<linearGradient
-								id="paint3_linear_87_7796"
-								x1="-0.340429"
-								y1="19.9998"
-								x2="14.5634"
-								y2="14.4649"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#16589B" />
-								<stop offset="1" stop-color="#1464B7" />
-							</linearGradient>
-						</defs>
-					</svg>
-					<div class="line-clamp-1">{$i18n.t('OneDrive')}</div>
-				</DropdownMenu.Item>
+							<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive (personal)')}</div>
+						</DropdownMenu.Item>
+						<DropdownMenu.Item
+							class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
+							on:click={() => {
+								uploadOneDriveHandler('organizations');
+							}}
+						>
+							<div class="flex flex-col">
+								<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive (work/school)')}</div>
+								<div class="text-xs text-gray-500">Includes SharePoint</div>
+							</div>
+						</DropdownMenu.Item>
+					</DropdownMenu.SubContent>
+				</DropdownMenu.Sub>
 			{/if}
 		</DropdownMenu.Content>
 	</div>

+ 204 - 74
src/lib/utils/onedrive-file-picker.ts

@@ -2,70 +2,128 @@ import { PublicClientApplication } from '@azure/msal-browser';
 import type { PopupRequest } from '@azure/msal-browser';
 import { v4 as uuidv4 } from 'uuid';
 
-let CLIENT_ID = '';
+class OneDriveConfig {
+	private static instance: OneDriveConfig;
+	private clientId: string = '';
+	private sharepointUrl: string = '';
+	private msalInstance: PublicClientApplication | null = null;
+	private currentAuthorityType: 'personal' | 'organizations' = 'personal';
 
-async function getCredentials() {
-	if (CLIENT_ID) return;
+	private constructor() {}
 
-	const response = await fetch('/api/config');
-	if (!response.ok) {
-		throw new Error('Failed to fetch OneDrive credentials');
+	public static getInstance(): OneDriveConfig {
+		if (!OneDriveConfig.instance) {
+			OneDriveConfig.instance = new OneDriveConfig();
+		}
+		return OneDriveConfig.instance;
 	}
-	const config = await response.json();
-	CLIENT_ID = config.onedrive?.client_id;
-	if (!CLIENT_ID) {
-		throw new Error('OneDrive client ID not configured');
+
+	public async initialize(authorityType?: 'personal' | 'organizations'): Promise<void> {
+		if (authorityType && this.currentAuthorityType !== authorityType) {
+			this.currentAuthorityType = authorityType;
+			this.msalInstance = null;
+		}
+		await this.getCredentials();
 	}
-}
 
-let msalInstance: PublicClientApplication | null = null;
+	public async ensureInitialized(authorityType?: 'personal' | 'organizations'): Promise<void> {
+		await this.initialize(authorityType);
+	}
 
-// Initialize MSAL authentication
-async function initializeMsal() {
-	try {
-		if (!CLIENT_ID) {
-			await getCredentials();
+	private async getCredentials(): Promise<void> {
+		
+		const headers: HeadersInit = {
+			'Content-Type': 'application/json'
+		};
+
+		const response = await fetch('/api/config', { 
+			headers,
+			credentials: 'include'
+		});
+		
+		if (!response.ok) {
+			throw new Error('Failed to fetch OneDrive credentials');
+		}
+		
+		const config = await response.json();
+		
+		const newClientId = config.onedrive?.client_id;
+		const newSharepointUrl = config.onedrive?.sharepoint_url;
+		
+		if (!newClientId) {
+			throw new Error('OneDrive configuration is incomplete');
 		}
 
-		const msalParams = {
-			auth: {
-				authority: 'https://login.microsoftonline.com/consumers',
-				clientId: CLIENT_ID
-			}
-		};
+		this.clientId = newClientId;
+		this.sharepointUrl = newSharepointUrl;
+	}
+
+	public async getMsalInstance(authorityType?: 'personal' | 'organizations'): Promise<PublicClientApplication> {
+		await this.ensureInitialized(authorityType);
+		
+		if (!this.msalInstance) {
+			const authorityEndpoint = this.currentAuthorityType === 'organizations' ? 'common' : 'consumers';
+			const msalParams = {
+				auth: {
+					authority: `https://login.microsoftonline.com/${authorityEndpoint}`,
+					clientId: this.clientId
+				}
+			};
 
-		if (!msalInstance) {
-			msalInstance = new PublicClientApplication(msalParams);
-			if (msalInstance.initialize) {
-				await msalInstance.initialize();
+			this.msalInstance = new PublicClientApplication(msalParams);
+			if (this.msalInstance.initialize) {
+				await this.msalInstance.initialize();
 			}
 		}
 
-		return msalInstance;
-	} catch (error) {
-		throw new Error(
-			'MSAL initialization failed: ' + (error instanceof Error ? error.message : String(error))
-		);
+		return this.msalInstance;
+	}
+
+	public getAuthorityType(): 'personal' | 'organizations' {
+		return this.currentAuthorityType;
+	}
+
+	public getSharepointUrl(): string {
+		return this.sharepointUrl;
+	}
+
+	public getBaseUrl(): string {
+		if (this.currentAuthorityType === 'organizations') {
+			if (!this.sharepointUrl || this.sharepointUrl === '') {
+				throw new Error('Sharepoint URL not configured');
+			}
+
+			let sharePointBaseUrl = this.sharepointUrl.replace(/^https?:\/\//, '');
+			sharePointBaseUrl = sharePointBaseUrl.replace(/\/$/, '');
+
+			return `https://${sharePointBaseUrl}`;
+		} else {
+			return 'https://onedrive.live.com/picker';
+		}
 	}
 }
 
+
 // Retrieve OneDrive access token
-async function getToken(): Promise<string> {
-	const authParams: PopupRequest = { scopes: ['OneDrive.ReadWrite'] };
+async function getToken(resource?: string, authorityType?: 'personal' | 'organizations'): Promise<string> {
+	const config = OneDriveConfig.getInstance();
+	await config.ensureInitialized(authorityType);
+	
+	const currentAuthorityType = config.getAuthorityType();
+
+	const scopes = currentAuthorityType === 'organizations'
+		? [`${resource || config.getBaseUrl()}/.default`]
+		: ['OneDrive.ReadWrite'];
+	
+	const authParams: PopupRequest = { scopes };
 	let accessToken = '';
-	try {
-		msalInstance = await initializeMsal();
-		if (!msalInstance) {
-			throw new Error('MSAL not initialized');
-		}
 
+	try {
+		const msalInstance = await config.getMsalInstance(authorityType);
 		const resp = await msalInstance.acquireTokenSilent(authParams);
 		accessToken = resp.accessToken;
 	} catch (err) {
-		if (!msalInstance) {
-			throw new Error('MSAL not initialized');
-		}
-
+		const msalInstance = await config.getMsalInstance(authorityType);
 		try {
 			const resp = await msalInstance.loginPopup(authParams);
 			msalInstance.setActiveAccount(resp.account);
@@ -88,60 +146,121 @@ async function getToken(): Promise<string> {
 	return accessToken;
 }
 
-const baseUrl = 'https://onedrive.live.com/picker';
-const params = {
-	sdk: '8.0',
+interface PickerParams {
+	sdk: string;
 	entry: {
-		oneDrive: {
-			files: {}
-		}
-	},
-	authentication: {},
+		oneDrive: Record<string, unknown>;
+	};
+	authentication: Record<string, unknown>;
 	messaging: {
-		origin: window?.location?.origin,
-		channelId: uuidv4()
-	},
+		origin: string;
+		channelId: string;
+	};
 	typesAndSources: {
-		mode: 'files',
-		pivots: {
-			oneDrive: true,
-			recent: true
+		mode: string;
+		pivots: Record<string, boolean>;
+	};
+}
+
+interface PickerResult {
+	command?: string;
+	items?: OneDriveFileInfo[];
+	[key: string]: any;
+}
+
+// Get picker parameters based on account type
+function getPickerParams(): PickerParams {
+	const channelId = uuidv4();
+	const config = OneDriveConfig.getInstance();
+	
+	const params: PickerParams = {
+		sdk: '8.0',
+		entry: {
+			oneDrive: {}
+		},
+		authentication: {},
+		messaging: {
+			origin: window?.location?.origin || '',
+			channelId
+		},
+		typesAndSources: {
+			mode: 'files',
+			pivots: {
+				oneDrive: true,
+				recent: true
+			}
 		}
+	};
+	
+	// For personal accounts, set files object in oneDrive
+	if (config.getAuthorityType() !== 'organizations') {
+		params.entry.oneDrive = { files: {} };
 	}
-};
+	
+	return params;
+}
+
+interface OneDriveFileInfo {
+	id: string;
+	name: string;
+	parentReference: {
+		driveId: string;
+	};
+	'@sharePoint.endpoint': string;
+	[key: string]: any;
+}
 
 // Download file from OneDrive
-async function downloadOneDriveFile(fileInfo: any): Promise<Blob> {
-	const accessToken = await getToken();
+async function downloadOneDriveFile(fileInfo: OneDriveFileInfo, authorityType?: 'personal' | 'organizations'): Promise<Blob> {
+	const accessToken = await getToken(undefined, authorityType);
 	if (!accessToken) {
 		throw new Error('Unable to retrieve OneDrive access token.');
 	}
+	
+	// The endpoint URL is provided in the file info
 	const fileInfoUrl = `${fileInfo['@sharePoint.endpoint']}/drives/${fileInfo.parentReference.driveId}/items/${fileInfo.id}`;
+	
 	const response = await fetch(fileInfoUrl, {
 		headers: {
 			Authorization: `Bearer ${accessToken}`
 		}
 	});
+	
 	if (!response.ok) {
-		throw new Error('Failed to fetch file information.');
+		throw new Error(`Failed to fetch file information: ${response.status} ${response.statusText}`);
 	}
+	
 	const fileData = await response.json();
 	const downloadUrl = fileData['@content.downloadUrl'];
+	
+	if (!downloadUrl) {
+		throw new Error('Download URL not found in file data');
+	}
+	
 	const downloadResponse = await fetch(downloadUrl);
+	
 	if (!downloadResponse.ok) {
-		throw new Error('Failed to download file.');
+		throw new Error(`Failed to download file: ${downloadResponse.status} ${downloadResponse.statusText}`);
 	}
+	
 	return await downloadResponse.blob();
 }
 
 // Open OneDrive file picker and return selected file metadata
-export async function openOneDrivePicker(): Promise<any | null> {
+export async function openOneDrivePicker(authorityType?: 'personal' | 'organizations'): Promise<PickerResult | null> {
 	if (typeof window === 'undefined') {
 		throw new Error('Not in browser environment');
 	}
+
+	// Initialize OneDrive config with the specified authority type
+	const config = OneDriveConfig.getInstance();
+	await config.initialize(authorityType);
+	
 	return new Promise((resolve, reject) => {
 		let pickerWindow: Window | null = null;
 		let channelPort: MessagePort | null = null;
+		const params = getPickerParams();
+		const baseUrl = config.getBaseUrl();
 
 		const handleWindowMessage = (event: MessageEvent) => {
 			if (event.source !== pickerWindow) return;
@@ -166,7 +285,9 @@ export async function openOneDrivePicker(): Promise<any | null> {
 					switch (command.command) {
 						case 'authenticate': {
 							try {
-								const newToken = await getToken();
+								// Pass the resource from the command for org accounts
+								const resource = config.getAuthorityType() === 'organizations' ? command.resource : undefined;
+								const newToken = await getToken(resource, authorityType);
 								if (newToken) {
 									channelPort?.postMessage({
 										type: 'result',
@@ -178,9 +299,12 @@ export async function openOneDrivePicker(): Promise<any | null> {
 								}
 							} catch (err) {
 								channelPort?.postMessage({
-									result: 'error',
-									error: { code: 'tokenError', message: 'Failed to get token' },
-									isExpected: true
+									type: 'result',
+									id: portData.id,
+									data: {
+										result: 'error',
+										error: { code: 'tokenError', message: 'Failed to get token' }
+									}
 								});
 							}
 							break;
@@ -227,7 +351,7 @@ export async function openOneDrivePicker(): Promise<any | null> {
 
 		const initializePicker = async () => {
 			try {
-				const authToken = await getToken();
+				const authToken = await getToken(undefined, authorityType);
 				if (!authToken) {
 					return reject(new Error('Failed to acquire access token'));
 				}
@@ -240,8 +364,14 @@ export async function openOneDrivePicker(): Promise<any | null> {
 				const queryString = new URLSearchParams({
 					filePicker: JSON.stringify(params)
 				});
-				const url = `${baseUrl}?${queryString.toString()}`;
 
+				let url = '';
+				if(config.getAuthorityType() === 'organizations') {
+					url = baseUrl + `/_layouts/15/FilePicker.aspx?${queryString}`;
+				} else {
+					url = baseUrl + `?${queryString}`;
+				}
+			
 				const form = pickerWindow.document.createElement('form');
 				form.setAttribute('action', url);
 				form.setAttribute('method', 'POST');
@@ -268,17 +398,17 @@ export async function openOneDrivePicker(): Promise<any | null> {
 }
 
 // Pick and download file from OneDrive
-export async function pickAndDownloadFile(): Promise<{ blob: Blob; name: string } | null> {
-	const pickerResult = await openOneDrivePicker();
+export async function pickAndDownloadFile(authorityType?: 'personal' | 'organizations'): Promise<{ blob: Blob; name: string } | null> {
+	const pickerResult = await openOneDrivePicker(authorityType);
 
 	if (!pickerResult || !pickerResult.items || pickerResult.items.length === 0) {
 		return null;
 	}
 
 	const selectedFile = pickerResult.items[0];
-	const blob = await downloadOneDriveFile(selectedFile);
+	const blob = await downloadOneDriveFile(selectedFile, authorityType);
 
 	return { blob, name: selectedFile.name };
 }
 
-export { downloadOneDriveFile };
+export { downloadOneDriveFile };