JavaScript uygulamasını ADAL.js'den MSAL.js geçirme

Microsoft Authentication Library for JavaScript (MSAL.js, msal-browser olarak da bilinir) 2.x, Microsoft kimlik platformundaki JavaScript uygulamalarıyla kullanmanızı önerdiğimiz kimlik doğrulama kitaplığıdır. Bu makalede, MSAL.js 2.x kullanmak için ADAL.js kullanan bir uygulamayı geçirmek için yapmanız gereken değişiklikler vurgulanır

Not

MSAL.js 1.x üzerinde MSAL.js 2.x'i kesinlikle öneririz. Kimlik doğrulama kodu verme akışı daha güvenlidir ve Safari gibi tarayıcıların 3. taraf tanımlama bilgilerini engellemek için uyguladığı gizlilik önlemlerine rağmen tek sayfalı uygulamaların iyi bir kullanıcı deneyimini korumasını sağlar.

Önkoşullar

  • Platform / Yanıtı URL Türü'nü Uygulama Kaydı portalında Tek sayfalı uygulama olarak ayarlamanız gerekir (Uygulama kaydınıza Web gibi başka platformlar eklendiyse, yeniden yönlendirme URI'lerinin çakışmadığından emin olmanız gerekir. Bkz. Yeniden yönlendirme URI'leri kısıtlamaları)
  • Uygulamalarınızı Internet Explorer üzerinde çalıştırmak için MSAL.js'in bağlı olduğu ES6 özellikleri için polyfills sağlamanız gerekir.
  • Henüz yapmadıysanız Microsoft Entra uygulamalarınızı v2 uç noktasına geçirin

MSAL'yi yükleme ve içeri aktarma

MSAL.js 2.x kitaplığını yüklemenin iki yolu vardır:

Npm aracılığıyla:

npm install @azure/msal-browser

Ardından modül sisteminize bağlı olarak aşağıda gösterildiği gibi içeri aktarın:

import * as msal from "@azure/msal-browser"; // ESM

const msal = require('@azure/msal-browser'); // CommonJS

CDN aracılığıyla:

Betiği HTML belgenizin üst bilgi bölümüne yükleyin:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js"></script>
  </head>
</html>

CDN kullanırken alternatif CDN bağlantıları ve en iyi yöntemler için bkz. CDN Kullanımı

MSAL'ı başlatma

ADAL.jsiçinde AuthenticationContext sınıfının örneğini oluşturursunuz. Bu sınıf, kimlik doğrulaması (login, acquireTokenPopup vb.) elde etmek için kullanabileceğiniz yöntemleri kullanıma sunar. Bu nesne, uygulamanızın yetkilendirme sunucusuna veya kimlik sağlayıcısına olan bağlantısını temsil eder. Başlatılırken tek zorunlu parametre clientId değeridir:

window.config = {
  clientId: "YOUR_CLIENT_ID"
};

var authContext = new AuthenticationContext(config);

MSAL.jsiçinde, bunun yerine PublicClientApplication sınıfının örneğini oluşturursunuz. ADAL.jsgibi oluşturucu da en azından parametresini içeren bir clientId bekler. Daha fazla bilgi için bkz: MSAL.jsbaşlatma

const msalConfig = {
  auth: {
      clientId: 'YOUR_CLIENT_ID'
  }
};

const msalInstance = new msal.PublicClientApplication(msalConfig);

Hem ADAL.js hem de MSAL.js, belirtmezseniz, yetki URI’si varsayılan olarak https://login.microsoftonline.com/common değerine ayarlanır.

Not

v2.0'da https://login.microsoftonline.com/common yetkilisini kullanırsanız, kullanıcıların herhangi bir Microsoft Entra kuruluşuyla veya kişisel bir Microsoft hesabıyla (MSA) oturum açmasına izin verirsiniz. MSAL.js'da, herhangi bir Microsoft Entra hesabıyla oturum açmayı kısıtlamak istiyorsanız (ADAL.jsile aynı davranış), bunun yerine https://login.microsoftonline.com/organizations kullanın.

MSAL'yi yapılandırma

AuthenticationContext başlatılırken kullanılan ADAL.jsyapılandırma seçeneklerinden bazıları MSAL.js'de kullanım dışı bırakılırken, bazı yeni seçenekler kullanıma sunulmuştur. Kullanılabilir seçeneklerin tam listesine bakın. Daha da önemlisi, clientId hariç bu seçeneklerin çoğu belirteç alma sırasında geçersiz kılınabilir ve bunları istek temelinde ayarlamanıza olanak sağlar. Örneğin, belirteçleri alırken başlatma sırasında ayarladığınızdan farklı bir otorite URI'si veya yeniden yönlendirme URI'sini kullanabilirsiniz.

Ayrıca, yapılandırma seçenekleri aracılığıyla oturum açma deneyimini (yani açılır pencereleri kullanma veya sayfayı yeniden yönlendirme) belirtmeniz gerekmez. Bunun yerine, MSAL.js örnek aracılığıyla loginPopup ve loginRedirect yöntemlerini kullanıma sunarPublicClientApplication.

Log kaydını etkinleştir

ADAL.js'da, günlüğü kodunuzun herhangi bir yerinde ayrı olarak yapılandırabilirsiniz:

window.config = {
  clientId: "YOUR_CLIENT_ID"
};

var authContext = new AuthenticationContext(config);

var Logging = {
  level: 3,
  log: function (message) {
      console.log(message);
  },
  piiLoggingEnabled: false
};


authContext.log(Logging)

MSAL.js'de günlük kaydı, yapılandırma seçeneklerinin bir parçası olarak başlatılır ve PublicClientApplication üzerinde başlatma sırasında oluşturulur.

const msalConfig = {
  auth: {
      // authentication related parameters
  },
  cache: {
      // cache related parameters
  },
  system: {
      loggerOptions: {
          loggerCallback(loglevel, message, containsPii) {
              console.log(message);
          },
          piiLoggingEnabled: false,
          logLevel: msal.LogLevel.Verbose,
      }
  }
}

const msalInstance = new msal.PublicClientApplication(msalConfig);

MSAL API'sine geçiş yapma

ADAL.js'daki bazı genel yöntemlerin MSAL.js eşdeğerleri vardır:

ADAL (ADAL) MSAL (Türkçe) Notlar
acquireToken acquireTokenSilent Yeniden adlandırıldı ve şimdi bir hesap nesnesi bekliyor
acquireTokenPopup acquireTokenPopup Şimdi asenkron çalışır ve bir promise döndürür
acquireTokenRedirect acquireTokenRedirect Şimdi asenkron çalışır ve bir promise döndürür
handleWindowCallback handleRedirectPromise Yeniden yönlendirme deneyimi kullanılıyorsa gereklidir
getCachedUser getAllAccounts Yeniden adlandırıldı ve şimdi hesapların bir dizisini döndürür.

Diğerleri kullanım dışı bırakılmıştır ancak MSAL.js yeni yöntemler sunar:

ADAL (ADAL) MSAL (Türkçe) Notlar
login Yok Kullanımdan kaldırıldı. Kullan loginPopup veya loginRedirect
logOut Yok Kullanımdan kaldırıldı. logoutPopup veya logoutRedirect
Yok loginPopup
Yok loginRedirect
Yok logoutPopup
Yok logoutRedirect
Yok getAccountByHomeId Hesapları ev kimliğine (oid + kiracı kimliği) göre filtreler
Yok getAccountLocalId Hesapları yerel kimlikle filtreler (ADFS için kullanışlıdır)
Yok getAccountUsername Hesapları kullanıcı adına göre filtreler (varsa)

Ayrıca, MSAL.js ADAL.js aksine TypeScript'te uygulandığından, projelerinizde kullanabileceğiniz çeşitli türleri ve arabirimleri kullanıma sunar. Daha fazla bilgi almak için MSAL.js API başvurusuna bakın.

Kaynaklar yerine kapsamları kullanma

Azure Active Directory v1.0 ile 2.0 uç noktaları arasındaki önemli bir fark, kaynaklara nasıl erişildiğinden farklıdır. v1.0 uç noktasıyla ADAL.js kullanırken, önce uygulama kayıt portalına bir izin kaydeder ve ardından aşağıda gösterildiği gibi bir kaynak (Microsoft Graph gibi) için erişim belirteci isteyebilirsiniz:

authContext.acquireTokenRedirect("https://graph.microsoft.com", function (error, token) {
  // do something with the access token
});

MSAL.js yalnızca v2.0 uç noktasını destekler. v2.0 uç noktası, kaynaklara erişmek için kapsam odaklı bir model kullanır. Bu nedenle, bir kaynak için erişim belirteci istediğinizde, bu kaynağın kapsamını da belirtmeniz gerekir:

msalInstance.acquireTokenRedirect({
  scopes: ["https://graph.microsoft.com/User.Read"]
});

Kapsam odaklı modelin avantajlarından biri dinamik kapsamları kullanabilmektir. v1.0 uç noktasını kullanarak uygulama oluştururken, kullanıcının oturum açma sırasında onay verebilmek için uygulamanın gerektirdiği tam izin kümesini ( statik kapsamlar olarak adlandırılır) kaydetmeniz gerekiyordu. v2.0'da scope parametresini kullanarak izinleri istediğiniz anda isteyebilirsiniz (dolayısıyla dinamik kapsamlar). Bu, kullanıcının kapsamlara artımlı onay sağlamasına olanak tanır. Bu nedenle, başlangıçta yalnızca kullanıcının uygulamanızda oturum açmasını istiyorsanız ve herhangi bir erişime ihtiyacınız yoksa, bunu yapabilirsiniz. Daha sonra kullanıcının takvimini okuma yeteneğine ihtiyacınız varsa, acquireToken yöntemlerinde takvim kapsamını isteyebilir ve kullanıcının onayını alabilirsiniz. Daha fazla bilgi için bkz. Kaynaklar ve kapsamlar

Geri çağırmalar yerine Promises kullanın

ADAL.js kimlik doğrulaması başarılı olduktan ve bir yanıt alındıktan sonra geri çağırmalar herhangi bir işlem için kullanılır:

authContext.acquireTokenPopup(resource, extraQueryParameter, claims, function (error, token) {
  // do something with the access token
});

MSAL.js içinde, bunun yerine promises kullanılır.

msalInstance.acquireTokenPopup({
      scopes: ["User.Read"] // shorthand for https://graph.microsoft.com/User.Read
  }).then((response) => {
      // do something with the auth response
  }).catch((error) => {
      // handle errors
  });

ES8 ile birlikte gelen async/await söz dizimini de kullanabilirsiniz:

const getAccessToken = async() => {
  try {
      const authResponse = await msalInstance.acquireTokenPopup({
          scopes: ["User.Read"]
      });
  } catch (error) {
      // handle errors
  }
}

Belirteçleri önbelleğe alma ve geri alma

ADAL.jsgibi MSAL.js de Web Depolama API'sini kullanarak tarayıcı depolamasında belirteçleri ve diğer kimlik doğrulama yapıtlarını önbelleğe alır. Kullanıcılarınız tarafından alınan belirteçleri depolamada daha güvenli olduğundan, ancak sessionStorage sekmeler ve kullanıcı oturumları arasında Çoklu Oturum Açma olanağı sunduğundan seçeneği kullanmanız localStorage önerilir (bkz. yapılandırma).

Daha da önemlisi, önbelleğe doğrudan erişmeniz gerekmez. Bunun yerine, erişim belirteçleri veya kullanıcı hesapları gibi kimlik doğrulama yapıtlarını almak için uygun bir MSAL.js API'sini kullanmanız gerekir.

Jetonları yenileme jetonlarıyla yenileştir

ADAL.js güvenlik nedeniyle yenileme belirteçleri döndürmeyen OAuth 2.0 örtük akışını kullanır (yenileme belirteçlerinin ömrü erişim belirteçlerinden daha uzun olur ve bu nedenle kötü amaçlı aktörlerin elinde daha tehlikelidir). Bu nedenle, ADAL.js, kullanıcının tekrar tekrar kimlik doğrulaması yapması istenmemesi için gizli bir IFrame kullanarak belirteç yenilemesi yapar.

PKCE desteğine sahip kimlik doğrulama kodu akışıyla, MSAL.js 2.x kullanan uygulamalar kimlik ve erişim belirteçleriyle birlikte yenileme belirteçleri alır ve bunları yenilemek için kullanılabilir. Yenileme belirteçlerinin kullanımı soyutlanır ve geliştiricilerin bunların etrafında mantık oluşturmaması gerekir. Bunun yerine MSAL, yenileme belirteçlerini kullanarak belirteç yenilemeyi kendi başına yönetir. Belirteç önbelleği şeması değiştiğinden ve ADAL.js kullanılan şemayla uyumsuz olduğundan ADAL.js içeren önceki belirteç önbelleğiniz MSAL.js aktarılamaz.

Hataları ve özel durumları işleme

MSAL.jskullanırken karşılaşabileceğiniz en yaygın hata türü hatadır interaction_in_progress . Bu hata, başka bir etkileşimli API sürmekteyken etkileşimli bir API (loginPopup, loginRedirect, acquireTokenPopup, acquireTokenRedirect) çağrıldığında oluşur. login* ve acquireToken* API'leri zaman uyumsuz olduğu için, başka birini çağırmadan önce ilgili promise'ların çözüldüğünden emin olmanız gerekir.

Bir diğer yaygın hata ise şeklindedir interaction_required. Bu hata genellikle etkileşimli bir belirteç alma istemi başlatılarak çözülür. Örneğin, erişmeye çalıştığınız web API'sinde kullanıcının çok faktörlü kimlik doğrulaması (MFA) gerçekleştirmesini gerektiren bir Koşullu Erişim ilkesi olabilir. Bu durumda, interaction_required hatası işleme alınarak acquireTokenPopup veya acquireTokenRedirect tetiklenir, ve bu da kullanıcının MFA gerçekleştirmesini sağlar.

Yine de, korumalı bir kaynak için erişim belirteci almak için gereken izinler kullanıcı tarafından onaylanmadığında oluşan bir diğer yaygın hatayla karşılaşabilirsiniz consent_required. interaction_required gibi, consent_required hatasının çözümü sıklıkla acquireTokenPopup veya acquireTokenRedirect kullanarak etkileşimli bir belirteç alma istemi başlatmaktır.

Daha fazla bilgi için bkz. Yaygın MSAL.js hataları ve bunların nasıl işleneceğini

Olaylar API'sini kullanma

MSAL.js (>=v2.4), uygulamalarınızda kullanabileceğiniz bir olay API'sini tanıtır. Bu olaylar, kimlik doğrulama işlemiyle ve MSAL'nin her an ne yaptığıyla ilgilidir ve kullanıcı arabirimini güncelleştirmek, hata iletilerini göstermek, devam eden bir etkileşim olup olmadığını denetlemek vb. için kullanılabilir. Örneğin, aşağıda herhangi bir nedenle oturum açma işlemi başarısız olduğunda çağrılacak bir olay geri çağırması yer alır:

const callbackId = msalInstance.addEventCallback((message) => {
  // Update UI or interact with EventMessage here
  if (message.eventType === EventType.LOGIN_FAILURE) {
      if (message.error instanceof AuthError) {
          // Do something with the error
      }
    }
});

Performans için, artık gerekli olmadığında olay geri çağırmalarının kaydını kaldırmak önemlidir. Daha fazla bilgi için bkz: MSAL.js Olayları API'si

Birden çok hesabı işleme

ADAL.js, kimliği doğrulanmış olan varlığı temsil eden bir kullanıcı kavramına sahiptir. MSAL.js, bir kullanıcının birden fazla hesabı olabileceği göz önüne alındığında kullanıcılar yerine hesaplar kullanır. Bu, artık birden çok hesabı denetlemeniz ve çalışmak için uygun hesabı seçmeniz gerektiği anlamına da gelir. Aşağıdaki kod parçacığında bu işlem gösterilmektedir:

let homeAccountId = null; // Initialize global accountId (can also be localAccountId or username) used for account lookup later, ideally stored in app state

// This callback is passed into `acquireTokenPopup` and `acquireTokenRedirect` to handle the interactive auth response
function handleResponse(resp) {
  if (resp !== null) {
      homeAccountId = resp.account.homeAccountId; // alternatively: resp.account.homeAccountId or resp.account.username
  } else {
      const currentAccounts = myMSALObj.getAllAccounts();
      if (currentAccounts.length < 1) { // No cached accounts
          return;
      } else if (currentAccounts.length > 1) { // Multiple account scenario
          // Add account selection logic here
      } else if (currentAccounts.length === 1) {
          homeAccountId = currentAccounts[0].homeAccountId; // Single account scenario
      }
  }
}

Daha fazla bilgi için bkz. MSAL.js'deki hesaplar

Sarmalayıcı kitaplıklarını kullanma

Angular ve React çerçeveleri için geliştiriyorsanız, sırasıyla MSAL Angular v2 ve MSAL React'i kullanabilirsiniz. Bu sarmalayıcılar MSAL.js ile aynı genel API'yi kullanıma sunarken, kimlik doğrulama ve belirteç alma işlemlerini kolaylaştırabilecek çerçeveye özgü yöntemler ve bileşenler sunar.

Uygulamayı çalıştırma

Değişiklikleriniz tamamlandıktan sonra uygulamayı çalıştırın ve kimlik doğrulama senaryonuzu test edin:

npm start

Örnek: spayı ADAL.js ve MSAL.js ile güvenli hale getirme

Aşağıdaki kod parçacıkları, tek sayfalı bir uygulamanın Microsoft kimlik platformuyla kullanıcıların kimliğini doğrulamasını ve ilk ADAL.js ve ardından MSAL.jskullanarak Microsoft Graph için erişim belirteci alma için gereken en düşük kodu gösterir:

ADAL.js kullanma MSAL.js kullanma

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="https://alcdn.msauth.net/lib/1.0.18/js/adal.min.js"></script>
</head>

<body>
    <div>
        <p id="welcomeMessage" style="visibility: hidden;"></p>
        <button id="loginButton">Login</button>
        <button id="logoutButton" style="visibility: hidden;">Logout</button>
        <button id="tokenButton" style="visibility: hidden;">Get Token</button>
    </div>
    <script>
        // DOM elements to work with
        var welcomeMessage = document.getElementById("welcomeMessage");
        var loginButton = document.getElementById("loginButton");
        var logoutButton = document.getElementById("logoutButton");
        var tokenButton = document.getElementById("tokenButton");

        // if user is logged in, update the UI
        function updateUI(user) {
            if (!user) {
                return;
            }

            welcomeMessage.innerHTML = 'Hello ' + user.profile.upn + '!';
            welcomeMessage.style.visibility = "visible";
            logoutButton.style.visibility = "visible";
            tokenButton.style.visibility = "visible";
            loginButton.style.visibility = "hidden";
        };

        // attach logger configuration to window
        window.Logging = {
            piiLoggingEnabled: false,
            level: 3,
            log: function (message) {
                console.log(message);
            }
        };

        // ADAL configuration
        var adalConfig = {
            instance: 'https://login.microsoftonline.com/',
            clientId: "ENTER_CLIENT_ID_HERE",
            tenant: "ENTER_TENANT_ID_HERE",
            redirectUri: "ENTER_REDIRECT_URI_HERE",
            cacheLocation: "sessionStorage",
            popUp: true,
            callback: function (errorDesc, token, error, tokenType) {
                if (error) {
                    console.log(error, errorDesc);
                } else {
                    updateUI(authContext.getCachedUser());
                }
            }
        };

        // instantiate ADAL client object
        var authContext = new AuthenticationContext(adalConfig);

        // handle redirect response or check for cached user
        if (authContext.isCallback(window.location.hash)) {
            authContext.handleWindowCallback();
        } else {
            updateUI(authContext.getCachedUser());
        }

        // attach event handlers to button clicks
        loginButton.addEventListener('click', function () {
            authContext.login();
        });

        logoutButton.addEventListener('click', function () {
            authContext.logOut();
        });

        tokenButton.addEventListener('click', () => {
            authContext.acquireToken(
                "https://graph.microsoft.com",
                function (errorDesc, token, error) {
                    if (error) {
                        console.log(error, errorDesc);

                        authContext.acquireTokenPopup(
                            "https://graph.microsoft.com",
                            null, // extraQueryParameters
                            null, // claims
                            function (errorDesc, token, error) {
                                if (error) {
                                    console.log(error, errorDesc);
                                } else {
                                    console.log(token);
                                }
                            }
                        );
                    } else {
                        console.log(token);
                    }
                }
            );
        });
    </script>
</body>

</html>


<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.34.0/js/msal-browser.min.js"></script>
</head>

<body>
    <div>
        <p id="welcomeMessage" style="visibility: hidden;"></p>
        <button id="loginButton">Login</button>
        <button id="logoutButton" style="visibility: hidden;">Logout</button>
        <button id="tokenButton" style="visibility: hidden;">Get Token</button>
    </div>
    <script>
        // DOM elements to work with
        const welcomeMessage = document.getElementById("welcomeMessage");
        const loginButton = document.getElementById("loginButton");
        const logoutButton = document.getElementById("logoutButton");
        const tokenButton = document.getElementById("tokenButton");

        // if user is logged in, update the UI
        const updateUI = (account) => {
            if (!account) {
                return;
            }

            welcomeMessage.innerHTML = `Hello ${account.username}!`;
            welcomeMessage.style.visibility = "visible";
            logoutButton.style.visibility = "visible";
            tokenButton.style.visibility = "visible";
            loginButton.style.visibility = "hidden";
        };

        // MSAL configuration
        const msalConfig = {
            auth: {
                clientId: "ENTER_CLIENT_ID_HERE",
                authority: "https://login.microsoftonline.com/ENTER_TENANT_ID_HERE",
                redirectUri: "ENTER_REDIRECT_URI_HERE",
            },
            cache: {
                cacheLocation: "sessionStorage"
            },
            system: {
                loggerOptions: {
                    loggerCallback(loglevel, message, containsPii) {
                        console.log(message);
                    },
                    piiLoggingEnabled: false,
                    logLevel: msal.LogLevel.Verbose,
                }
            }
        };

        // instantiate MSAL client object
        const pca = new msal.PublicClientApplication(msalConfig);

        // handle redirect response or check for cached user
        pca.handleRedirectPromise().then((response) => {
            if (response) {
                pca.setActiveAccount(response.account);
                updateUI(response.account);
            } else {
                const account = pca.getAllAccounts()[0];
                updateUI(account);
            }
        }).catch((error) => {
            console.log(error);
        });

        // attach event handlers to button clicks
        loginButton.addEventListener('click', () => {
            pca.loginPopup().then((response) => {
                pca.setActiveAccount(response.account);
                updateUI(response.account);
            })
        });

        logoutButton.addEventListener('click', () => {
            pca.logoutPopup().then((response) => {
                window.location.reload();
            });
        });

        tokenButton.addEventListener('click', () => {
            const account = pca.getActiveAccount();

            pca.acquireTokenSilent({
                account: account,
                scopes: ["User.Read"]
            }).then((response) => {
                console.log(response);
            }).catch((error) => {
                if (error instanceof msal.InteractionRequiredAuthError) {
                    pca.acquireTokenPopup({
                        scopes: ["User.Read"]
                    }).then((response) => {
                        console.log(response);
                    });
                }

                console.log(error);
            });
        });
    </script>
</body>

</html>

Sonraki adımlar