angularcanactivate

Service Injection in CanActivateFn - Angular 16.2


I'm trying to use the value I collect from the Authorization Header in an authGuard.

The Service that manages the login successfully logs the value I expect.

import { HttpClient, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, tap } from 'rxjs';
import { environment } from 'src/environments/environment';

@Injectable()
export class AuthenticateService {
  private base = '/authenticate';

  authorizationHeader: string | null = null;

  constructor(private http: HttpClient) { }

  login(loginRequest: any): Observable<any> {
    return this.http.post(environment.APIEndpoint + this.base + '/login', loginRequest, { observe: 'response' })
      .pipe(
        tap((response: HttpResponse<any>) => {
          this.authorizationHeader = response.headers.get('Authorization');
          console.log('Service' + this.authorizationHeader);
        })
      );
  }

  

  getAuthorizationHeader(): string | null {
    return this.authorizationHeader;
  }
}

But trying to access authorizationHeader from the authGuard function like this:

import { CanActivateFn, Router } from '@angular/router';
import { AuthenticateService } from '../authenticate/authenticate.service';
import { inject } from '@angular/core';

export const authGuard: CanActivateFn = (route, state) => {

  const authorization = inject(AuthenticateService).getAuthorizationHeader();
  console.log('authGuard - ', authorization);

  const router = inject(Router) 

  switch (authorization) {
    case 'ADMIN':
      return true;
    case 'USER':
      router.navigate(['/not-found']);
      return false;
    case null:
    default:
      router.navigate(['/authenticate']);
      return false;
  }
};

will result in authorization being null. As far as I know, that is because inject() creates a new instance of the class.

I tried setting authorizationHeader as signal(), like I saw on this post:

  authorizationHeader = signal('');


  login(loginRequest: any): Observable<any> {
    return this.http.post(environment.APIEndpoint + this.base + '/login', loginRequest, { observe: 'response' })
      .pipe(
        tap((response: HttpResponse<any>) => {
          const authorizationHeader = response.headers.get('Authorization');
          if (authorizationHeader !== null) {
            this.authorizationHeader.set(authorizationHeader);
            console.log('Service - ')
          }
        })
      );
  }

hence retrieving the variable from authGuard like this:

  const authorization = inject(AuthenticateService).authorizationHeader();

but it didn't work.


Solution

  • It's very likely you added AuthenticateService to providers more than once, which will create multiple instances.

    I would recommend you let Angular do the providing for you, add providedIn

    @Injectable({ providedIn: 'root' }) // <-- Add this 
    export class AuthenticateService {
    

    This way you should have only one instance.

    Don't forget to remove AuthenticateService from all providers list.