<commit_before>import * as Msal from '@azure/msal-browser';

interface AccessTokenRequestOptions {
    scopes: string[];
    returnUrl: string;
}

interface AccessTokenResult {
    status: AccessTokenResultStatus;
    token?: AccessToken;
}

interface AccessToken {
    value: string;
    expires: Date | null;
    grantedScopes: string[];
}

enum AccessTokenResultStatus {
    Success = "success",
    RequiresRedirect = "requiresRedirect"
}

enum AuthenticationResultStatus {
    Redirect = "redirect",
    Success = "success",
    Failure = "failure",
    OperationCompleted = "operationCompleted"
}

interface AuthenticationResult {
    status: AuthenticationResultStatus;
    state?: any;
    message?: string;
}

interface AuthorizeService {
    getUser(): Promise<object | undefined>;
    getAccessToken(request?: AccessTokenRequestOptions): Promise<AccessTokenResult>;
    signIn(state: any): Promise<AuthenticationResult>;
    completeSignIn(state: any): Promise<AuthenticationResult>;
    signOut(state: any): Promise<AuthenticationResult>;
    completeSignOut(url: string): Promise<AuthenticationResult>;
}

interface AuthorizeServiceConfiguration extends Msal.Configuration {
    defaultAccessTokenScopes: string[];
    additionalScopesToConsent: string[];
    loginMode: string;
}

class MsalAuthorizeService implements AuthorizeService {
    private readonly _msalApplication: Msal.PublicClientApplication;
    private _account: Msal.AccountInfo | undefined | null;
    private _redirectCallback: Promise<AuthenticationResult | null> | undefined;
    private _requestedScopes: string[] | undefined;

    constructor(private readonly _settings: AuthorizeServiceConfiguration) {
        if (this._settings.auth?.knownAuthorities?.length == 0) {
            this._settings.auth.knownAuthorities = [new URL(this._settings.auth.authority!).hostname]
        }
        this._msalApplication = new Msal.PublicClientApplication(this._settings);
    }

    getAccount() {
        if (this._account) {
            return this._account;
        }

        const accounts = this._msalApplication.getAllAccounts();
        if (accounts && accounts.length) {
            return accounts[0];
        }

        return null;
    }

    async getUser() {
        const account = this.getAccount();
        if (!account) {
            return;
        }

        const scopes: string[] = [];
        if (this._settings.defaultAccessTokenScopes && this._settings.defaultAccessTokenScopes.length > 0) {
            scopes.push(...this._settings.defaultAccessTokenScopes)
        }

        if (this._settings.additionalScopesToConsent && this._settings.additionalScopesToConsent.length > 0) {
            scopes.push(...this._settings.additionalScopesToConsent);
        }

        if (this._requestedScopes && this._requestedScopes.length > 0) {
            scopes.push(...this._requestedScopes);
        }

        const silentRequest = {
            redirectUri: this._settings.auth?.redirectUri,
            account: account,
            scopes: scopes
        };

        try {
            const response = await this._msalApplication.acquireTokenSilent(silentRequest);
            return response.idTokenClaims;
        } catch (e) {
            await this.signInCore(silentRequest);
        }
    }

    async getAccessToken(request?: AccessTokenRequestOptions): Promise<AccessTokenResult> {
        try {
            const newToken = await this.getTokenCore(request?.scopes);
            return {
                status: AccessTokenResultStatus.Success,
                token: newToken
            };
        } catch (e) {
            return {
                status: AccessTokenResultStatus.RequiresRedirect
            };
        }
    }

    async getTokenCore(scopes?: string[]): Promise<AccessToken | undefined> {
        const account = this.getAccount();
        if (!account) {
            return;
        }

        this._requestedScopes = scopes;
        const silentRequest = {
            redirectUri: this._settings.auth?.redirectUri,
            account: account,
            scopes: scopes || this._settings.defaultAccessTokenScopes
        };

        const response = await this._msalApplication.acquireTokenSilent(silentRequest);
        return {
            value: response.accessToken,
            grantedScopes: response.scopes,
            expires: response.expiresOn
        };
    }

    async signIn(state: any) {
        try {
            // Before we start any sign-in flow, clear out any previous state so that it doesn't pile up.
            this.purgeState();

            const request: Msal.AuthorizationUrlRequest = {
                redirectUri: this._settings.auth?.redirectUri,
                state: await this.saveState(state),
                scopes: []
            };

            if (this._settings.defaultAccessTokenScopes && this._settings.defaultAccessTokenScopes.length > 0) {
                request.scopes = this._settings.defaultAccessTokenScopes;
            }<commit_msg>Fix null-ref for MSAL WASM Auth (#35872)<commit_after>import * as Msal from '@azure/msal-browser';

interface AccessTokenRequestOptions {
    scopes: string[];
    returnUrl: string;
}

interface AccessTokenResult {
    status: AccessTokenResultStatus;
    token?: AccessToken;
}

interface AccessToken {
    value: string;
    expires: Date | null;
    grantedScopes: string[];
}

enum AccessTokenResultStatus {
    Success = "success",
    RequiresRedirect = "requiresRedirect"
}

enum AuthenticationResultStatus {
    Redirect = "redirect",
    Success = "success",
    Failure = "failure",
    OperationCompleted = "operationCompleted"
}

interface AuthenticationResult {
    status: AuthenticationResultStatus;
    state?: any;
    message?: string;
}

interface AuthorizeService {
    getUser(): Promise<object | undefined>;
    getAccessToken(request?: AccessTokenRequestOptions): Promise<AccessTokenResult>;
    signIn(state: any): Promise<AuthenticationResult>;
    completeSignIn(state: any): Promise<AuthenticationResult>;
    signOut(state: any): Promise<AuthenticationResult>;
    completeSignOut(url: string): Promise<AuthenticationResult>;
}

interface AuthorizeServiceConfiguration extends Msal.Configuration {
    defaultAccessTokenScopes: string[];
    additionalScopesToConsent: string[];
    loginMode: string;
}

class MsalAuthorizeService implements AuthorizeService {
    private readonly _msalApplication: Msal.PublicClientApplication;
    private _account: Msal.AccountInfo | undefined | null;
    private _redirectCallback: Promise<AuthenticationResult | null> | undefined;
    private _requestedScopes: string[] | undefined;

    constructor(private readonly _settings: AuthorizeServiceConfiguration) {
        if (this._settings.auth?.knownAuthorities?.length == 0) {
            this._settings.auth.knownAuthorities = [new URL(this._settings.auth.authority!).hostname]
        }
        this._msalApplication = new Msal.PublicClientApplication(this._settings);
    }

    getAccount() {
        if (this._account) {
            return this._account;
        }

        const accounts = this._msalApplication.getAllAccounts();
        if (accounts && accounts.length) {
            return accounts[0];
        }

        return null;
    }

    async getUser() {
        const account = this.getAccount();
        if (!account) {
            return;
        }

        const scopes: string[] = [];
        if (this._settings.defaultAccessTokenScopes && this._settings.defaultAccessTokenScopes.length > 0) {
            scopes.push(...this._settings.defaultAccessTokenScopes)
        }

        if (this._settings.additionalScopesToConsent && this._settings.additionalScopesToConsent.length > 0) {
            scopes.push(...this._settings.additionalScopesToConsent);
        }

        if (this._requestedScopes && this._requestedScopes.length > 0) {
            scopes.push(...this._requestedScopes);
        }

        const silentRequest = {
            redirectUri: this._settings.auth?.redirectUri,
            account: account,
            scopes: scopes
        };

        try {
            const response = await this._msalApplication.acquireTokenSilent(silentRequest);
            return response.idTokenClaims;
        } catch (e) {
            await this.signInCore(silentRequest);
        }
    }

    async getAccessToken(request?: AccessTokenRequestOptions): Promise<AccessTokenResult> {
        try {
            const newToken = await this.getTokenCore(request?.scopes);
            return {
                status: AccessTokenResultStatus.Success,
                token: newToken
            };
        } catch (e) {
            return {
                status: AccessTokenResultStatus.RequiresRedirect
            };
        }
    }

    async getTokenCore(scopes?: string[]): Promise<AccessToken | undefined> {
        const account = this.getAccount();
        if (!account) {
            throw new Error('Failed to retrieve token, no account found.');
        }

        this._requestedScopes = scopes;
        const silentRequest = {
            redirectUri: this._settings.auth?.redirectUri,
            account: account,
            scopes: scopes || this._settings.defaultAccessTokenScopes
        };

        const response = await this._msalApplication.acquireTokenSilent(silentRequest);
        return {
            value: response.accessToken,
            grantedScopes: response.scopes,
            expires: response.expiresOn
        };
    }

    async signIn(state: any) {
        try {
            // Before we start any sign-in flow, clear out any previous state so that it doesn't pile up.
            this.purgeState();

            const request: Msal.AuthorizationUrlRequest = {
                redirectUri: this._settings.auth?.redirectUri,
                state: await this.saveState(state),
                scopes: []
            };

            if (this._settings.defaultAccessTokenScopes && this._settings.defaultAccessTokenScopes.length > 0) {
                request.scopes = this._settings.defaultAccessTokenScopes;
            }
{'fraction_non_alphanumeric': 0.09440110595437938, 'fraction_numerical': 0.0016786807544188801, 'mean_word_length': 2.508139937651541, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5531423', 'n_tokens_mistral': 2642, 'n_tokens_neox': 2579, 'n_words': 542}