token not send to the server in angular WWW-Authenticate: Bearer and get 401 error

1.5k views Asked by At

I'm new in angular and I've created a small role-based app using Angular 9 for frontend and Asp.net core. when I log in/logout to the app is working correctly without any issue and I can access the non-Authorize controllers from frontend but when I want to access an Authorize controller ( "company" ) I get 401 error. enter image description here

enter image description here when I check the "Network" tab in my browser I can see that "WWW-Authenticate: Bearer"

enter image description here and I checked in Microsoft Docs then I find

Notice that the response includes a Www-Authenticate header with the challenge set to Bearer. That indicates the server expects a bearer token.

I'm pretty sure that the issue is not from backend because I checked the backend with Postman as well and it works correctly

So I put the code here

auth.service.ts:

    @Injectable({
    providedIn: 'root'
  })
  export class AuthService {
    baseUrl = environment.apiUrl + 'authentication/';  //environment.apiUrl + 'auth/';
    jwtHelper = new JwtHelperService();
    decodedToken: any;
    currentUser: User;      
  //================================================================================================
    constructor(private myalertService: MyalertService,private http: HttpClient) {}
  //================================================================================================
    login(model: any) {
      return this.http.post(this.baseUrl + 'login', model).pipe(
        map((response: any) => {
          const user = response;
          if (user) {
            localStorage.setItem('token', user.user.token);
            localStorage.setItem('user', JSON.stringify(user.user));
            this.decodedToken = this.jwtHelper.decodeToken(user.user.token);
            this.currentUser = user.user;             
           
          }
        })
      );
    } 
  //================================================================================================
    loggedIn() {
      const token = localStorage.getItem('token');
      return !this.jwtHelper.isTokenExpired(token);
    }
  //================================================================================================
    roleMatch(allowedRoles): boolean {
      let isMatch = false;
      const userRoles = this.decodedToken.role as Array<string>;   
 
      allowedRoles.forEach(element => {
        if (userRoles.includes(element)) {
          isMatch = true;
          return;
        }
      });
      return isMatch;
    }   
      //===================================================================================================
  }

company.service.ts:

@Injectable({
  providedIn: 'root'
})
export class CompanyService {
   result = false;
   baseUrl = environment.apiUrl + 'companies';

      
constructor(private myalertService: MyalertService,private http: HttpClient  ) { }
 //=============================================================================================

  getCompaniesList(page?,itemsPerPage?,srchMdl?): Observable<PaginatedResult<Company[]>> {

      const paginatedResult : PaginatedResult<Company[]> = new PaginatedResult<Company[]>();
  
      let params = new HttpParams();       

      if (page != null && itemsPerPage != null) {
        params = params.append('pageNumber', page);
        params = params.append('pageSize', itemsPerPage);
       
      } 
   
      if (srchMdl != null) {

              if(srchMdl.name != "" && srchMdl.name != null ) params = params.append('name',srchMdl.name);
              if(srchMdl.address != "" && srchMdl.address != null ) params = params.append('address',srchMdl.address);
              if(srchMdl.country != "" && srchMdl.country != null) params = params.append('country',srchMdl.country);
              if(srchMdl.searchOperator != "" && srchMdl.searchOperator != null) params = params.append('searchOperator',srchMdl.searchOperator); else params = params.append('searchOperator','AND');
              if(srchMdl.sortBasedOn != "" && srchMdl.sortBasedOn != null) params = params.append('orderBy',srchMdl.sortBasedOn);
      }

    

            return this.http.get<Company[]>(this.baseUrl , { observe: 'response', params},)
            .pipe(
              map(response => {
                paginatedResult.result = response.body;
                if (response.headers.get('X-Pagination') != null) {
                  paginatedResult.pagination = JSON.parse(response.headers.get('X-Pagination'));  
                }             
                return paginatedResult;
              })
            );
    } 
    //=============================================================================================
  }

company.resolver.ts:

    @Injectable()
export class CompanyResolver implements Resolve<Company[]> {
    pageNumber = 0;
    pageSize = 5;

    constructor(private companyService: CompanyService, private router: Router,
        private alertify: MyalertService) {}

    resolve(route: ActivatedRouteSnapshot): Observable<Company[]> {
        return this.companyService.getCompaniesList(this.pageNumber, this.pageSize).pipe(
            catchError(error => {
                this.alertify.error('Problem retrieving data');
                this.router.navigate(['/home']);
                return of(null);
            })
        );
    }
}

hasRole.directive.ts:

@Directive({
    selector: '[appHasRole]'
  })
  export class HasRoleDirective implements OnInit {
    @Input() appHasRole: string[];
    isVisible = false;
  
    constructor(
      private viewContainerRef: ViewContainerRef,
      private templateRef: TemplateRef<any>,
      private authService: AuthService) { }
  
    ngOnInit() {
      const userRoles = this.authService.decodedToken.role as Array<string>;
        
      if (!userRoles) {
        this.viewContainerRef.clear();
      }

      if (this.authService.roleMatch(this.appHasRole)) {
        if (!this.isVisible) {
          this.isVisible = true;
          this.viewContainerRef.createEmbeddedView(this.templateRef);
        } else {
          this.isVisible = false;
          this.viewContainerRef.clear();
        }
      }
    }
  
  }

auth.guard.ts:

@Injectable({
    providedIn: 'root'
  })
  export class AuthGuard implements CanActivate {
    constructor(  private authService: AuthService, 
                  private router: Router,
                  private alertify: MyalertService) {}
  
    canActivate(next: ActivatedRouteSnapshot): boolean {    
    
     const roles = next.firstChild.data['userRoles'] as Array<string>;
     console.log('auth.guard.ts');
     console.log(roles);
      if (roles) {
        const match = this.authService.roleMatch(roles);
        if (match) {
          return true;
        } else {        
          this.alertify.error('You are not authorized to access this area');
        }
      }
  
      if (this.authService.loggedIn()) {
        return true;
      }
  
      this.alertify.error('You shall not pass!!!');
      this.router.navigate(['/company']);
      return false;
    }
  }

routes.ts:

export const appRoutes: Routes = [
    {path: '', component: HomeComponent},
    {
        path: '',
         runGuardsAndResolvers: 'always',
         canActivate: [AuthGuard],
        children: [
    
           {path: 'company', component: CompanyComponent, data: {userRoles: ['Administrator', 'Manager']}},
      
            {path: 'register', component: RegisterComponent },
            {path: 'memberList', component: MemberListComponent},
    
            {path: 'admin', component: AdminPanelComponent, data: {userRoles: ['Administrator', 'Manager']}},

        ]
    },
    {path: '**', redirectTo: '', pathMatch: 'full'},
];

app.module.ts:

export function tokenGetter() {
   return localStorage.getItem('token');
 }

@NgModule({
   declarations: [        
      CompanyComponent,      
      HomeComponent,         
      NavBarComponent,
      HasRoleDirective
   ],
   imports: [      
      ReactiveFormsModule,
      BrowserModule,
      AppRoutingModule,
      BrowserAnimationsModule,
      MatSliderModule,
      MatButtonModule,
      MatIconModule,
      MatDividerModule,
      MatSnackBarModule,
      MatTableModule,
      HttpClientModule,
      FormsModule,
      MatFormFieldModule,
      MatCardModule,
      MatInputModule,
      MatToolbarModule,
      MatPaginatorModule,
      MatProgressSpinnerModule,
      MatSelectModule,
      MatRadioModule,
      FlexLayoutModule,
      RouterModule.forRoot(appRoutes),
      JwtModule.forRoot({
         config: {
            //This automatically adds the token to the request.
           tokenGetter: tokenGetter,
          
           whitelistedDomains:['localhost:5001'],
           blacklistedRoutes: ['localhost:5001/api/authentication'],
          
         }
       })
   ],
   providers: [
      CompanyResolver,
      AuthService,
      AuthGuard,
      CompanyService
   ],
   bootstrap: [
      AppComponent
   ]
})
export class AppModule { }

navBar.component.ts:

xport class NavBarComponent implements OnInit {
  model: any = {};

  constructor( 
    private authService: AuthService ,
    private alertify: MyalertService ,
    private router: Router
    ) {}

  ngOnInit() {
    this.model.username = 'abcdefgh';
    this.model.password = 'Pass12345678';
  }


  login() {  

    this.authService.login(this.model).subscribe(next => {
      this.alertify.success('Logged in successfully');
     
    }, error => {
      this.alertify.error(error);
    }, () => {
      

    });
  }


  loggedIn() {
    const token = localStorage.getItem('token');
    return !!token;
  }
  logout() {
    localStorage.removeItem('token');
    localStorage.removeItem('user');
    this.authService.decodedToken = null;
    this.authService.currentUser = null;
    this.alertify.success('logged out');
    this.router.navigate(['/home']);
  }
}

navBar.component.html:

 <mat-toolbar color="primary">
 
  <span class="fill-remaining-space"></span>
 
  <button mat-button routerLink="login">Login</button>
  <button mat-button (click)="logout()">Logout</button>

  <ul *ngIf="loggedIn()" >
     <button *appHasRole="['Admin','Manager']" mat-button [routerLink]="['/company']">company Manager</button>
     <button  mat-button [routerLink]="['/memberList']">member List</button>
  </ul>



  
  
  <form #loginForm="ngForm" *ngIf="!loggedIn()"  class="form-inline my-2 my-lg-0" (ngSubmit)="login()">  

    <mat-form-field appearance="legacy" >    
      <input matInput name="username" placeholder="Username " [(ngModel)]="model.username" >
    </mat-form-field>

    <mat-form-field appearance="legacy" > 
      <input matInput name="password" placeholder="Password " [(ngModel)]="model.password">
    </mat-form-field>

    <button mat-raised-button color="accent"   type="submit"> Login </button>   
  </form>
    
</mat-toolbar>
1

There are 1 answers

3
Namrata Das On

The backend expects an Bearer token in every request. In your response headers the Bearer token are missing.

You can set the header in interceptor as

const token = this.oauthService.accessToken;
const authToken = "Bearer " + token;

headers: request.headers.set("Authorization", authToken).

enter image description here