Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting the picker v8.0 to access with MS Graph Token #3

Open
gaetanDev7 opened this issue Jun 17, 2022 · 9 comments
Open

Getting the picker v8.0 to access with MS Graph Token #3

gaetanDev7 opened this issue Jun 17, 2022 · 9 comments

Comments

@gaetanDev7
Copy link

gaetanDev7 commented Jun 17, 2022

Hello,

I've implemented a picker to access OneDrive/ Sharepoint using V 8.0 SDK. Our platform is heavily dependent on Microsoft Graph to perform access for all our customers.

Here is the implementation example:

import { useEffect, useState } from 'react';
import { ODPickerCallBack, ODPickerConfiguration } from './Types';

let win: any = null;
let port: any = null;
const baseUrl = 'https://xxxxxxxxx.sharepoint.com';

export const useOneDrivePicker = (getToken: () => Promise<any>) => {
    const [selectedOneDriveFileCallBackInfo, setSelectedOneDriveFileCallBackInfo] = useState<ODPickerCallBack>();
    const [selectedCancelOneDriveCallBackInfo, setSelectedCancelOneDriveCallBackInfo] = useState<boolean>();
    const [errorOneDriveCallBackInfo, setErrorOneDriveCallBackInfo] = useState<unknown>();

    const OnSuccessCallback = (files: ODPickerCallBack) => {
        setSelectedOneDriveFileCallBackInfo(files);
    };

    const OnCancelCallback = () => {
        setSelectedCancelOneDriveCallBackInfo(true);
    };

    const OnErrorCallback = (error: unknown) => {
        setErrorOneDriveCallBackInfo(error);
    };

    /**
     * Combines an arbitrary set of paths ensuring and normalizes the slashes
     *
     * @param paths 0 to n path parts to combine
     */
    function combine(...paths: any[]) {
        return paths
            .map((path) => path.replace(/^[\\|/]/, '').replace(/[\\|/]$/, ''))
            .join('/')
            .replace(/\\/g, '/');
    }

    // the options we pass to the picker page through the querystring
    const params = {
        sdk: '8.0',
        entry: {
            oneDrive: {
                files: {},
            },
        },
        selection: {
            mode: 'single',
        },
        authentication: {},
        messaging: {
            origin: 'http://localhost:3000',
            channelId: '400',
        },
        typesAndSources: {
            mode: 'files',
            pivots: {
                oneDrive: true,
                recent: true,
                sharedLibraries: true,
            },
        },
    };

    async function messageListener(message: any) {
        // get a graph token
        const token = await getToken();
        console.log('token in message=', token);
        switch (message.data.type) {
            case 'notification':
                console.log(`notification: ${message.data}`);
                break;

            case 'command':
                port.postMessage({
                    type: 'acknowledge',
                    id: message.data.id,
                });

                const command = message.data.data;
                // eslint-disable-next-line no-case-declarations
                switch (command.command) {
                    case 'authenticate':
                        if (typeof token !== 'undefined' && token !== null) {
                            port.postMessage({
                                type: 'result',
                                id: message.data.id,
                                data: {
                                    result: 'token',
                                    token,
                                },
                            });
                        } else {
                            console.error(`Could not get auth token for command: ${JSON.stringify(command)}`);
                        }

                        break;

                    case 'close':
                        win.close();
                        break;

                    case 'pick':
                        console.log(`Picked: ${JSON.stringify(command)}`);

                        console.log(`<pre>${JSON.stringify(command, null, 2)}</pre>`);

                        port.postMessage({
                            type: 'result',
                            id: message.data.id,
                            data: {
                                result: 'success',
                            },
                        });

                        win.close();

                        break;

                    default:
                        console.warn(`Unsupported command: ${JSON.stringify(command)}`, 2);

                        port.postMessage({
                            result: 'error',
                            error: {
                                code: 'unsupportedCommand',
                                message: command.command,
                            },
                            isExpected: true,
                        });
                        break;
                }

                break;
            default:
                console.warn(`Unsupported message: ${JSON.stringify(message.data.type)}`, 2);
        }
    }

    const openOneDrivePicker = (odPickerConfig: ODPickerConfiguration) => {
        // Everything went fine, create the picker.
        if (odPickerConfig.accessToken) {
            console.log('token=', odPickerConfig.accessToken);

            win = window.open('', 'Picker', 'width=800,height=600');

            if (!win) return;

            const queryString = new URLSearchParams({
                filePicker: JSON.stringify(params),
            });

            const url = combine(baseUrl, `_layouts/15/FilePicker.aspx?${queryString}`);

            const form = win.document.createElement('form');
            form.setAttribute('action', url);
            form.setAttribute('method', 'POST');
            win.document.body.append(form);

            const input = win.document.createElement('input');
            input.setAttribute('type', 'hidden');
            input.setAttribute('name', 'access_token');
            input.setAttribute('value', odPickerConfig.accessToken);
            form.appendChild(input);

            form.submit();

            window.addEventListener('message', (event) => {
                if (event.source && event.source === win) {
                    const message = event.data;

                    if (message.type === 'initialize' && message.channelId === params.messaging.channelId) {
                        port = event.ports[0];

                        port.addEventListener('message', messageListener);

                        port.start();

                        port.postMessage({
                            type: 'activate',
                        });
                    }
                }
            });
        } else {
            console.error('oauth Token no found:');
        }

        // eslint-disable-next-line consistent-return
        return true;
    };

    return {
        openOneDrivePicker,
        selectedOneDriveFileCallBackInfo,
        selectedCancelOneDriveCallBackInfo,
        errorOneDriveCallBackInfo,
    };
};

You will find below a screenshot of the error message :

test drawio

So basically, in my MS Graph token my ‘audience’ is as follows:
"aud": "https://graph.microsoft.com/"

Is there currently a way to make the picker work with MS Graph ? Please advise.

Thanks in advance for your Help.

@patrick-rodgers
Copy link
Collaborator

No, the picker does not currently call the graph APIs. This is great feedback we will share with the feature team, but there is currently not a timeline to switch the backend to Microsoft Graph.

@patrick-rodgers patrick-rodgers added the enhancement New feature or request label Jun 22, 2022
@gaetanDev7
Copy link
Author

Thank you @patrick-rodgers for this information. Hope to be able to have the picker soon connected to Microsoft Graph.

@Tjerkdb
Copy link

Tjerkdb commented Aug 2, 2022

Very much hoping this can be implemented!

@tstrader
Copy link

Same here... really difficult to work with this picker when it effectively means we need 3 different auth tokens for the solution to work....

@scottsidwell
Copy link

I've recently been investigating integrating Microsoft file-picking into our app, and ultimately concluded that v7.2 is a clearer path forward for us.

As a consumer, I don't want to think or understand the nuances of Microsoft's multiple file-management services - the moment I have to start worrying about the differences between one-drive or sharepoint configurations, the surface area of what I need to test as an app developer has doubled and my confidence that future changes won't subtly break behaviour is halved.

Which is a bit of shame v8 doesn't do this for us, hopefully MS Graph support is roadmapped soon 🤞

@eclrbohnhoff
Copy link

I was forced to use v7.2 API since it is required that we must use MS Graph. Adding +1 here that it is added to the roadmap for v8.

@armasson
Copy link

armasson commented Jul 7, 2023

Hey team,

It looks like we have the same problem.

We were using the File picker SDK and we tried to use the access token we already have for the application.

Technically speaking the load is done as it:

// Init the library with an empty auth
new msal.PublicClientApplication({ auth: { clientId: '...'}})
...
// Then generate the form with the local token
 win = window.open('', 'Picker', 'width=800,height=600') as WindowProxy;
const authToken = await getToken();
const queryString = new URLSearchParams({
    filePicker: JSON.stringify({...}),
});

const url = combine(baseUrl, `_layouts/15/FilePicker.aspx?${queryString}`);

const form = win.document.createElement('form');
form.setAttribute('action', url);
form.setAttribute('method', 'POST');
win.document.body.append(form);

const input = win.document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', 'access_token');
input.setAttribute('value', authToken);
form.appendChild(input);

form.submit();

using it, we have this error:

     "status": 401,
     "authenticate":  "Bearer realm=\"d49cbc78-6d92-451d-84a3-b04cd87d201f\",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\"",
     "code": "3000003,invalid_client",
                "message": "Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown.",
                "codes": {
                    "order": [
                        "x-ms-diagnostics"
                    ],
                    "map": {
                        "x-ms-diagnostics": {
                            "source": "x-ms-diagnostics",
                            "code": "3000003,invalid_client",
                            "message": "Invalid audience Uri 'https://graph.microsoft.com/'.",
                            "isDisplayable": true
                        }
                    }
                },
                ...

Here is the token information used:

{
  "aud": "https://graph.microsoft.com",
  "iss": "https://sts.windows.net/d49cbc78-6d92-451d-84a3-b04cd87d201f/"
  ...
  "appid": "4af4d3f2-******",
   "scp": "... Files.ReadWrite.All ... Sites.Read.All ...",
}

At first sight it seems the audience https://graph.microsoft.com/, can not be used. If so, how can we use the librairie to access the graph API ?
Has there been any progress on this ? 🙏

@Tjerkdb
Copy link

Tjerkdb commented Nov 29, 2023

Is there any update on this? Is this being considered at all?

@JCrew0
Copy link
Collaborator

JCrew0 commented Mar 20, 2024

@Tjerkdb @armasson We have it logged on our end that we'd like to support Graph. However, now that we no longer have a javascript wrapper like v7.2 did, there's new security concerns for switching to Graph. We hope to work with the auth team to get this supported, but in the meantime, v7.2 might be best for users reliant on Graph.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants