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:
- Get OAuth2 Token from https://login.live.com/oauth20_authorize.srf
- 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.