I'm using an Auth service with OIDC to get user data after a user is logged in. I need that user data to determine what side-nav items a user will see, which is handled in a StepsService.
My current approach is creating an observable User that the StepService can reach out to, and use the data from that Observable to set all of the necessary StepService values. My files are as follows:
steps.service.ts
export class StepsService {
// Instantiate the initial variables that will act as state storage
sections: Observable<SectionModel[]>;
currentSection: Observable<SectionModel>;
stepsInCurrentSection: Observable<StepModel[]>;
currentStep: Observable<StepModel>;
currentStepIndex: BehaviorSubject<number> = new BehaviorSubject<number>(0);
totalStepsInCurrentSection: number;
totalSections: number;
currentSectionIndex: BehaviorSubject<number> = new BehaviorSubject<number>(0);
numberOfCompletedSteps: BehaviorSubject<number> = new BehaviorSubject<number>(0);
constructor(private authService: AuthService) {
// Set state variables based on initial steps available
this.sections = this.authService.getUser().pipe(
map((user) => {
console.log('user is: ', user);
const userRoles: string[] = user.profile['role'];
return this.getUserSpecificSteps(userRoles);
}),
tap((sections) => (this.totalSections = sections.length))
);
this.currentSection = combineLatest([this.sections, this.currentSectionIndex]).pipe(
map((data) => {
const sections = data[0];
const index = data[1];
return sections[index];
})
);
this.stepsInCurrentSection = this.currentSection.pipe(
map((sections) => sections.steps),
tap((steps) => (this.totalStepsInCurrentSection = steps.length))
);
this.currentStep = combineLatest([this.stepsInCurrentSection, this.currentStepIndex]).pipe(
map((data) => {
const steps = data[0];
const index = data[1];
return steps[index];
})
);
}
getUserSpecificSteps(userRoles: string[]) {
return userRoles?.includes('Admin') ? adminSections : userSections;
}
...
auth.service.ts
export class AuthService {
private config = environment;
private _isAuthenticated: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public readonly IsAuthenticated: Observable<boolean> = this._isAuthenticated.asObservable();
private _userManager: UserManager;
public _user: User | null;
public userSubject$: Observable<User>;
constructor() {
this._userManager = new UserManager(getClientSettings(this.config));
this._userManager
.getUser()
.then((user: any) => {
if (user && !user.expired) {
this._user = user;
this.userSubject$ = user;
this._isAuthenticated.next(this.isAuthenticated());
}
return user;
})
.catch((e) => console.log(e));
//fires after every user refresh
this._userManager.events.addUserLoaded((args) => {
this._userManager
.getUser()
.then((user) => {
this._user = user;
})
.catch((e) => console.log(e));
});
}
getUser() {
return this.userSubject$;
}
...
Currently this throws an error that this.authService.getUser() is undefined
and if I add a null check after the method call, it throws an error that I am passing undefined data to a data stream.
I do also have an APP_INITIALIZER
inside of my app.module
file that initializes the app with the auth service. I'm not sure if I'm using the observables correctly, or what the problem could be.
Thanks in advance!