0

I try to find the easiest way to create a angular app, where a user has to authenticate with Microsoft including xboxlive consent so I can also get xboxlive data of a user.

I already implemented the logic in a java app with following flow:

  1. Get OAuth2 Token from https://login.live.com/oauth20_authorize.srf
  2. Get Refresh & Access Token from https://login.live.com/oauth20_token.srf

So in my test angular app, I found the msal lib that I'm using now.

  • NPM: 7.22.0
  • Node: v20.5.1
  • msal-angular: 2.5.10
  • msal-browser: 2.38.0

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS, HttpClientModule, HttpInterceptor } from '@angular/common/http';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
import { MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalBroadcastService, MsalGuard, MsalGuardConfiguration, MsalInterceptor, MsalInterceptorConfiguration, MsalRedirectComponent, MsalService, ProtectedResourceScopes } from '@azure/msal-angular';
import { IPublicClientApplication, InteractionType, LogLevel, PublicClientApplication } from '@azure/msal-browser';
import { environment } from 'src/environments/environment';

@NgModule({
    declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
    ],
    imports: [
        BrowserModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        HttpClientModule
    ],
    providers: [
        {
            provide: MSAL_INSTANCE,
            useFactory: MSALInstanceFactory
        },
        {
            provide: MSAL_GUARD_CONFIG,
            useFactory: MsalGuardConfigFactory
        },
        {
            provide: HTTP_INTERCEPTORS,
            useClass: MsalInterceptor,
            multi: true
        },
        {
            provide: MSAL_INTERCEPTOR_CONFIG,
            useFactory: MsalInterceptorConfigFactory
        },
        MsalService,
        MsalBroadcastService,
        MsalGuard,
        AppComponent
    ],
    bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule { }


function MSALInstanceFactory():IPublicClientApplication{
    return new PublicClientApplication({
        auth:{
            clientId: environment.auth.clientId,
            redirectUri: environment.auth.redirectUri,
        },
        system:{
            loggerOptions:{
                loggerCallback: (level,message,conatinsPii) =>{
                    console.log(message);
                },
                logLevel: LogLevel.Verbose
            }
        }
    })
}

function MsalGuardConfigFactory(): MsalGuardConfiguration{
    return {
        interactionType: InteractionType.Redirect,
        authRequest: {
            scopes:["XboxLive.signin", "Xboxlive.offline_access"],
        }
    }
}

function MsalInterceptorConfigFactory(): MsalInterceptorConfiguration{
    const myProtectedResourcesMap = new Map<string, Array<string | ProtectedResourceScopes > | null>();
    myProtectedResourcesMap.set('http://localhost:8082/userDetails',["XboxLive.signin", "Xboxlive.offline_access"])
    return {
        interactionType: InteractionType.Redirect,
        protectedResourceMap: myProtectedResourcesMap,
        authRequest: (msalService, httpReq, originalAuthRequest)=>{
            return {
                ...originalAuthRequest
            }
        }
    }
}

app.component.ts

import { HttpClient } from '@angular/common/http';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { AuthenticationResult, EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import {filter, take, takeUntil } from "rxjs/operators";

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy{
    title = 'SSO-Login';
    activeUser: string | undefined = "unknown user";
    isAuthenticated = false
    logoUrl = "../assets/logo.jpeg"

    private unsubscribe = new Subject<void>();

    constructor(private msalService: MsalService,
                private msalBrodcastService: MsalBroadcastService,
                private router: Router,
                private httpClient: HttpClient){}

    login(): void {
        // this.msalService.instance.loginPopup({
        //     scopes:["User.Read"]
        // });
        this.msalService.instance.loginRedirect(
            {scopes:['XboxLive.signin offline_access']}
        )
        console.log(sessionStorage)
    }

    logout(): void{
        this.msalService.instance.logoutPopup(
            {mainWindowRedirectUri: "/"}
        );
        this.router.navigate(['']);
    }
    ngOnInit(): void {
        this.msalBrodcastService.inProgress$.pipe(
            filter((status: InteractionStatus) => status === InteractionStatus.None),
            takeUntil(this.unsubscribe)
        ).subscribe(()=>{
            this.setAuthenticationStatus();
        })

        this.msalBrodcastService.msalSubject$
        .pipe(
            filter((message: EventMessage) => message.eventType === EventType.LOGIN_SUCCESS),
            takeUntil(this.unsubscribe)
        ).subscribe((message: EventMessage) =>{
            const authResult = message.payload as AuthenticationResult;
            this.msalService.instance.setActiveAccount(authResult.account);
        })
    }

    ngOnDestroy(): void {
        this.unsubscribe.next(undefined);
        this.unsubscribe.complete();
    }

    setAuthenticationStatus(): void{
        let activeAccount = this.msalService.instance.getActiveAccount();

        if(!activeAccount && this.msalService.instance.getAllAccounts().length > 0){
            activeAccount = this.msalService.instance.getAllAccounts()[0];
            this.msalService.instance.setActiveAccount(activeAccount);
        }
        this.isAuthenticated = !!activeAccount;
        this.activeUser = activeAccount?.username;
    }
}

Response after the login:

The provided value for the input parameter 'scope' is not valid. The scope 'XboxLive.signin Xboxlive.offline_access openid profile offline_access' is not configured for this tenant.

I found that msal is calling: https://login.microsoftonline.com/common/oauth2/v2.0/token where it fails. Since the endpoint is wrong, the process fails.

I couldn't find any solution for this problem anywhere. Is it even possible to add xbox consent with the msal lib?

I also couldn't find anyone who has already implemented this.

I expected that the msal lib could be easily adjusted to add xboxlive consent.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

0 Answers0