Cara memigrasikan aplikasi Node.js dari ADAL ke MSAL

Microsoft Authentication Library for Node (MSAL Node) sekarang menjadi SDK yang direkomendasikan untuk mengaktifkan autentikasi dan otorisasi untuk aplikasi Anda yang terdaftar di platform identitas Microsoft. Artikel ini membahas langkah-langkah penting yang perlu Anda lalui untuk memigrasikan aplikasi Anda dari Active Directory Authentication Library for Node (ADAL Node) ke MSAL Node.

Prasyarat

Memperbarui pengaturan pendaftaran aplikasi

Saat bekerja dengan ADAL Node, Anda kemungkinan menggunakan titik akhir Azure AD v1.0. Aplikasi yang bermigrasi dari ADAL ke MSAL harus beralih ke titik akhir Azure AD v2.0.

Menginstal dan mengimpor MSAL

  1. instal paket MSAL Node melalui npm:
  npm install @azure/msal-node
  1. Setelah itu, impor MSAL Node dalam kode Anda:
  const msal = require('@azure/msal-node');
  1. Terakhir, hapus instalan paket ADAL Node dan hapus referensi apa pun dalam kode Anda:
  npm uninstall adal-node

Menginisialisasi MSAL

Di ADAL Node, Anda menginisialisasi AuthenticationContext objek, yang kemudian mengekspos metode yang dapat Anda gunakan dalam alur autentikasi yang berbeda (misalnya, acquireTokenWithAuthorizationCode untuk aplikasi web). Saat menginisialisasi, satu-satunya parameter wajib adalah URI otoritas:

var adal = require('adal-node');

var authorityURI = "https://login.microsoftonline.com/common";
var authenticationContext = new adal.AuthenticationContext(authorityURI);

Di MSAL Node, Anda memiliki dua alternatif sebagai gantinya: Jika Anda membangun aplikasi seluler atau aplikasi desktop, Anda membuat PublicClientApplication instans objek. Konstruktor mengharapkan objek konfigurasi yang berisi clientId parameter paling sedikit. MSAL mengatur default URI otoritas jika https://login.microsoftonline.com/common Anda tidak menentukannya.

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

const pca = new msal.PublicClientApplication({
        auth: {
            clientId: "YOUR_CLIENT_ID"
        }
    });

Catatan

Jika Anda menggunakan otoritas https://login.microsoftonline.com/common di v2.0, Anda akan mengizinkan pengguna untuk masuk dengan organisasi Microsoft Entra atau akun Microsoft pribadi (MSA). Di MSAL Node, jika Anda ingin membatasi login ke akun Microsoft Entra apa pun (perilaku yang sama seperti node ADAL), gunakan https://login.microsoftonline.com/organizations sebagai gantinya.

Di sisi lain, jika Anda membuat aplikasi web atau aplikasi daemon, Anda membuat ConfidentialClientApplication instans objek. Dengan aplikasi tersebut, Anda juga perlu memberikan kredensial klien, seperti rahasia klien atau sertifikat:

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

const cca = new msal.ConfidentialClientApplication({
        auth: {
            clientId: "YOUR_CLIENT_ID",
            clientSecret: "YOUR_CLIENT_SECRET"
        }
    });

Baik PublicClientApplication dan ConfidentialClientApplication, tidak seperti ADAL AuthenticationContext, terikat ke ID klien. Artinya jika Anda memiliki ID klien yang berbeda yang ingin Anda gunakan dalam aplikasi Anda, Anda perlu membuat contoh instans MSAL baru untuk masing-masing. Lihat selengkapnya: Inisialisasi Node MSAL

Mengonfigurasi MSAL

Saat membangun aplikasi di platform identitas Microsoft, aplikasi Anda akan berisi banyak parameter yang terkait dengan autentikasi. Di ADAL Node, AuthenticationContext objek memiliki sejumlah parameter konfigurasi terbatas yang dapat Anda buat, sementara parameter yang tersisa menggantung dengan bebas dalam kode Anda (misalnya, clientSecret):

var adal = require('adal-node');

var authority = "https://login.microsoftonline.com/YOUR_TENANT_ID"
var validateAuthority = true,
var cache = null;

var authenticationContext = new adal.AuthenticationContext(authority, validateAuthority, cache);
  • authority: URL yang mengidentifikasi otoritas token
  • validateAuthority: fitur yang mencegah kode Anda meminta token dari otoritas yang berpotensi berbahaya
  • cache: mengatur cache token yang digunakan oleh instans AuthenticationContext ini. Jika parameter ini tidak diatur, maka cache memori default akan digunakan.

Simpul MSAL di sisi lain menggunakan objek konfigurasi jenis Konfigurasi. Berisi properti berikut:

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

const msalConfig = {
    auth: {
        clientId: "YOUR_CLIENT_ID",
        authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
        clientSecret: "YOUR_CLIENT_SECRET",
        knownAuthorities: [],
    },
    cache: {
        // your implementation of caching
    },
    system: {
        loggerOptions: { /** logging related options */ }
    }
}


const cca = new msal.ConfidentialClientApplication(msalConfig);

Sebagai perbedaan penting, MSAL tidak memiliki bendera untuk menonaktifkan validasi otoritas dan otoritas selalu divalidasi secara default. MSAL membandingkan otoritas yang Anda minta dengan daftar otoritas yang diketahui oleh Microsoft atau daftar otoritas yang Anda tentukan dalam konfigurasi Anda. Lihat selengkapnya: Opsi Konfigurasi

Beralih ke API MSAL

Sebagian besar metode publik di ADAL Node memiliki ekuivalen di MSAL Node:

ADAL MSAL (Microsoft Authentication Library) Catatan
acquireToken acquireTokenSilent Diganti namanya dan sekarang mengharapkan objek akun
acquireTokenWithAuthorizationCode acquireTokenByCode
acquireTokenWithClientCredentials acquireTokenByClientCredential
acquireTokenWithRefreshToken acquireTokenByRefreshToken Berguna untuk memigrasikan token refresh yang valid
acquireTokenWithDeviceCode acquireTokenByDeviceCode Sekarang menyederhanakan pengambilan kode pengguna (lihat di bawah)
acquireTokenWithUsernamePassword acquireTokenByUsernamePassword

Namun, beberapa metode dalam ADAL Node ditolak, sewaktu MSAL Node menawarkan metode baru:

ADAL MSAL (Microsoft Authentication Library) Catatan
acquireUserCode T/A Digabungkan dengan acquireTokeByDeviceCode (lihat di atas)
T/A acquireTokenOnBehalfOf Metode baru yang mengabstraksi alur OBO
acquireTokenWithClientCertificate T/A Tidak lagi diperlukan karena sertifikat ditetapkan selama inisialisasi sekarang (lihat opsi konfigurasi)
T/A getAuthCodeUrl Metode baru yang mengabstraksi konstruksi URL titik akhir otorisasi

Menggunakan cakupan sebagai ganti sumber daya

Perbedaan penting antara titik akhir v1.0 vs. v2.0 adalah tentang cara akses sumber daya. Di ADAL Node, Anda akan terlebih dahulu mendaftarkan izin di portal pendaftaran aplikasi, lalu meminta token akses untuk sumber daya (seperti Microsoft Graph) seperti yang ditunjukkan di bawah ini:

authenticationContext.acquireTokenWithAuthorizationCode(
    req.query.code,
    redirectUri,
    resource, // e.g. 'https://graph.microsoft.com'
    clientId,
    clientSecret,
    function (err, response) {
        // do something with the authentication response
    }
);

MSAL Node hanya mendukung titik akhir v2.0 . Titik akhir v2.0 menggunakan model yang berfokus pada cakupan untuk mengakses sumber daya. Jadi, ketika Anda meminta token akses untuk sumber daya, Anda juga perlu menentukan cakupan untuk sumber daya tersebut:

const tokenRequest = {
    code: req.query.code,
    scopes: ["https://graph.microsoft.com/User.Read"],
    redirectUri: REDIRECT_URI,
};

pca.acquireTokenByCode(tokenRequest).then((response) => {
    // do something with the authentication response
}).catch((error) => {
    console.log(error);
});

Salah satu keuntungan dari model yang ber sentris cakupan adalah kemampuan untuk menggunakan cakupan dinamis. Saat membangun aplikasi menggunakan v1.0, Anda perlu mendaftarkan serangkaian izin lengkap (disebut cakupan statis) yang diperlukan oleh aplikasi untuk disetujui pengguna pada saat masuk. Di v2.0, Anda dapat menggunakan parameter cakupan untuk meminta izin pada saat Anda menginginkannya (karenanya, cakupan dinamis). Ini memungkinkan pengguna untuk memberikan persetujuan inkremental untuk cakupan. Jadi, jika pada awalnya Anda hanya ingin pengguna masuk ke aplikasi Anda dan Anda tidak memerlukan akses apa pun, Anda dapat melakukannya. Jika nantinya Anda memerlukan kemampuan untuk membaca kalender pengguna, Anda selanjutnya dapat meminta cakupan kalender dalam metode acquireToken dan mendapatkan persetujuan pengguna. Lihat selengkapnya: Sumber daya dan cakupan

Menggunakan janji sebagai ganti panggilan balik

Di ADAL Node, panggilan balik digunakan untuk operasi apa pun setelah otentikasi berhasil dan respons diperoleh:

var context = new AuthenticationContext(authorityUrl, validateAuthority);

context.acquireTokenWithClientCredentials(resource, clientId, clientSecret, function(err, response) {
    if (err) {
        console.log(err);
    } else {
        // do something with the authentication response
    }
});

Di MSAL Node, janji digunakan sebagai gantinya:

    const cca = new msal.ConfidentialClientApplication(msalConfig);

    cca.acquireTokenByClientCredential(tokenRequest).then((response) => {
        // do something with the authentication response
    }).catch((error) => {
        console.log(error);
    });

Anda juga dapat menggunakan sintaks asinkron/tunggu yang dilengkapi dengan ES8:

    try {
        const authResponse = await cca.acquireTokenByCode(tokenRequest);
    } catch (error) {
        console.log(error);
    }

Aktifkan pencatatan log

Di ADAL Node, Anda mengonfigurasi pengelogan secara terpisah di mana saja dalam kode Anda:

var adal = require('adal-node');

//PII or OII logging disabled. Default Logger does not capture any PII or OII.
adal.logging.setLoggingOptions({
  log: function (level, message, error) {
    console.log(message);

    if (error) {
        console.log(error);
    }
  },
  level: logging.LOGGING_LEVEL.VERBOSE, // provide the logging level
  loggingWithPII: false  // Determine if you want to log personal identification information. The default value is false.
});

Di MSAL Node, pengelogan adalah bagian dari opsi konfigurasi dan dibuat dengan inisialisasi instans MSAL Node:

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

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 cca = new msal.ConfidentialClientApplication(msalConfig);

Mengaktifkan penembolokan token

Di ADAL Node, Anda memiliki opsi untuk mengimpor cache token dalam memori. Cache token digunakan sebagai parameter saat menginisialisasi AuthenticationContext objek:

var MemoryCache = require('adal-node/lib/memory-cache');

var cache = new MemoryCache();
var authorityURI = "https://login.microsoftonline.com/common";

var context = new AuthenticationContext(authorityURI, true, cache);

MSAL Node menggunakan cache token dalam memori secara default. Anda tidak perlu mengimpornya secara eksplisit; cache token dalam memori diekspos sebagai bagian ConfidentialClientApplication dari kelas dan PublicClientApplication .

const msalTokenCache = publicClientApplication.getTokenCache();

Yang penting, cache token Anda sebelumnya dengan ADAL Node tidak akan dapat ditransfer ke MSAL Node, karena skema cache tidak kompatibel. Namun, Anda dapat menggunakan token refresh valid yang diperoleh aplikasi Anda sebelumnya dengan ADAL Node di MSAL Node. Lihat bagian tentang token refresh untuk informasi selengkapnya.

Anda juga dapat menulis cache Anda ke disk dengan menyediakan plugin cache Anda sendiri. Plugin cache harus mengimplementasikan antarmuka ICachePlugin. Seperti pengelogan, caching adalah bagian dari opsi konfigurasi dan dibuat dengan inisialisasi instans MSAL Node:

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

const msalConfig = {
    auth: {
        // authentication related parameters
    },
    cache: {
        cachePlugin // your implementation of cache plugin
    },
    system: {
        // logging related options
    }
}

const msalInstance = new ConfidentialClientApplication(msalConfig);

Contoh plugin cache dapat diimplementasikan seperti di bawah ini:

const fs = require('fs');

// Call back APIs which automatically write and read into a .json file - example implementation
const beforeCacheAccess = async (cacheContext) => {
    cacheContext.tokenCache.deserialize(await fs.readFile(cachePath, "utf-8"));
};

const afterCacheAccess = async (cacheContext) => {
    if(cacheContext.cacheHasChanged) {
        await fs.writeFile(cachePath, cacheContext.tokenCache.serialize());
    }
};

// Cache Plugin
const cachePlugin = {
    beforeCacheAccess,
    afterCacheAccess
};

Jika Anda mengembangkan aplikasi klien publik seperti aplikasi desktop, Microsoft Authentication Extensions for Node menawarkan mekanisme yang aman bagi aplikasi klien untuk melakukan serialisasi dan persistensi cache token lintas platform. Platform yang didukung Windows, Mac, dan Linux.

Catatan

Ekstensi Autentikasi Microsoft untuk Simpul tidak disarankan untuk aplikasi web, karena dapat menyebabkan masalah skala dan performa. Sebagai gantinya, aplikasi web direkomendasikan untuk menyimpan cache dalam sesi.

Menghapus logika di sekitar token refresh

Di ADAL Node, token refresh (RT) diekspos memungkinkan Anda mengembangkan solusi seputar penggunaan token ini dengan menyimpannya dalam cache dan menggunakan acquireTokenWithRefreshToken metode . Skenario umum ketika RT sangat relevan:

  • Layanan jangka panjang yang menjalankan tindakan termasuk me-refresh dasbor atas nama pengguna ketika pengguna tidak lagi terhubung.
  • Skenario WebFarm untuk memungkinkan klien membawa RT ke layanan web (caching dilakukan di sisi klien, cookie terenkripsi, dan bukan sisi server).

MSAL Node, bersama dengan MSAL lainnya, tidak mengekspos token refresh karena alasan keamanan. Sebagai gantinya, MSAL menangani token refreshing untuk Anda. Dengan demikian, Anda tidak perlu lagi membangun logika untuk ini. Namun, Anda dapat menggunakan token refresh yang diperoleh sebelumnya (dan masih valid) dari cache ADAL Node untuk mendapatkan sekumpulan token baru dengan MSAL Node. Untuk melakukan ini, MSAL Node menawarkan acquireTokenByRefreshToken, yang setara dengan metode ADAL Node acquireTokenWithRefreshToken :

var msal = require('@azure/msal-node');

const config = {
    auth: {
        clientId: "ENTER_CLIENT_ID",
        authority: "https://login.microsoftonline.com/ENTER_TENANT_ID",
        clientSecret: "ENTER_CLIENT_SECRET"
    }
};

const cca = new msal.ConfidentialClientApplication(config);

const refreshTokenRequest = {
    refreshToken: "", // your previous refresh token here
    scopes: ["https://graph.microsoft.com/.default"],
    forceCache: true,
};

cca.acquireTokenByRefreshToken(refreshTokenRequest).then((response) => {
    console.log(response);
}).catch((error) => {
    console.log(error);
});

Untuk informasi selengkapnya, silakan merujuk ke sampel MSAL Node.

Catatan

Kami menyarankan Anda untuk menghancurkan cache token ADAL Node yang lebih lama setelah Anda menggunakan token refresh yang masih valid untuk mendapatkan sekumpulan token baru menggunakan metode MSAL Node acquireTokenByRefreshToken seperti yang ditunjukkan di atas.

Menangani kesalahan dan pengecualian

Saat menggunakan MSAL Node, jenis kesalahan paling umum yang mungkin Anda hadapi adalah interaction_required kesalahan. Kesalahan ini sering diatasi dengan memulai permintaan akuisisi token interaktif. Misalnya, saat menggunakan acquireTokenSilent, jika tidak ada token refresh yang di-cache, MSAL Node tidak akan dapat memperoleh token akses secara diam-diam. Demikian pula, API web yang coba Anda akses mungkin memiliki kebijakan Akses Bersyarat, mengharuskan pengguna untuk melakukan autentikasi multifaktor (MFA). Dalam kasus seperti itu, menangani interaction_required kesalahan dengan memicu acquireTokenByCode akan meminta pengguna untuk MFA, memungkinkan mereka untuk memenuhinya.

Namun kesalahan umum lain yang mungkin Anda hadapi adalah consent_required, yang terjadi ketika izin yang diperlukan untuk mendapatkan token akses untuk sumber daya yang dilindungi tidak disetujui oleh pengguna. Seperti dalam interaction_required, solusi untuk consent_required kesalahan sering memulai permintaan akuisisi token interaktif, menggunakan metode .acquireTokenByCode

Menjalankan aplikasi

Setelah perubahan selesai, jalankan aplikasi dan uji skenario autentikasi Anda:

npm start

Contoh: Memperoleh token dengan ADAL Node vs. MSAL Node

Cuplikan di bawah ini menunjukkan aplikasi web klien rahasia dalam kerangka kerja Express.js. Ini melakukan masuk ketika pengguna mencapai rute autentikasi /auth, memperoleh token akses untuk Microsoft Graph melalui rute /redirect dan kemudian menampilkan konten token yang dikatakan.

Menggunakan Node ADAL Menggunakan Node MSAL
// Import dependencies
var express = require('express');
var crypto = require('crypto');
var adal = require('adal-node');

// Authentication parameters
var clientId = 'Enter_the_Application_Id_Here';
var clientSecret = 'Enter_the_Client_Secret_Here';
var tenant = 'Enter_the_Tenant_Info_Here';
var authorityUrl = 'https://login.microsoftonline.com/' + tenant;
var redirectUri = 'http://localhost:3000/redirect';
var resource = 'https://graph.microsoft.com';

// Configure logging
adal.Logging.setLoggingOptions({
    log: function (level, message, error) {
        console.log(message);
    },
    level: adal.Logging.LOGGING_LEVEL.VERBOSE,
    loggingWithPII: false
});

// Auth code request URL template
var templateAuthzUrl = 'https://login.microsoftonline.com/'
    + tenant + '/oauth2/authorize?response_type=code&client_id='
    + clientId + '&redirect_uri=' + redirectUri
    + '&state=<state>&resource=' + resource;

// Initialize express
var app = express();

// State variable persists throughout the app lifetime
app.locals.state = "";

app.get('/auth', function(req, res) {

    // Create a random string to use against XSRF
    crypto.randomBytes(48, function(ex, buf) {
        app.locals.state = buf.toString('base64')
            .replace(/\//g, '_')
            .replace(/\+/g, '-');

        // Construct auth code request URL
        var authorizationUrl = templateAuthzUrl
            .replace('<state>', app.locals.state);

        res.redirect(authorizationUrl);
    });
});

app.get('/redirect', function(req, res) {
    // Compare state parameter against XSRF
    if (app.locals.state !== req.query.state) {
        res.send('error: state does not match');
    }

    // Initialize an AuthenticationContext object
    var authenticationContext =
        new adal.AuthenticationContext(authorityUrl);

    // Exchange auth code for tokens
    authenticationContext.acquireTokenWithAuthorizationCode(
        req.query.code,
        redirectUri,
        resource,
        clientId,
        clientSecret,
        function(err, response) {
            res.send(response);
        }
    );
});

app.listen(3000, function() {
    console.log(`listening on port 3000!`);
});
// Import dependencies
const express = require("express");
const msal = require('@azure/msal-node');

// Authentication parameters
const config = {
    auth: {
        clientId: "Enter_the_Application_Id_Here",
        authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here",
        clientSecret: "Enter_the_Client_Secret_Here"
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
};

const REDIRECT_URI = "http://localhost:3000/redirect";

// Initialize MSAL Node object using authentication parameters
const cca = new msal.ConfidentialClientApplication(config);

// Initialize express
const app = express();

app.get('/auth', (req, res) => {

    // Construct a request object for auth code
    const authCodeUrlParameters = {
        scopes: ["user.read"],
        redirectUri: REDIRECT_URI,
    };

    // Request auth code, then redirect
    cca.getAuthCodeUrl(authCodeUrlParameters)
        .then((response) => {
            res.redirect(response);
        }).catch((error) => res.send(error));
});

app.get('/redirect', (req, res) => {

    // Use the auth code in redirect request to construct
    // a token request object
    const tokenRequest = {
        code: req.query.code,
        scopes: ["user.read"],
        redirectUri: REDIRECT_URI,
    };

    // Exchange the auth code for tokens
    cca.acquireTokenByCode(tokenRequest)
        .then((response) => {
            res.send(response);
        }).catch((error) => res.status(500).send(error));
});

app.listen(3000, () =>
    console.log(`listening on port 3000!`));

Langkah berikutnya