【问题标题】:Authenticate azure python function to call it from AJAX request without exposing a token or key验证 azure python 函数以从 AJAX 请求调用它而不暴露令牌或密钥
【发布时间】:2020-11-11 05:29:57
【问题描述】:

我创建了一个 Python Azure Function 并通过在 Azure 上托管为 App ServiceJS 代码调用它。

我需要在这个函数上设置azure active directory authentication。 我已经在 azure function app 和 azure app service 中配置了 azure Active Directory 身份验证,并在两者上都启用了 CORS,但仍然面临 CORS 问题

从源“app-service-url”的“azure-function-url”重定向到“https://login.windows.net”对 XMLHttpRequest 的访问已被 CORS 策略阻止:否“Access-Control-Allow” -Origin' 标头存在于请求的资源上。

基本上我想要对 azure python 函数进行身份验证,以便我可以从 AJAX 请求调用它而不在应用服务中公开令牌? 我做错什么了吗?

还有一种方法可以在使用 azure 活动目录身份验证的同时使用 azure 函数返回登录用户的电子邮件 ID 吗?我可以在 c# 中找到一个代码示例,如下所示。

laimsPrincipal cp = ClaimsPrincipal.Current;
string welcome = string.Format("Welcome, {0} {1}!", 
cp.FindFirst(ClaimTypes.GivenName).Value, `cp.FindFirst(ClaimTypes.Surname).Value);`

现在的问题是,我需要使用 Python 来执行此操作,但我在网上找不到示例。谁能指出我正确的方向?或者帮助翻译这段代码。

【问题讨论】:

  • 不支持直接从 SPA 调用 Azure AD 端点,请使用 msal.js 来执行此操作。详情见此示例:github.com/AzureAD/microsoft-authentication-library-for-js/tree/…
  • 好的,试试这个。
  • @StanleyGong 还有一种方法可以在使用 azure 活动目录身份验证的同时使用 azure 函数返回登录用户的电子邮件 ID 吗?
  • 当然,一般情况下,你从Azure AD获取的token中都会涉及到用户的email-id,获取方法可以参考我之前的帖子:stackoverflow.com/questions/64658682/…
  • @StanleyGong 感谢您的回答!你能告诉我在我的 python httptrigger azure 函数中登录后如何获取从 Azure AD 获得的访问令牌吗?

标签: javascript python azure azure-active-directory


【解决方案1】:

这是一个从 html/JS 代码调用 Azure 函数的简单演示。

第一步:您应该注册一个 Azure AD 应用程序作为您的客户端,以便您可以使用此应用程序登录用户并获取令牌:

在这种情况下,它需要 Microsoft Graph API 读取用户权限:

**Step2:**用下面的代码创建一个python函数来测试:

import logging
import base64
import azure.functions as func
import json


def main(req: func.HttpRequest) -> func.HttpResponse:

    accessTokenPayLoad = req.headers.get("Authorization").replace("Bearer ","").split(".")[1]
    data = base64.b64decode(accessTokenPayLoad + '==')
    jsonObj = json.loads(data)
    upn = jsonObj['upn']
    return func.HttpResponse("hello, " + upn)
    
    

基本上,这个函数只是从访问令牌中读取用户的upn来读取用户的email-id。

Step3 创建函数应用后,请启用 CORS 以便它可以接受来自静态 HTML 的请求:

Step4下面的代码是HTML/JS代码示例,登录用户并获取token调用函数:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    
    <title>Azure Function test</title>
    <script type="text/javascript" src="https://alcdn.msauth.net/lib/1.4.4/js/msal.min.js"></script>

</head>
<body>
    <div >
                    
        <button id="SignIn" onclick="signIn()">Sign in</button><br/>
        <div id="WelcomeMessage"></div><br/>
        <div id="functionResult"></div>
    </div>
</body>

<script>
    
    var clientAppID = "<your client app id>"
    var tenantID = "<your tenant name/id>"
    var functionURL = "<your function url>";
    
    var demoScops = {
         scopes:["user.read"]
    }

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

    var myMSALObj = new Msal.UserAgentApplication(msalConfig);
    myMSALObj.handleRedirectCallback(authRedirectCallBack);
             
    
    function signIn() {
     
         myMSALObj.loginPopup(demoScops).then(function (loginResponse) {
            console.log(loginResponse);
            initPage();
             
         }).catch(function (error) {
             console.log(error);
         });
     }
     
    function initPage(){
        showWelcomeMessage();
        getGraphAccessTokenToCallFunction()
     }
     
     
     function callFunction(accessToken){

        var xmlHttp = new XMLHttpRequest();
        xmlHttp.onreadystatechange = function() { 
            if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
                document.getElementById('functionResult').innerHTML = xmlHttp.responseText;
            }
        }
        xmlHttp.open("GET", functionURL, true);
        xmlHttp.setRequestHeader("Authorization", "Bearer " + accessToken);
        xmlHttp.send(null);
        
     }
     
     
     function getGraphAccessTokenToCallFunction(){
         myMSALObj.acquireTokenSilent(demoScops).then(function (tokenResponse) {
         
              console.log(tokenResponse.accessToken);
              callFunction(tokenResponse.accessToken);
              
         }).catch(function (error) {
              console.log(error);
              })
        }     
     
     function showWelcomeMessage() {
             
         var divWelcome = document.getElementById('WelcomeMessage');
         divWelcome.innerHTML = 'welcome! ' + myMSALObj.account.name ;
         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") {
                 callMSGraph(graphConfig.graphEndpoint, response.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";
     }
     

     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;

     var loginType = isIE ? "REDIRECT" : "POPUP";
     
     if (loginType === 'POPUP') {
          if (myMSALObj.getAccount()) {
              initPage()
          }
     }
     else if (loginType === 'REDIRECT') {
         document.getElementById("SignIn").onclick = function () {
              myMSALObj.loginRedirect(requestObj);
         };
         if (myMSALObj.getAccount() && !myMSALObj.isCallback(window.location.hash)) {
              initPage()
          }
     } else {
         console.error('Please set a valid login type');
     }
     
     

      function signOut() {
          window.localStorage.clear();
          myMSALObj.logout();
      }
    
</script>

</html>

结果:

【讨论】:

  • 我正在使用 Azure AD 凭据(电子邮件和密码)而不是令牌对最终用户进行身份验证,所以在这种情况下,我将如何获取用户电子邮件?
  • 是的,我使用天蓝色活动目录登录进行身份验证,因此需要输入用户名和密码
  • 嗨@Gaurav我已经用演示更新了我的答案。我认为这会有所帮助
  • 感谢代码,但在我的场景中,我必须避免在前端暴露令牌?
  • @Gaurav,令牌永远不会在此演示中公开,这是登录用户并从 Azure AD 获取访问令牌以调用 API 的最标准方式。您的令牌永远不会被公开,因为它们没有硬编码在代码中,只有成功登录的用户才能从 Azure AD 获取令牌。
猜你喜欢
  • 2017-04-05
  • 1970-01-01
  • 2017-10-04
  • 1970-01-01
  • 2012-01-22
  • 1970-01-01
  • 1970-01-01
  • 2022-07-06
  • 1970-01-01
相关资源
最近更新 更多