Ok, you see here we are taking the user idToken as Observable.
I am not sure is it a
stream
or signal
but for reactive programming it's very critical
Stream — A stream of discrete events. Also known in other FRP systems as an event, like keyboard input events, WebSocket events, login/logout events etc. (in rxjs
— Subject, Observable)
Signal — A container/store for a value that changes over time. Some FRP systems call it a behavior, property, cell or signal, like isLogin state, key press state, current user state etc. (in rxjs
— BehaviorSubject, ReplaySubject(1), shareReplay(1)).
For example in Angular components @Output
is a stream, and @Input
is a signal
Signal and Stream are the ones of most important concepts of functional reactive programming (FRP) (rxjs is also a FRP library and it is not unique). This distinction is more important then hot and cold observables, but nobody talks about it in most tutorials and rxjs with it's "Observable
for everything" is simply a mashup of those two concepts which is why it is very difficult to understand.
And in other implementations of FRP, signal and stream can have a pending state, where we explicitly await for the new value. In rxjs we don't have such state, so we need to explicitly indicate it, like adding isLoading: true
to the object or use undefined
/no value for this. But in this example I think we can omit it.
So here I think we should treat afAuth.idToken
as a signal with idToken
state that we need to convert to a stream, mix it's value into interceptor and we can do it like this:
- converting the
signal
topromise
withlastValueFrom
and becausesignal
is never ending observable we should take only 1 value withtake(1)
- converting the
signal
topromise
withfirstValueFrom
. It should work like this, but in some cases it could return the value before current value. - just use
signal
itself.Here we a taking/waiting current value and then switch to tokenHandler streamintercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return this.afAuth.idToken.pipe(switchMap((res) => tokenHandler(req, next, res))); }
And I can see now that HttpInterceptor
do not support Promise
s anymore, so the first two options are excluded.
So in the end it should look like this
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(public router: Router, private afAuth: AngularFireAuth) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.afAuth.idToken.pipe(switchMap((res) => tokenHandler(req, next, res)));
}
}
const tokenHandler = (
request: HttpRequest<any>,
next: HttpHandler,
currentUser: string | null,
): Observable<HttpEvent<any>> => {
return next.handle(request.clone({ setHeaders: { Authorization: `Bearer ${currentUser}` } }));
};