Självstudie: Logga in användare och anropa Microsoft Graph API i en Electron Desktop-app

I den här självstudien skapar du ett Electron Skriv bords program som loggar in användare och anropar Microsoft Graph med hjälp av kod flödet för auktorisering med PKCE. Skriv bords appen som du skapar använder Microsoft Authentication Library (MSAL) för Node.js.

Följ stegen i den här självstudien för att:

  • Registrera programmet i Azure Portal
  • Skapa ett Electron Desktop-projekt
  • Lägg till autentiserings logik i din app
  • Lägga till en metod för att anropa ett webb-API
  • Lägg till registrerings information för appen
  • Testa appen

Förutsättningar

Registrera programmet

Börja med att utföra stegen i Registrera ett program med Microsoft Identity Platform för att registrera din app.

Använd följande inställningar för din app-registrering:

  • Namn: ElectronDesktopApp (rekommenderas)
  • Konto typer som stöds: konton i valfri organisations katalog (alla Azure AD Directory-flera klienter) och personliga Microsoft-konton (t. ex. Skype, Xbox)
  • Plattforms typ: mobil-och skriv bords program
  • Omdirigerings-URI: msal://redirect

Skapa projektet

Skapa en mapp som är värd för ditt program, till exempel ElectronDesktopApp.

  1. Ändra först till projekt katalogen i terminalen och kör sedan följande npm kommandon:

    npm init -y
    npm install --save @azure/msal-node axios bootstrap dotenv jquery popper.js
    npm install --save-dev babel electron@10.1.6 webpack
    
  2. Skapa sedan en mapp med namnet app. I den här mappen skapar du en fil med namnet index.html som fungerar som användar gränssnitt. Lägg till följande kod där:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
        <title>MSAL Node Electron Sample App</title>
    
        <!-- adding Bootstrap 4 for UI components  -->
        <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
    
        <link rel="SHORTCUT ICON" href="https://c.s-microsoft.com/favicon.ico?v2" type="image/x-icon">
    </head>
    
    <body>
        <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
            <a class="navbar-brand">Microsoft identity platform</a>
            <div class="btn-group ml-auto dropleft">
                <button type="button" id="signIn" class="btn btn-secondary" aria-expanded="false">
                    Sign in
                </button>
                <button type="button" id="signOut" class="btn btn-success" hidden aria-expanded="false">
                    Sign out
                </button>
            </div>
        </nav>
        <br>
        <h5 class="card-header text-center">Electron sample app calling MS Graph API using MSAL Node</h5>
        <br>
        <div class="row" style="margin:auto">
            <div id="cardDiv" class="col-md-3" style="display:none">
                <div class="card text-center">
                    <div class="card-body">
                        <h5 class="card-title" id="WelcomeMessage">Please sign-in to see your profile and read your mails
                        </h5>
                        <div id="profileDiv"></div>
                        <br>
                        <br>
                        <button class="btn btn-primary" id="seeProfile">See Profile</button>
                        <br>
                        <br>
                        <button class="btn btn-primary" id="readMail">Read Mails</button>
                    </div>
                </div>
            </div>
            <br>
            <br>
            <div class="col-md-4">
                <div class="list-group" id="list-tab" role="tablist">
                </div>
            </div>
            <div class="col-md-5">
                <div class="tab-content" id="nav-tabContent">
                </div>
            </div>
        </div>
        <br>
        <br>
    
        <script>
            window.jQuery = window.$ = require('jquery');
            require("./renderer.js");
        </script>
    
        <!-- importing bootstrap.js and supporting js libraries -->
        <script src="../node_modules/jquery/dist/jquery.js"></script>
        <script src="../node_modules/popper.js/dist/umd/popper.js"></script>
        <script src="../node_modules/bootstrap/dist/js/bootstrap.js"></script>
    </body>
    
    </html>
    
  3. Skapa sedan en fil med namnet main.js och Lägg till följande kod:

    require('dotenv').config()
    
    const path = require('path');
    const { app, ipcMain, BrowserWindow } = require('electron');
    const { IPC_MESSAGES } = require('./constants');
    
    const { callEndpointWithToken } = require('./fetch');
    const AuthProvider = require('./AuthProvider');
    
    const authProvider = new AuthProvider();
    let mainWindow;
    
    function createWindow () {
        mainWindow = new BrowserWindow({
            width: 800,
            height: 600,
            webPreferences: {
            nodeIntegration: true
            }
        });
    
        mainWindow.loadFile(path.join(__dirname, './index.html'));
        };
    
    app.on('ready', () => {
        createWindow();
    });
    
    app.on('window-all-closed', () => {
        app.quit();
    });
    
    
    // Event handlers
    ipcMain.on(IPC_MESSAGES.LOGIN, async() => {
        const account = await authProvider.login(mainWindow);
    
        await mainWindow.loadFile(path.join(__dirname, './index.html'));
    
        mainWindow.webContents.send(IPC_MESSAGES.SHOW_WELCOME_MESSAGE, account);
    });
    
    ipcMain.on(IPC_MESSAGES.LOGOUT, async() => {
        await authProvider.logout();
        await mainWindow.loadFile(path.join(__dirname, './index.html'));
    });
    
    ipcMain.on(IPC_MESSAGES.GET_PROFILE, async() => {
    
        const tokenRequest = {
            scopes: ['User.Read'],
        };
    
        const token = await authProvider.getToken(mainWindow, tokenRequest);
        const account = authProvider.account
    
        await mainWindow.loadFile(path.join(__dirname, './index.html'));
    
        const graphResponse = await callEndpointWithToken(`${process.env.GRAPH_ENDPOINT_HOST}${process.env.GRAPH_ME_ENDPOINT}`, token);
    
        mainWindow.webContents.send(IPC_MESSAGES.SHOW_WELCOME_MESSAGE, account);
        mainWindow.webContents.send(IPC_MESSAGES.SET_PROFILE, graphResponse);
    });
    
    ipcMain.on(IPC_MESSAGES.GET_MAIL, async() => {
    
        const tokenRequest = {
            scopes: ['Mail.Read'],
        };
    
        const token = await authProvider.getToken(mainWindow, tokenRequest);
        const account = authProvider.account;
    
        await mainWindow.loadFile(path.join(__dirname, './index.html'));
    
        const graphResponse = await callEndpointWithToken(`${process.env.GRAPH_ENDPOINT_HOST}${process.env.GRAPH_MAIL_ENDPOINT}`, token);
    
        mainWindow.webContents.send(IPC_MESSAGES.SHOW_WELCOME_MESSAGE, account);
        mainWindow.webContents.send(IPC_MESSAGES.SET_MAIL, graphResponse);
    });
    

I kodfragmentet ovan initierar vi ett Electron huvud fönster och skapar vissa händelse hanterare för interaktioner med Electron-fönstret. Vi kan också importera konfigurations parametrar, instansiera authProvider -klassen för att hantera inloggning, logga ut och hämta token och anropa Microsoft Graph-API: et.

  1. I samma mapp (app) skapar du en annan fil med namnet renderer.js och lägger till följande kod:

    const { ipcRenderer } = require('electron');
    const { IPC_MESSAGES } = require('./constants');
    
    // UI event handlers
    document.querySelector('#signIn').addEventListener('click', () => {
        ipcRenderer.send(IPC_MESSAGES.LOGIN);
    });
    
    document.querySelector('#signOut').addEventListener('click', () => {
        ipcRenderer.send(IPC_MESSAGES.LOGOUT);
    });
    
    document.querySelector('#seeProfile').addEventListener('click', () => {
        ipcRenderer.send(IPC_MESSAGES.GET_PROFILE);
    });
    
    document.querySelector('#readMail').addEventListener('click', () => {
        ipcRenderer.send(IPC_MESSAGES.GET_MAIL);
    });
    
    // Main process message subscribers
    ipcRenderer.on(IPC_MESSAGES.SHOW_WELCOME_MESSAGE, (event, account) => {
        showWelcomeMessage(account);
    });
    
    ipcRenderer.on(IPC_MESSAGES.SET_PROFILE, (event, graphResponse) => {
        updateUI(graphResponse, `${process.env.GRAPH_ENDPOINT_HOST}${process.env.GRAPH_ME_ENDPOINT}`);
    });
    
    ipcRenderer.on(IPC_MESSAGES.SET_MAIL, (event, graphResponse) => {
        updateUI(graphResponse, `${process.env.GRAPH_ENDPOINT_HOST}${process.env.GRAPH_MAIL_ENDPOINT}`);
    });
    
    // DOM elements to work with
    const welcomeDiv = document.getElementById("WelcomeMessage");
    const signInButton = document.getElementById("signIn");
    const signOutButton = document.getElementById("signOut");
    const cardDiv = document.getElementById("cardDiv");
    const profileDiv = document.getElementById("profileDiv");
    const tabList = document.getElementById("list-tab");
    const tabContent = document.getElementById("nav-tabContent");
    
    function showWelcomeMessage(account) {
        cardDiv.style.display = "initial";
        welcomeDiv.innerHTML = `Welcome ${account.name}`;
        signInButton.hidden = true;
        signOutButton.hidden = false;
    }
    
    function clearTabs() {
        tabList.innerHTML = "";
        tabContent.innerHTML = "";
    }
    
    function updateUI(data, endpoint) {
    
        console.log(`Graph API responded at: ${new Date().toString()}`);
    
        if (endpoint === `${process.env.GRAPH_ENDPOINT_HOST}${process.env.GRAPH_ME_ENDPOINT}`) {
            setProfile(data);
        } else if (endpoint === `${process.env.GRAPH_ENDPOINT_HOST}${process.env.GRAPH_MAIL_ENDPOINT}`) {
            setMail(data);
        }
    }
    
    function setProfile(data) {
        profileDiv.innerHTML = ''
    
        const title = document.createElement('p');
        const email = document.createElement('p');
        const phone = document.createElement('p');
        const address = document.createElement('p');
    
        title.innerHTML = "<strong>Title: </strong>" + data.jobTitle;
        email.innerHTML = "<strong>Mail: </strong>" + data.mail;
        phone.innerHTML = "<strong>Phone: </strong>" + data.businessPhones[0];
        address.innerHTML = "<strong>Location: </strong>" + data.officeLocation;
    
        profileDiv.appendChild(title);
        profileDiv.appendChild(email);
        profileDiv.appendChild(phone);
        profileDiv.appendChild(address);
    }
    
    function setMail(data) {
        const mailInfo = data;
        if (mailInfo.value.length < 1) {
            alert("Your mailbox is empty!")
        } else {
            clearTabs();
            mailInfo.value.slice(0, 10).forEach((d, i) => {
                    createAndAppendListItem(d, i);
                    createAndAppendContentItem(d, i);
            });
        }
    }
    
    function createAndAppendListItem(d, i) {
        const listItem = document.createElement("a");
        listItem.setAttribute("class", "list-group-item list-group-item-action")
        listItem.setAttribute("id", "list" + i + "list")
        listItem.setAttribute("data-toggle", "list")
        listItem.setAttribute("href", "#list" + i)
        listItem.setAttribute("role", "tab")
        listItem.setAttribute("aria-controls", i)
        listItem.innerHTML = d.subject;
        tabList.appendChild(listItem);
    }
    
    function createAndAppendContentItem(d, i) {
        const contentItem = document.createElement("div");
        contentItem.setAttribute("class", "tab-pane fade")
        contentItem.setAttribute("id", "list" + i)
        contentItem.setAttribute("role", "tabpanel")
        contentItem.setAttribute("aria-labelledby", "list" + i + "list")
    
        if (d.from) {
            contentItem.innerHTML = "<strong> from: " + d.from.emailAddress.address + "</strong><br><br>" + d.bodyPreview + "...";
            tabContent.appendChild(contentItem);
        }
    }
    
  2. Slutligen skapar du en fil med namnet constants.js som kommer att lagra sträng konstanterna för att beskriva program händelser:

    const IPC_MESSAGES = {
        SHOW_WELCOME_MESSAGE: 'SHOW_WELCOME_MESSAGE',
        LOGIN: 'LOGIN',
        LOGOUT: 'LOGOUT',
        GET_PROFILE: 'GET_PROFILE',
        SET_PROFILE: 'SET_PROFILE',
        GET_MAIL: 'GET_MAIL',
        SET_MAIL: 'SET_MAIL'
    }
    
    module.exports = {
        IPC_MESSAGES: IPC_MESSAGES,
    }
    

Nu har du ett enkelt användar gränssnitt och interaktioner för din Electron-app. När du har slutfört resten av självstudien bör filen och mappstrukturen i projektet se ut ungefär så här:

ElectronDesktopApp/
├── App
│   ├── authProvider.js
│   ├── constants.js
│   ├── fetch.js
│   ├── main.js
│   ├── renderer.js
│   ├── index.html
├── package.json
└── .env

Lägg till autentiserings logik i din app

I app -mappen skapar du en fil med namnet AuthProvider.js. Detta innehåller en autentiseringsprovider klass som hanterar inloggnings-, utloggning-, token-förvärv, konto val och relaterade autentiserings aktiviteter med hjälp av MSAL-noden. Lägg till följande kod där:

const { PublicClientApplication, LogLevel, CryptoProvider } = require('@azure/msal-node');
const { protocol } = require('electron');
const path = require('path');
const url = require('url');

/**
 * To demonstrate best security practices, this Electron sample application makes use of
 * a custom file protocol instead of a regular web (https://) redirect URI in order to
 * handle the redirection step of the authorization flow, as suggested in the OAuth2.0 specification for Native Apps.
 */
const CUSTOM_FILE_PROTOCOL_NAME = process.env.REDIRECT_URI.split(':')[0]; // e.g. msal://redirect

/**
 * Configuration object to be passed to MSAL instance on creation.
 * For a full list of MSAL Node configuration parameters, visit:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
 */
const MSAL_CONFIG = {
    auth: {
        clientId: process.env.CLIENT_ID,
        authority: `${process.env.AAD_ENDPOINT_HOST}${process.env.TENANT_ID}`,
        redirectUri: process.env.REDIRECT_URI,
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: LogLevel.Verbose,
        }
    }
};

class AuthProvider {

    clientApplication;
    cryptoProvider;
    authCodeUrlParams;
    authCodeRequest;
    pkceCodes;
    account;

    constructor() {
        /**
         * Initialize a public client application. For more information, visit:
         * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/initialize-public-client-application.md
         */
        this.clientApplication = new PublicClientApplication(MSAL_CONFIG);
        this.account = null;

        // Initialize CryptoProvider instance
        this.cryptoProvider = new CryptoProvider();

        this.setRequestObjects();
    }

    /**
     * Initialize request objects used by this AuthModule.
     */
    setRequestObjects() {
        const requestScopes =  ['openid', 'profile', 'User.Read'];
        const redirectUri = process.env.REDIRECT_URI;

        this.authCodeUrlParams = {
            scopes: requestScopes,
            redirectUri: redirectUri
        };

        this.authCodeRequest = {
            scopes: requestScopes,
            redirectUri: redirectUri,
            code: null
        }

        this.pkceCodes = {
            challengeMethod: "S256", // Use SHA256 Algorithm
            verifier: "", // Generate a code verifier for the Auth Code Request first
            challenge: "" // Generate a code challenge from the previously generated code verifier
        };
    }

    async login(authWindow) {
        const authResult = await this.getTokenInteractive(authWindow, this.authCodeUrlParams);
        return this.handleResponse(authResult);
    }

    async logout() {
        if (this.account) {
            await this.clientApplication.getTokenCache().removeAccount(this.account);
            this.account = null;
        }
    }

    async getToken(authWindow, tokenRequest) {
        let authResponse;

        authResponse = await this.getTokenInteractive(authWindow, tokenRequest);

        return authResponse.accessToken || null;
    }

    // This method contains an implementation of access token acquisition in authorization code flow
    async getTokenInteractive(authWindow, tokenRequest) {

        /**
         * Proof Key for Code Exchange (PKCE) Setup
         *
         * MSAL enables PKCE in the Authorization Code Grant Flow by including the codeChallenge and codeChallengeMethod parameters
         * in the request passed into getAuthCodeUrl() API, as well as the codeVerifier parameter in the
         * second leg (acquireTokenByCode() API).
         *
         * MSAL Node provides PKCE Generation tools through the CryptoProvider class, which exposes
         * the generatePkceCodes() asynchronous API. As illustrated in the example below, the verifier
         * and challenge values should be generated previous to the authorization flow initiation.
         *
         * For details on PKCE code generation logic, consult the
         * PKCE specification https://tools.ietf.org/html/rfc7636#section-4
         */

        const {verifier, challenge} = await this.cryptoProvider.generatePkceCodes();

        this.pkceCodes.verifier = verifier;
        this.pkceCodes.challenge = challenge;

        const authCodeUrlParams = {
            ...this.authCodeUrlParams,
            scopes: tokenRequest.scopes,
            codeChallenge: this.pkceCodes.challenge, // PKCE Code Challenge
            codeChallengeMethod: this.pkceCodes.challengeMethod // PKCE Code Challenge Method
        };

        const authCodeUrl = await this.clientApplication.getAuthCodeUrl(authCodeUrlParams);

        protocol.registerFileProtocol(CUSTOM_FILE_PROTOCOL_NAME, (req, callback) => {
            const requestUrl = url.parse(req.url, true);
            callback(path.normalize(`${__dirname}/${requestUrl.path}`));
        });

        const authCode = await this.listenForAuthCode(authCodeUrl, authWindow);

        const authResponse = await this.clientApplication.acquireTokenByCode({
            ...this.authCodeRequest,
            scopes: tokenRequest.scopes,
            code: authCode,
            codeVerifier: this.pkceCodes.verifier // PKCE Code Verifier
        });

        return authResponse;
    }

    // Listen for authorization code response from Azure AD
    async listenForAuthCode(navigateUrl, authWindow) {

        authWindow.loadURL(navigateUrl);

        return new Promise((resolve, reject) => {
            authWindow.webContents.on('will-redirect', (event, responseUrl) => {
                try {
                    const parsedUrl = new URL(responseUrl);
                    const authCode = parsedUrl.searchParams.get('code');
                    resolve(authCode);
                } catch (err) {
                    reject(err);
                }
            });
        });
    }

    /**
     * Handles the response from a popup or redirect. If response is null, will check if we have any accounts and attempt to sign in.
     * @param response
     */
    async handleResponse(response) {
        if (response !== null) {
            this.account = response.account;
        } else {
            this.account = await this.getAccount();
        }

        return this.account;
    }

    /**
     * Calls getAllAccounts and determines the correct account to sign into, currently defaults to first account found in cache.
     * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
     */
    async getAccount() {
        const cache = this.clientApplication.getTokenCache();
        const currentAccounts = await cache.getAllAccounts();

        if (currentAccounts === null) {
            console.log('No accounts detected');
            return null;
        }

        if (currentAccounts.length > 1) {
            // Add choose account code here
            console.log('Multiple accounts detected, need to add choose account code.');
            return currentAccounts[0];
        } else if (currentAccounts.length === 1) {
            return currentAccounts[0];
        } else {
            return null;
        }
    }
}

module.exports = AuthProvider;

I kodfragmentet ovan initierade vi först MSAL-noden PublicClientApplication genom att skicka ett konfigurations objekt ( msalConfig ). Sedan exponeras login logout och getToken metoder som anropas av Main-modulen (main.js). I login och getToken , hämtar vi ID och åtkomsttoken, genom att först begära en auktoriseringskod och sedan utväxla detta med en token med hjälp av MSAL-nodens acquireTokenByCode offentliga API.

Lägga till en metod för att anropa ett webb-API

Skapa en annan fil med namnet fetch.js. Den här filen kommer att innehålla en Axios HTTP-klient för att göra REST-anrop till Microsoft Graph API.

const axios = require('axios');

/**
 * Makes an Authorization 'Bearer' request with the given accessToken to the given endpoint.
 * @param endpoint
 * @param accessToken
 */
async function callEndpointWithToken(endpoint, accessToken) {
    const options = {
        headers: {
            Authorization: `Bearer ${accessToken}`
        }
    };

    console.log('Request made at: ' + new Date().toString());

    const response = await axios.default.get(endpoint, options);

    return response.data;
}

module.exports = {
    callEndpointWithToken: callEndpointWithToken,
};

Lägg till registrerings information för appen

Skapa slutligen en miljö fil för att lagra registrerings informationen för appen som ska användas när du hämtar tokens. Det gör du genom att skapa en fil med namnet . kuvert i rotmappen i exemplet (ElectronDesktopApp) och lägga till följande kod:

# Credentials
CLIENT_ID=Enter_the_Application_Id_Here
TENANT_ID=Enter_the_Tenant_Id_Here

# Configuration
REDIRECT_URI=msal://redirect

# Endpoints
AAD_ENDPOINT_HOST=Enter_the_Cloud_Instance_Id_Here
GRAPH_ENDPOINT_HOST=Enter_the_Graph_Endpoint_Here

# RESOURCES
GRAPH_ME_ENDPOINT=v1.0/me
GRAPH_MAIL_ENDPOINT=v1.0/me/messages

# SCOPES
GRAPH_SCOPES=User.Read Mail.Read

Fyll i informationen med de värden som du får från Azure App Registration Portal:

  • Enter_the_Tenant_Id_here ska vara något av följande:
    • Om ditt program har stöd för konton i den här organisations katalogen ersätter du värdet med klient-ID eller klient namn. Till exempel contoso.microsoft.com.
    • Om ditt program har stöd för konton i en organisations katalog ersätter du värdet med organizations .
    • Om ditt program har stöd för konton i en organisations katalog och personliga Microsoft-konton ersätter du värdet med common .
    • Om du bara vill begränsa stödet till personliga Microsoft-konton ersätter du värdet med consumers .
  • Enter_the_Application_Id_Here: Program-ID: t (klient) för det program som du har registrerat.
  • Enter_the_Cloud_Instance_Id_Here: Azure Cloud-instansen där ditt program är registrerat.
    • För det huvudsakliga (eller globala) Azure-molnet anger du https://login.microsoftonline.com/ .
    • För nationella moln (till exempel Kina) kan du hitta lämpliga värden i nationella moln.
  • Enter_the_Graph_Endpoint_Here är instansen av det Microsoft Graph-API som programmet ska kommunicera med.
    • För den globala Microsoft Graph API-slutpunkten ersätter du båda instanserna av den här strängen med https://graph.microsoft.com/ .
    • För slut punkter i nationella moln distributioner, se nationella moln distributioner i Microsoft Graph-dokumentationen.

Testa appen

Du har slutfört skapandet av programmet och är nu redo att starta Electron Desktop-appen och testa appens funktioner.

  1. Starta appen genom att köra följande kommando inifrån roten i projektmappen:
electron App/main.js
  1. I programmets huvud fönster bör du se innehållet i index.html -filen och knappen Logga in .

Testa inloggning och logga ut

När du har läst in index.html -filen väljer du Logga in. Du uppmanas att logga in med Microsoft Identity Platform:

inloggnings varning

Om du godkänner de begärda behörigheterna visar webb programmen ditt användar namn, vilket indikerar en lyckad inloggning:

lyckad inloggning

Testa webb-API-anrop

När du har loggat in väljer du Se profil för att Visa användar profil informationen som returneras i svaret från anropet till Microsoft Graph-API:

profil information från Microsoft Graph

Välj Läs meddelanden om du vill visa meddelandena i användarens konto. Du kommer att visas på skärmen för godkännande:

godkännande skärmen för läsa. e-postbehörighet

Efter medgivande visas meddelanden som returneras i svaret från anropet till Microsoft Graph API:

e-postinformation från Microsoft Graph

Så här fungerar programmet

När en användare väljer knappen Logga in för första gången anropas get- getTokenInteractive metoden för AuthProvider.js . Den här metoden omdirigerar användaren att logga in med slut punkten för Microsoft Identity Platform och verifiera användarens autentiseringsuppgifter och hämtar sedan en auktoriseringskod. Den här koden utbyts sedan för en åtkomsttoken med hjälp acquireTokenByCode av ett offentligt API för MSAL-noden.

I det här fallet skickas en PKCE till den CORS-skyddade token-slutpunkten och utbyts för token. En ID-token, åtkomsttoken och uppdateringstoken tas emot av ditt program och bearbetas av MSAL-noden och informationen i tokens cachelagras.

ID-token innehåller grundläggande information om användaren, t. ex. visnings namnet. Åtkomsttoken har en begränsad livs längd och upphör att gälla efter 24 timmar. Om du planerar att använda dessa token för åtkomst till skyddad resurs måste Server dels servern verifiera den för att garantera att token har utfärdats till en giltig användare för ditt program.

Den Desktop-app som du har skapat i den här självstudien gör ett REST-anrop till Microsoft Graph API med hjälp av en åtkomsttoken som Bearer-token i begär ande huvudet (RFC 6750).

Microsoft Graph-API: t kräver att User. Read -omfånget läser en användar profil. Som standard läggs denna omfattning automatiskt till i varje program som är registrerat i Azure Portal. Andra API: er för Microsoft Graph, samt anpassade API: er för backend-servern, kan kräva ytterligare omfång. Till exempel kräver Microsoft Graph-API: t e-post. Read -omfånget för att kunna lista användarens e-post.

När du lägger till omfattningar kan användarna uppmanas att ange ytterligare medgivande för de tillagda omfattningarna.

Hjälp och support

Om du behöver hjälp, vill rapportera ett problem eller vill veta mer om dina support alternativ, se Hjälp och support för utvecklare.

Nästa steg

Om du vill gå djupare till Node.js och Electron Desktop Application Development på Microsoft Identity Platform kan du läsa vår scenario serie med flera delar: