Is using http://localhost as redirect URI safe for SSO authentication with MSAL in electron app?

97 views Asked by At

I have created a electron app and need to integrate O365 login in it,every blog or video I see everyone is referring to redirect URI as http://localhost but to me it seems a concern using this in production as any malicious app running in desktop can fetch the token from this endpoint in between

authconfig.js


const { PublicClientApplication, InteractionRequiredAuthError } = require('@azure/msal-node');
const { NativeBrokerPlugin } = require("@azure/msal-node-extensions");
const { shell} = require('electron');
const {promises} = require('fs');
class AuthProvider {
    msalConfig
    clientApplication;
    account;
    cache;
    tokenResponseFromCache;
    constructor(msalConfig) {
        this.msalConfig = {
            auth:{
                clientId:"84XXXXXXXXXXXXXXX",
                authority:"https://login.microsoftonline.com/876XXXXXXXXXXXXXX/",
            },
            broker: {        nativeBrokerPlugin: new NativeBrokerPlugin()    },
            system:{
                loggerOpitons:{
                    loggerCallback(logLevel,message,containspii){
                    },
                    piiLoggingEnabled:false,
                    logLevel:3,
                }
            }
        };
        /**
         * Initialize a public client application. 
         */
        this.msalConfig = msalConfig;
        this.clientApplication = new PublicClientApplication(this.msalConfig);
        this.cache = this.clientApplication.getTokenCache();
        this.account = null;
    }
    async login() {
            try{
                const openBrowser = async(url)=>{
                    await shell.openExternal(url);
                };
                const successTemplate = await promises.readFile("./redirect.html","utf-8");
                const authResponse = await this.clientApplication.acquireTokenInteractive({
                    openBrowser,
                    successTemplate:successTemplate,
                    failureTemplate:"<h1>Oops!Something went Wrong</h1><p>Close the window and try again</p>",
                });
                console.log(authResponse);
                this.account = authResponse.account;
            }catch(error){
                console.log(error);
            }
        }
    async logout() {
        if (!this.account) return;

        try {
            /**
             * If you would like to end the session with AAD, use the logout endpoint. You'll need to enable
             * the optional token claim 'login_hint' for this to work as expected.
             */
            if (this.account.idTokenClaims.hasOwnProperty('login_hint')) {
                await shell.openExternal(`${this.msalConfig.auth.authority}/oauth2/v2.0/logout?logout_hint=${encodeURIComponent(this.account.idTokenClaims.login_hint)}`);
            }

            await this.cache.removeAccount(this.account);
            this.account = null;
        } catch (error) {
            console.log(error);
        }
    }

    async getToken(tokenRequest) {
        let authResponse;
        const account = this.account || (await this.getAccount());
        if(this.tokenResponseFromCache){
         authResponse = this.tokenResponseFromCache;   
        }
        else{
            if (account) {
                tokenRequest.account = account;
                authResponse = await this.getTokenSilent(tokenRequest);
            } else {
                authResponse = await this.getTokenInteractive(tokenRequest);
            }
        }
       

        return authResponse || null;
    }

    async getTokenSilent(tokenRequest) {
        try {
            return await this.clientApplication.acquireTokenSilent(tokenRequest);
        } catch (error) {
            if (error instanceof InteractionRequiredAuthError) {
                console.log('Silent token acquisition failed, acquiring token interactive');
                return await this.getTokenInteractive(tokenRequest);
            }

        }
    }

    async getTokenInteractive(tokenRequest) {
        try {
            const openBrowser = async (url) => {
                await shell.openExternal(url);
            };

            const authResponse = await this.clientApplication.acquireTokenInteractive({
                ...tokenRequest,
                openBrowser,
                successTemplate: '<h1>Successfully signed in!</h1> <p>You can close this window now.</p>',
                errorTemplate: '<h1>Oops! Something went wrong</h1> <p>Check the console for more information.</p>',
            });

            return authResponse;
        } catch (error) {
            throw error;
        }
    }

    /**
     * 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.
     */
    async getAccount() {
        const currentAccounts = await this.cache.getAllAccounts();

        if (!currentAccounts) {
            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;

in the above code I tried passing redirect URI like-

`this.msalConfig = {
            auth:{
                clientId:"841XXXXXXX",
                authority:"https://login.microsoftonline.com/875XXXXXXXXXXXXXX/",
                redirectUri : "http://localhost/authcallback" (also tried "https://appname/authcallback"
               }
        }`

and registered the tried URL in Azure app as well

but this doesn't work by default still http://localhost is only getting passed with the request as redirect URI so when I login I get this error

[enter image description here]

if anyone could guide me on what wrong am I doing here or is my understanding wrong, it would be really helpful, I referred to all other posts on stack overflow but nothing worked for me yet.

Thanks in advance

0

There are 0 answers