Search code examples
angularspring-bootjwtfrontendrequest-headers

Authorization header with JWT in Angular to all requests


I am writing my first project to try my skills in Spring Boot and Angular. I have three endpoints: /register - this endpoint can access every person /login - this endpoint can access every person, response - token and token exp.time /users - this endpoint can access only authorized persons, who have valid JWT token, that they got as a response after successful login, response - List

My backend generates JWT token for authorization, I need to store it in local storage and add header Authorization: Bearer TOKEN, to all my requests, after login, to access endpoints that require authorization

My backend works great, i tested it several times with postman, and everything works as I wanted.

The problem is with my fronted. It receives my JWT token and stores it in localstorage, but my requests don't have Authorization header with token.

I tried many ways that I found on the internet but nothing helped.

request

token in local storage

checked token from local storage with postman

LoginComponent

import { Component } from '@angular/core';
import { CardModule } from 'primeng/card';
import { InputTextModule } from 'primeng/inputtext';
import { FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { FormBuilder } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';  
import { ImageModule } from 'primeng/image';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
import { UserLoginDto } from '../../interfaces/userLoginDto';
import { UserService } from '../../services/user.service';
import { LoginResponse } from '../../interfaces/loginResponse';

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [
    CardModule,
    InputTextModule,
    ReactiveFormsModule,
    ButtonModule,
    RouterModule,
    CommonModule,
    ImageModule
  ],
  templateUrl: './login.component.html',
  styleUrl: './login.component.css',
})
export class LoginComponent {
  loginForm!: FormGroup;

  constructor(
    private fb: FormBuilder,
    private authService: AuthService,
    private router: Router,
    private userService: UserService
  ){}



  ngOnInit(): void {
    this.loginForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required]
    });
  }

  get email() {
    return this.loginForm.controls['email'];
  }
  get password() { return this.loginForm.controls['password']; }

  onSubmit(){
    if(this.loginForm.valid){
      const userLoginDto: UserLoginDto = {
        email: this.loginForm.value.email,
        password: this.loginForm.value.password
      };
      this.userService.loginUser(userLoginDto).subscribe({
        next: (response: LoginResponse) => {  
          console.log('User logged in successfully', response);
          const token = response.token;  
          this.authService.setAuthToken(token);
          this.router.navigate(['/home']);
        },
        error: (err) => console.error('Error registering user', err)
      });
    }
  }
}

UserService

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../interfaces/user';
import { UserRegisterDto } from '../interfaces/userRegisterDto';
import { UserLoginDto } from '../interfaces/userLoginDto';
import { LoginResponse } from '../interfaces/loginResponse';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private apiServerUrl  = 'http://localhost:8080';

  constructor(private http: HttpClient) { }

  public getAllUsers(): Observable<User[]>{
    return this.http.get<User[]>(`${this.apiServerUrl}/users`);
  }

  public registerUser(user: UserRegisterDto): Observable<UserRegisterDto>{
    return this.http.post<UserRegisterDto>(`${this.apiServerUrl}/register`, user);
  }

  public loginUser(user: UserLoginDto): Observable<LoginResponse>{
    return this.http.post<LoginResponse>(`${this.apiServerUrl}/login`, user);
  }
}

AuthService

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private tokenKey: string = 'token';

  getAuthToken(): string | null{
    const token =  window.localStorage.getItem(this.tokenKey);
    console.log("Retrieved token:", token);
    return token;
  }

  setAuthToken(token: string | null): void{
    if(token !== null){
      window.localStorage.setItem(this.tokenKey, token);
    } else{
      window.localStorage.removeItem(this.tokenKey)
    }
  }
}

AuthInterceptor

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';

import { Observable } from 'rxjs';
import { AuthService } from './services/auth.service';


@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(
    private authService: AuthService,
  ){}

  intercept(req: HttpRequest<any>, next: HttpHandler){
    const authToken = this.authService.getAuthToken();
    const authReq = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${authToken}`)
    });

    return next.handle(authReq);
  }
}

main.ts

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app/app.routes';
import { provideRouter } from '@angular/router';
import { AuthInterceptor } from './app/auth-interceptor';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
  ]
})
  .catch((err) => console.error(err));

I am Angular 18, standalone project

I will be glad for any help, thank you all

I tried many methods to solve this, but I have same result. On one of the forums I read that it is necessary to add **AuthInterceptor ** and add it to main.ts, also I tried this version of **AuthInterceptor **: How to add Authorization Header to Angular http request?


Solution

  • Your interceptor is "old-style". There is a way to get it working through some configuration option that I don't remember at the moment - but the easiest is to generate new-style (functional) interceptor. It will look something like this:

    export const authInterceptor: HttpInterceptorFn = (req, next) => {
      const authService = inject(AuthService);
      const router = inject(Router);
      const authToken = authService.getToken();
    
      if (authToken) {
        req = req.clone({
          setHeaders: {
            Authorization: `Bearer ${authToken}`
          }
        });
      }
      return next(req).pipe(
        catchError((error) => {
          if (error instanceof HttpErrorResponse && error.status === 401) {
            router.navigate(['login']);
          }
          return throwError(() => error);
        })
      );
    };
    

    Additionally, you need to update app.config.ts:

    export const appConfig: ApplicationConfig = {
      providers: [provideRouter(routes), 
        provideHttpClient(withInterceptors([authInterceptor]))
      ]
    };
    

    See more in the docs.