Pass clientId dynamically from API response in app.module.ts

94 Views Asked by At

I am using MSAL library for Single sign on Authentication in Angular project.

Inside my app.module.ts file, I want to pass client_id parameter dynamically from API response. I created a config service where I am getting client_id in API response. But how to call that config service and pass client_id dynamically in app.module.ts file?

 auth: {
     clientId: <dynamic value from API response>,
     authority: <dynamic value from API response>,
     redirectUri: '/',
 },
1

There are 1 best solutions below

0
JSmart523 On

You need functionality that can initiate authorization attempts, and give other parts of your code a way to wait for an answer and to be notified if there is a change.

There are four common ways to do this.

Promises

While possible, promises return one response, not a stream that might return zero or many updates. So I recognize this as an alternative but I'm not going to go into how I would implement it because I wouldn't.

Signals

If you're using the most recent version of Angular and the rest of your team is comfortable with signals, then I'd go for that.

They are production-ready, as announced yesterday. https://blog.angular.io/signal-inputs-available-in-developer-preview-6a7ff1941823

rxjs

The way I'm going to explain is (literally) last week's best practice.

Let's say you had a service with a private method "getAuth" that would try to get existing/new authentication and return a promise or observable of an auth object.

You can then also include the following in your service

private authPromiseSubject$: Subject<ObservableInput<Auth>> = new Subject();
readonly auth$: Observable<undefined|Readonly<Auth>> = authPromiseSubject$.pipe(
  // if a new one is added while waiting for an old one to resolve, toss the old one
  switchAll(),
  shareReplay(1)
);
readonly clientId$ = this.auth$.pipe(
  map(auth => auth.clientId),
  distinctUntilChanged(),
  shareReplay(1)
);

ngOnInit() {
  this.reload();
}

// Example property
readonly isLoggedOff: Observable<boolean> = this auth$.pipe(
  map(auth => !auth?.clientId), // or whatever code makes sense
  distinctUntilChanged(),
  shareReplay(1)
);

// Example method, plus called onInit for initial loading
reload() {
  this.authPromiseSubject$.next(
    this.getAuth()
  );
}

// Example method
logout() {
  console.log("Log out server code goes here");
  console.log("Clearing local session storage, too");
  this.authPromiseSubject$.next(
    of(undefined)
  );
}

With a service like this, everything that uses it and needs the client id can instead pipe from the clientId$ observable. If you keep it in observables as long as possible and, instead of subscribing via Typescript, use the async pipe in the templates, then your components can be extremely reactive and fairly efficient.

NgRx (or some other state engine)

NgRx is a code library that takes the example I just gave you and extends it. Not every Angular developer I know knows NgRx, but I don't know any Angular developers that know NgRx and don't regret learning it sooner and might argue that this method is the best practice. Take that how you will.