SPA 的 MSAL JS 目录选择器(切换目录)组件

MSAL JS Directory Chooser (Switch Directory) Component for SPA

Microsoft 是否有(或是否有人知道的任何自定义 JS 组件)目录选择器来为 AAD 安全 SPA 提供“切换目录”功能(当前使用 MSAL JS https://github.com/AzureAD/microsoft-authentication-library-for-js)?

如果你想通过 msal.js 切换 SPA 中的租户,似乎没有关于它的官方演示。根据我的理解,如果你想这样做,你应该解决两件事:

  1. 您的应用应该能够获取当前帐户所属的所有租户。
  2. public 客户端 Azure AD 应用程序应该是 multi-tenant one 以便用户能够无缝登录到不同的租户。

对于第1点,我们可以使用this API获取所有用户租户。此 API 属于 Azure 管理 rest API,因此您的 public 客户端 Azure AD 应用程序应获得以下权限,以便登录的用户可以调用此 API 获取所有租户:

我msal.js为你写了一个简单的演示,我认为可以满足你的要求,只需尝试下面的HTML页面代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    
    <title>Azure AD 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"/><br/>
    </div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
    
    var clientAppID = "<multi tenant public client Azure ad app id>"
    var tenantID = "<tenant ID for default login >"
    
    var demoScops = {
         scopes:["https://management.azure.com/user_impersonation"]
    }

    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();
        getAllTenants();
        
     }
     

     
     function showWelcomeMessage() {
             
         var divWelcome = document.getElementById('WelcomeMessage');
         divWelcome.innerHTML = 'welcome! ' + myMSALObj.account.userName + '</br>';
         var loginbutton = document.getElementById('SignIn');
         loginbutton.innerHTML = 'sign out';
         loginbutton.setAttribute('onclick', 'signOut();');
     }
     
     function getAllTenants(){
         myMSALObj.acquireTokenSilent(demoScops).then(function (tokenResponse) {
         
             var accessToken = tokenResponse.accessToken;
             $.ajax({
                 url: "https://management.azure.com/tenants?api-version=2020-01-01",
                 type: "GET",
                 async: false,
                 beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Bearer '+ accessToken);},
                 success: function(data) { 
                    var divWelcome = document.getElementById('WelcomeMessage');
                    divWelcome.innerHTML += "  your current tenant: "+ myMSALObj.account.idToken.tid +", all your tenants :</br>"
                    data.value.forEach(item=>{
                        var tentantItem  = "<div id='"+item.tenantId+"' style='border: 2px solid grey; margin:5px; width:500px' onclick='switchTenant(this)' > name :"+item.displayName+ "     Tenant  ID:"+ item.tenantId +"</div>" 
                        divWelcome.innerHTML += tentantItem;
                    })
                 }
              });
              
              
         }).catch(function (error) {
              console.log(error);
              })
        }     
     
     function switchTenant(obj){

        var msalConfig = {
                 auth: {
                     clientId: clientAppID,
                     authority: "https://login.microsoftonline.com/" + $(obj).attr('id')
                },
                 cache: {
                     cacheLocation: "localStorage",
                     storeAuthStateInCookie: true
                }
    };
     
    
    
    var myMSALObj = new Msal.UserAgentApplication(msalConfig);
    myMSALObj.handleRedirectCallback(authRedirectCallBack);
    
     myMSALObj.loginPopup(demoScops).then(function (loginResponse) {
            console.log(loginResponse);
            location.reload();
             
         }).catch(function (error) {
             console.log(error);
         });
    
    
    }
     
     
     function authRedirectCallBack(error, response) {
         if (error) {
             console.log(error);
         }
     }
     
     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>

结果:

登录后,它会显示我当前的租户 ID 和我所有的租户:

单击租户项目后,会提示登录 window 并在成功登录后重新定位页面:

终于: