Mihai Marinescu
Mihai Marinescu's Blog

Mihai Marinescu's Blog

Angular Universal: send session token to proxy

Angular Universal: send session token to proxy

Mihai Marinescu's photo
Mihai Marinescu
·Oct 9, 2021·

2 min read

Your angular universal app might use the server just to save some data in the session when a user logs in and then pass all the requests to another server where the actual db is read and so on.

  app.use('/api', createProxyMiddleware({target: process.env.API_URL}));

In my app, when the user logs in, I'm using a controller on my login route on the server, where I'm authenticating the user and then get the user data;

  app.post('/api/login', loginController);

In this controller, after authenticating the user, I'm attaching the auth_token to req.session.token and I'm using this session token in a middleware that attaches it as a bearer on all of the server requests.

export function bearerMiddleware(req: any, res: any, next: Function) {
  // @ts-ignore
  req.headers['Authorization'] = `Bearer ${req.session.bearer}`;
  next();
}

But, when I'm making calls from components directly to the express server(user refreshing a page), for example, making an http.get('/api/some-url'), the call that is sent to the proxy doesn't have the req.session.token on it, thus, it has no authorization header, because the HttpClient in Angular strips header cookies by default.

One possible solution to overcome this is found in this github issue but there is also another way of doing things.

  • Provide the token in your server.ts file; see this tutorial on providing data from the server to the client via transfer state
app.get('*', (req, res) => {

res.render(indexHtml, {
   req,
   providers: [
     {provide: APP_BASE_HREF, useValue: req.baseUrl},
     {provide: AUTH_TOKEN, useValue: req.session.token}
   ]
 });
});
  • create an HTTP_INTERCEPTOR that takes this token from the transferState when the call is made on the server and attaches it to the headers of the http call
import {Injectable, PLATFORM_ID, Injector} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from 
'@angular/common/http';
import {Observable} from 'rxjs';
import {TransferStateService} from '@app/services/transfer.state.service';
import {isPlatformServer} from '@angular/common';
import {AUTH_TOKEN} from '@src/tokens';
import {APP_BASE_URL_KEY} from '@src/dictionary/transfer-state.dictionary';

@Injectable({
  providedIn: 'root'
})

export class APIInterceptor implements HttpInterceptor {

  constructor(private transferStateService: TransferStateService, private injector: 
 Injector) {}

  private platformId = this.injector.get(PLATFORM_ID);
  private isServer = isPlatformServer(this.platformId);

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
    const baseUrl: any = 'http://localhost:4200'; // you need 'http://' urls to work on the server
    let token: any = '';
    let apiReq: any;

    if (this.isServer) {
      token = this.injector.get(AUTH_TOKEN) || '';
      this.transferStateService.set(APP_BASE_URL_KEY, baseUrl);

      apiReq = req.clone({
        url    : `${baseUrl}/${req.url}`,
        headers: req.headers.set('Authorization', `Bearer ${token}`),
      });
    } else {
      apiReq = req.clone({url    : `${baseUrl}/${req.url}`,}) // or just `/${req.url}`
    }

  return next.handle(apiReq);
 }
}

And now, when you refresh the page and the http call is made on the server will have the bearer token attached to it

 
Share this