Skip to content

Instantly share code, notes, and snippets.

@SonyStone
Last active January 16, 2023 22:05
Show Gist options
  • Save SonyStone/1ae11c24cfa2ab10599bd89db112dd0b to your computer and use it in GitHub Desktop.
Save SonyStone/1ae11c24cfa2ab10599bd89db112dd0b to your computer and use it in GitHub Desktop.

image Ok, you see here we are taking the user idToken as Observable. I am not shore 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 to promise with lastValueFrom and because signal is never ending observable we should take only 1 value with take(1)
  • converting the signal to promise with firstValueFrom. It should work like this, but in some cases it could return the value before current value.
  • just use signal itself.
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      return this.afAuth.idToken.pipe(switchMap((res) => tokenHandler(req, next, res)));
    }
    Here we a taking/waiting current value and then switch to tokenHandler stream

And I can see now that HttpInterceptor do not support Promises 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}` } }));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment