带有MSAL令牌的Sharepoint API中的未授权请求

时间:2019-10-16 14:11:25

标签: azure sharepoint active-directory azure-active-directory msal

背景 我正在开发一个移动应用程序,该应用程序可以通过Azure Active Directory(Microsoft身份验证)进行身份验证,并可以访问Sharepoint中的某些信息。我的首选是使用Ionic Cordova,但似乎不支持Cordova-MSAL集成。因此,我们选择Xamarin是为了开发跨平台应用程序,但与Microsoft身份验证兼容。

情况 我正在尝试连接Sharepoint API。

1-我已经在Azure(Active Directory)中注册了一个新应用,并且         给定的权限。

2-我通过MSAL获得了承载令牌         登录到自己的库(在Web中以及在Xamarin中)之后(例如,         下面的链接):         https://docs.microsoft.com/es-es/azure/active-directory/develop/quickstart-v2-javascript

3-现在,我向Sharepoint API发出以下请求。

url: http://site url/_api/web/lists(guid'list GUID'),
method: GET
Headers:
    Authorization: "Bearer " + accessToken
    accept: "application/json;odata=verbose" 

但是我总是遇到以下错误:

{"error_description":"Invalid JWT token. No certificate thumbprint specified in token header."}

我读了很多人在谈论MSAL的错误,但这是正式的方式(ADAL似乎已被弃用)。

非常感谢任何想法。

2 个答案:

答案 0 :(得分:2)

在利用MSAL.js进行身份验证和获取访问令牌以成功调用SharePoint Online API时,我也面临着这个问题。

Microsoft的以下文档提供了一个很棒的示例和通过MSAL进行身份验证的说明:https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-spa

但是,我需要继续前进,利用获取的令牌直接调用SharePoint API,而不是通过Graph API来使用服务。

要解决此问题,我必须按如下方式定义作用域:

scopes: [https://{tenantName}.sharepoint.com/.default]

使用Microsoft文章中的示例,我进行了必要的更改。我能够利用获取的访问令牌成功调用SharePoint API:

<!DOCTYPE html>
<html>
<head>
    <title>Quickstart for MSAL JS</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/msal.js"> 
</script>
</head>
<body>
    <h2>Welcome to MSAL.js Quickstart</h2><br />
    <h4 id="WelcomeMessage"></h4>
    <button id="SignIn" onclick="signIn()">Sign In</button><br /><br />
    <pre id="json"></pre>
    <script>

    var msalConfig = {
        auth: {
            clientId: "{clientID}",
            authority: "https://login.microsoftonline.com/organizations"
        },
        cache: {
            cacheLocation: "localStorage",
            storeAuthStateInCookie: true
        }
    };

    var sharePointConfig = {
        sharePointFileEndpoint: "https://{tenantName}.sharepoint.com/sites/{siteName}/_api/web/GetFolderByServerRelativeUrl('Shared Documents/{folderName}')/Files('testFile.txt')/$value"
    }

    //the defined scopes were updated for making calls to the SharePoint API.

    var requestObj = {
        scopes: ["https://{tenantName}.sharepoint.com/.default"]
    };

    var myMSALObj = new Msal.UserAgentApplication(msalConfig);
    // Register Callbacks for redirect flow
    myMSALObj.handleRedirectCallback(authRedirectCallBack);


    function signIn() {

        myMSALObj.loginPopup(requestObj).then(function (loginResponse) {
            //Login Success
            showWelcomeMessage();
            acquireTokenPopupAndCallAPI();

        }).catch(function (error) {
            console.log(error);
        });
    }


    function acquireTokenPopupAndCallAPI() {
        //Always start with acquireTokenSilent to obtain a token in the signed in user from cache
        myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {

            //Http Request

            callAPI(sharePointConfig.sharePointFileEndpoint, tokenResponse.accessToken, graphAPICallback);

        }).catch(function (error) {
            console.log(error);
            // Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
            // Call acquireTokenPopup(popup window)
            if (requiresInteraction(error.errorCode)) {
                myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {

                    //Http Request

                    callAPI(sharePointConfig.sharePointFileEndpoint, tokenResponse.accessToken, graphAPICallback);

                }).catch(function (error) {
                    console.log(error);
                });
            }
        });
    }


    function graphAPICallback(data) {
        document.getElementById("json").innerHTML = JSON.stringify(data, null, 2);
    }


    function showWelcomeMessage() {
        var divWelcome = document.getElementById('WelcomeMessage');
        divWelcome.innerHTML = 'Welcome ' + myMSALObj.getAccount().userName + "to Microsoft Graph API";
        var loginbutton = document.getElementById('SignIn');
        loginbutton.innerHTML = 'Sign Out';
        loginbutton.setAttribute('onclick', 'signOut();');
    }

    function authRedirectCallBack(error, response) {
        if (error) {
            console.log(error);
        }
        else {
            if (response.tokenType === "access_token") {

                callAPI(sharePointConfig.sharePointFileEndpoint, tokenResponse.accessToken, graphAPICallback);
            } else {
                console.log("token type is:" + response.tokenType);
            }
        }
    }

    function requiresInteraction(errorCode) {
        if (!errorCode || !errorCode.length) {
            return false;
        }
        return errorCode === "consent_required" ||
            errorCode === "interaction_required" ||
            errorCode === "login_required";
    }

    // Browser check variables
    var ua = window.navigator.userAgent;
    var msie = ua.indexOf('MSIE ');
    var msie11 = ua.indexOf('Trident/');
    var msedge = ua.indexOf('Edge/');
    var isIE = msie > 0 || msie11 > 0;
    var isEdge = msedge > 0;
    //If you support IE, our recommendation is that you sign-in using Redirect APIs
    //If you as a developer are testing using Edge InPrivate mode, please add "isEdge" to the if check
    // can change this to default an experience outside browser use
    var loginType = isIE ? "REDIRECT" : "POPUP";

    if (loginType === 'POPUP') {
        if (myMSALObj.getAccount()) {// avoid duplicate code execution on page load in case of iframe and popup window.
            showWelcomeMessage();
            acquireTokenPopupAndCallAPI();
        }
    }
    else if (loginType === 'REDIRECT') {
        document.getElementById("SignIn").onclick = function () {
            myMSALObj.loginRedirect(requestObj);
        };
        if (myMSALObj.getAccount() && !myMSALObj.isCallback(window.location.hash)) {// avoid duplicate code execution on page load in case of iframe and popup window.
            showWelcomeMessage();
            acquireTokenPopupAndCallAPI();
        }
    } else {
        console.error('Please set a valid login type');
    }


    function callAPI(theUrl, accessToken, callback) {
        var xmlHttp = new XMLHttpRequest();
        /*
        xmlHttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200)
                callback(JSON.parse(this.responseText));
        }
        */
        xmlHttp.open("GET", theUrl, true); // true for asynchronous
        xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
        xmlHttp.send();         
    }


    function signOut() {
        myMSALObj.logout();
    }

</script>
</body>
</html>

上面的示例是Microsoft的示例的修改版本。我能够利用它进行成功的测试。

答案 1 :(得分:0)

令牌无效,请检查获取访问令牌的方式。我授予了该应用AllSites.FullControl的权限。所以范围应该是

https://{xxx}.sharepoint.com/AllSites.FullControl

响应:

enter image description here