Hi I'm trying to write a simple Angular 6 interceptor that adds the jwt token to the header when sending requests.
The problem is that the header in the request arrives NULL on the backend, so of course I can't get the authorization token and I'm having trouble figuring out why.
I'm pretty sure the problem is in my js code because if I try to send the same request via any REST client I can see the header in my Java code just fine.
Here's my js code:
import { Component, OnInit } from '#angular/core';
import {Observable} from 'rxjs/Observable';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { UserService } from './user.service';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
constructor(private http: HttpClient, private userService: UserService) { }
ngOnInit() {
this.userService.getAllUsers().subscribe(
data => {
console.log(data);
/* this.users_from_db=data; */
},
err => {
console.log(err);
}
);
}
users_from_db: Observable<any>;
}
import {Injectable} from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable()
export class UserService {
constructor(private http: HttpClient) {}
public getAllUsers(): Observable<any> {
return this.http.get<any>('http://localhost:8080/users/get-all');
}
}
import { Injectable } from '#angular/core';
import {HttpInterceptor, HttpRequest, HttpHandler, HttpSentEvent, HttpHeaderResponse, HttpProgressEvent,
HttpResponse, HttpUserEvent, HttpErrorResponse} from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '#angular/router';
import {TokenStorage} from './token.storage';
import 'rxjs/add/operator/do';
const TOKEN_HEADER_KEY = 'Authorization';
#Injectable()
export class Interceptor implements HttpInterceptor {
constructor(private token: TokenStorage, private router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
let authReq = req;
if (this.token.getToken() != null) {
console.log("authorizing...");
authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + this.token.getToken())});
console.log(authReq);
}
if (!authReq.headers.has('Content-Type')) {
authReq = req.clone({ headers: req.headers.set('Content-Type', 'application/json') });
}
return next.handle(authReq).do(
(err: any) => {
if (err instanceof HttpErrorResponse) {
console.log(err);
console.log('req url :: ' + req.url);
if (err.status === 401) {
this.router.navigate(['login']);
}
}
}
);
}
}
The value of the token is surely there when I do this.token.getToken()in the intercept function. I checked by printing the value in the browser console.
Any help is appreciated, thanks.
This is my interceptor:
import { Injectable } from "#angular/core";
import { HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpInterceptor } from "#angular/common/http";
import { Observable, BehaviorSubject, throwError } from "rxjs";
import { catchError, map, filter, take, switchMap, finalize } from "rxjs/operators";
import { AppConsts } from "../consts";
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(this.addTokenToRequest(request)).pipe(
map(res => res),
catchError(err => {
if (err instanceof HttpErrorResponse && err.status === 401 && err.headers.has("Token-Expired")) {
// here code to refresh token if needed
} else {
return throwError(err);
}
})
);
}
private addTokenToRequest(request: HttpRequest<any>, token: string = null): HttpRequest<any> {
if (token) {
request = request.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}
else {
const currentUser = JSON.parse(localStorage.getItem(AppConsts.DEFAULT_USER_STORAGE_NAME));
if (currentUser && currentUser.token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
}
return request;
}
}
You also can see a simple example - http://jasonwatmore.com/post/2016/08/16/angular-2-jwt-authentication-example-tutorial
Related
I am new to Angular and I am trying to implement api call which send token in header on all api so I am using Interceptor.
I am not able to set header in request.
jwt.interceptor.ts
import { Injectable } from '#angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let currentUser = localStorage.getItem('useremail')
let currentUserToken = localStorage.getItem('token')
if (currentUser && currentUserToken) {
request = request.clone({
setHeaders: {
'Authorization': `${currentUserToken}`
}
});
}
// console.log("Request", request)
return next.handle(request);
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { AppUserListingComponent } from './app-user-listing/app-user-listing.component';
import { JwtInterceptor } from './jwt.interceptor';
#NgModule({
providers: [ { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true } ],
bootstrap: [AppComponent]
})
export class AppModule { }
user-listing.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserListingService {
apiUrl = "URL";
constructor(
private http: HttpClient
) { }
fetchAllUsers() {
return this.http.get(`${this.apiUrl}/fetchAdminsUsers`)
}
}
What is the reason my code is not working ?
Thanks in advance
In your app.module.ts code there's no import: [HttpClientModule] Do you have imported the HttpClientModule?
I'm experiencing a similar issue with angular 8.x http interceptor is not working OK.
Add imports: [BrowserModule, HttpClientModule] in #NgModule.
Authorization is a special header, you need to add withCredentials: true to the request to add it.
request = request.clone({
setHeaders: {
'Authorization': `${currentUserToken}`
},
withCredentials: true
});
try using instance of like below.
return next.handle(request).pipe(
tap(
event => {
if(event instanceof HttpResponse){
//api call success
console.log('success in calling API : ', event);
}
},
error => {
if(event instanceof HttpResponse){
//api call error
console.log('error in calling API : ', event);
}
}
)
)
Taking again the tutorial of the site Angular, I created in winamp a database with a table including a field {"id": id, "name": name} and I make 2 queries on this table with Symfony4:
1) A request to list heroes.
2) A request to create hero.
Executed from Angular 7, the query 1) works perfectly (route / listerHeroes).
Executed from Angular 7, query 2) does not work, it returns error 405 (route / ajouterHero). However launched from Postman, this query works.
I can not find any documentation to explain to me this bug on which I stumble for several days. A track please
Below copy of both classes: heroes.service.ts and component3.component.ts
// heroes.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { HttpErrorHandler, HandleError } from './http-error-handler.service';
import { Hero } from '../assets/Structure';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
#Injectable({ providedIn: 'root' })
export class HeroesService {
heroesUrl = 'http://heroes/';
private handleError: HandleError;
constructor(private http: HttpClient, httpErrorHandler: HttpErrorHandler) {
this.handleError = httpErrorHandler.createHandleError('HeroesService');
}
getHeroes$(): Observable<Hero[]> {
return this.http.get<Hero[]>(`${this.heroesUrl}listerHeroes`, httpOptions);
}
addHero(hero: Hero): Observable<Hero> {
return this.http
.post<Hero>(`${this.heroesUrl}ajouterHero`, hero, httpOptions)
.pipe(catchError(this.handleError('addHero', hero)));
}
}
// component3.component.ts
import { Component, OnInit } from '#angular/core';
import { HeroesService } from '../heroes.service';
import { Hero } from '../../assets/Structure';
#Component({
selector: 'app-component3',
templateUrl: './component3.component.html',
styleUrls: ['./component3.component.css']
})
export class Component3Component implements OnInit {
heroes: Hero[];
editHero: Hero;
constructor(private heroesService: HeroesService) {}
ngOnInit() {
this.heroesService.getHeroes$().subscribe(res => (this.heroes = res));
}
addHero(name: string): void {
name = name.trim();
console.log('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF C3A name =', name);
if (!name) {
return;
}
const newHero: Hero = { 'id': 0, 'name': name } as Hero;
this.heroesService.addHero(newHero).subscribe(hero => {
console.log('GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG C3B hero= ', hero);
this.heroes.push(hero);
});
}
}
I found the solution:
The blocking was listed backend (Symfony4) which refused the pre-query OPTIONS. It was necessary to install and configure the bundle nelmio (https://github.com/nelmio/NelmioApiDocBundle) which allows the smooth running of the request.
I have a snackbar component which I have created like this!
import {Component, ViewEncapsulation, OnInit, OnDestroy} from '#angular/core';
import {
MatSnackBar,
MatSnackBarConfig,
MatSnackBarHorizontalPosition,
MatSnackBarVerticalPosition,
} from '#angular/material';
import { Subscription } from 'rxjs';
import { AuthenticationService } from "../../services/authentication.service";
#Component({
selector: 'snack-message',
templateUrl: './messages.component.html',
styleUrls: [ './mesaages.component.scss' ],
encapsulation: ViewEncapsulation.None
})
export class SnackBarMessages implements OnInit, OnDestroy {
action: boolean = true;
setAutoHide: boolean = true;
autoHide: number = 2000;
horizontalPosition: MatSnackBarHorizontalPosition = 'center';
verticalPosition: MatSnackBarVerticalPosition = 'bottom';
private showMessageSub: Subscription;
messageData: object;
addExtraClass: boolean = false;
constructor(public snackBar: MatSnackBar, public authenticationService: AuthenticationService) {
}
ngOnInit() {
this.messageData = this.authenticationService.getMessageData();
this.showMessageSub = this.authenticationService.getMessageListener()
.subscribe(data => {
this.messageData = data;
});
this.openMessageSnackBar(this.messageData);
}
openMessageSnackBar(data) {
let config = new MatSnackBarConfig();
config.verticalPosition = this.verticalPosition;
config.horizontalPosition = this.horizontalPosition;
config.duration = this.setAutoHide ? this.autoHide : 0;
this.snackBar.open(data.message, data.action, config);
}
ngOnDestroy() {
if (this.showMessageSub) {
this.showMessageSub.unsubscribe();
}
}
}
I have added subject and i have created a subscription which listens to the messageListener in my Authentication Service. Then i call next with my subjectListener with the data I want to pass. I am not getting any call on my component. I don't understand why ? This is my Service!
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Subject } from 'rxjs';
import { Router, ActivatedRoute } from '../../../node_modules/#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
private mesageListener = new Subject<object>();
private messageData: object;
getMessageListener() {
return this.mesageListener.asObservable();
}
getMessageData() {
return this.messageData;
}
createUser(email: string, password: string) {
const userData: AuthModel = {
email: email,
password: password
}
const requestPath = this.getModes()[`${this.navigatedFrom}`];
this.http.post(`http://localhost:3000/api/${requestPath}/signup`,userData)
.subscribe(response => {
this.messageData = {
message: 'Sign Up Successful. Please Login now!',
action: 'Ok! Got it.'
}
this.mesageListener.next(this.messageData);
});
}
}
Try to use new BehaviorSubject<object>(null); for mesageListener .
you can see more details on Subject and BehavoiurSubject here : angular2-difference-between-a-behavior-subject-and-an-observable/
In Component subscribe to the value of Observable. It should work.
I have an Ionic 3 application that calls a Spring Boot API to login into Mobile App,My Spring Boot application is hosted in Aws.It works on android But it doesn't work on ios ,It Says Cross Issue I tried Many Solutions from internet But did not get the result.
The error I am getting is
response with status: 0 for url : null
this is my working spring boot controller
#CrossOrigin
#RestController
#RequestMapping("/api/customerr")
public class loginController extends NamedParameterJdbcDaoSupportClass{
#Autowired
LoginService loginService;
#Autowired
LoginValidation loginValidation;
#RequestMapping(value="/getUser", method = RequestMethod.POST)
public Response getUsers( #RequestBody UserRequest userRequest ) throws Exception {
List<User> users = null;
try {
loginValidation.getUsers(userRequest.getSsoid(), userRequest.getPassword() );
} catch (ValidationExceptions ex) {
ex.printStackTrace();
return new Response("400", ex.getMessage());
}
try {
users = loginService.getUsers(userRequest.getSsoid(),userRequest.getPassword() );
} catch (Exception ex) {
return new Response("400", ex.getMessage());
}
for(User u: users )
{
if(u.getStatus().getStatusId() == 2)
{
return new Response("300", u.getSsoid());
}
}
return new Response("200", users);
}
}
this is my ionic provider/service
import { Http, Response} from '#angular/http';
import { Injectable } from '#angular/core';
#Injectable()
export class loginService {
data:any;
constructor(public http : Http){}
getAllUsers(authData){
alert("api users")
return new Promise(resolve=>{
this.http.post('http://someAddress:8080/api/customerr/getUser', authData)
.map(res=>res.json())
.subscribe(data=>{
this.data=data;
resolve(this.data)
})
})
}
}
This is the login page where I am calling the api
import { Component, ViewChild } from '#angular/core';
import { Alert,AlertController,IonicPage,Loading,LoadingController,NavController,MenuController } from 'ionic-angular';
import { FormBuilder, FormGroup, Validators, NgForm } from '#angular/forms';
import { EmailValidator } from '../../validators/email';
import { CustomerPage } from '../customer/customer';
import { loginService } from '../../providers/loginservice/login.servie';
import {Response} from '#angular/http';
import { ToastController } from 'ionic-angular';
import { CustomerdetailsPage } from '../customerdetails/customerdetails';
#IonicPage()
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
#ViewChild('f') loginForm: NgForm;
loginResponse:any;
constructor(
public navCtrl: NavController,
public loadingCtrl: LoadingController,
public alertCtrl: AlertController,
public menu:MenuController,
private loginservice : loginService,
private toastCtrl: ToastController
){
}
ionViewWillEnter(){
this.menu.enable(false)
}
ionViewWillLeave(){
this.menu.enable(true)
}
loginUser() {
console.log("login data");
console.log(this.loginForm.value);
this.loginservice.getAllUsers(this.loginForm.value).then(
(data:any) =>
{
this.loginResponse = data.json();
alert("data");
alert(this.loginResponse.code);
if(this.loginResponse.code === '200')
{
if(this.loginResponse.data.length !== 0)
{
let key1 = 'islogIn';
localStorage.setItem(key1, "true");
this.navCtrl.setRoot(CustomerPage);
}
else if(this.loginResponse.data.length == 0)
{
let toast = this.toastCtrl.create({
message: 'invalid username or password',
duration: 3000,
position: 'bottom'
});
toast.onDidDismiss(() => {
console.log('Dismissed toast');
});
toast.present();
}
}
else if (this.loginResponse.code === '300')
{
let toast = this.toastCtrl.create({
message: 'user is deactivated',
duration: 3000,
position: 'bottom'
});
toast.onDidDismiss(() => {
console.log('Dismissed toast');
});
toast.present();
}
},
(error)=>{
alert(error);
}
);
}
}
And this the app.module.ts
#NgModule({
declarations: [
..
LoginPage,
...
],
imports: [
BrowserModule,
HttpClientModule,
HttpModule,
IonicStorageModule.forRoot(),
IonicModule.forRoot(MyApp),
],
bootstrap: [IonicApp],
entryComponents: [
.
.
LoginPage,
.
.
],
providers: [
Network,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler},
Storage,
SocialSharing,
File,
FileOpener,
DatabaseProvider,
SQLitePorter,
SQLite,
GlobalProvider,
PageserviceProvider,
loginService,
syncService
]
})
export class AppModule {}
Please Use Native HTTP API for IOS
https://ionicframework.com/docs/native/http/
I know that i can make a component and a service in Angular 2(Typescript) and can parse the response in component, but i want my architecture to be like below as for any changes in response i will have to change only my parser:
Service : Do API call and fetch response. Has a get response method.
Parser : Gets Response and parses response and returns response in a usable form to the component.
Component : gets Response from the parser and display data to view.
For doing it i am using an observable from service and subscribing it in parser. And then using an observable from parser and subscribing it in component.
But getting a lot of errors while doing so. Do i miss any steps or is it the right approach to the problem.
Adding Code :
Component:::
import { AuthenticateParser } from './../../parsers/authenticate.parser';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'my-component',
moduleId: module.id,
templateUrl: './app.component.html',
providers : [AuthenticateParser]
})
export class AppComponent implements OnInit {
// Constructor Function
constructor(private _authenticationService: AuthenticateParser) {
}
ngOnInit(): void {
this._authenticationService.setAuthenticationToken();
}
}
Service :::
import { Observable } from 'rxjs/Observable';
import { IRequest } from './api.interaction.interface';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
#Injectable()
export class ApiInteractionService {
// Constructor Function
constructor(private _http: Http) {
}
getApiResponse(p_attr: IRequest): Observable<Response> {
// Set request url
let _authenticationUrl: string = p_attr.url;
// Set request body
let _body: any = p_attr.body;
// Set content type to JSON
let _headers = new Headers(p_attr.header);
// Set a request option
let _options = new RequestOptions({ headers: _headers });
return this._http[p_attr.method](_authenticationUrl, _body, _options)
.map((response: Response) => response.json()) ///... change response to JSON format
.do((data:any) => console.log('All : ' + JSON.stringify(data))) //... console response
.catch((error: any) => Observable.throw(error.json().error || 'Server error')); //...errors if any
}}
PARSER :::
import { Injectable, Component } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { IRequest } from './../services/api.interaction.interface';
import { ApiInteractionService } from './../services/api.interaction.service';
#Injectable()
export class AuthenticateParser {
private _authenticationToken: string = '';
// Constructor Function
constructor(private _authenticationService: ApiInteractionService) {
}
// Fetches Authentication Token
setAuthenticationToken(): void {
let p_url: string = 'http://10.5.214.82:8443/auth/authenticate';
let p_body: Object = {
"userId": "admin",
"password": "admin"
};
let p_header: Object = {
"Content-Type": "application/json"
};
let p_attrs: IRequest = {
'method': 'post',
'url': p_url,
'body': p_body,
'header': p_header
};
this._authenticationService.getApiResponse(p_attrs)
.subscribe(
response => this.parseSuccessResponse(response),
error => this.parseErrorResponse(error)
);
}
// Parses Token response and sets it.
parseSuccessResponse(p_response: any): void {
this._authenticationToken = p_response.data;
}
// Parses Error response and sets it.
parseErrorResponse(p_error: any): void {
this._authenticationToken = p_error.data;
}
// returns authentication token
getAuthenticationToken():string {
return this._authenticationToken;
}
}
Sounds like you want to make a parser as a service. Would have helped if you gave example code.
In your parser, you can subscribe to the http.get response, and then parse the response, and then publish the result to a subject. Then in your component you subscribe to the subject from parser.
Your parser will be something like:
import { Injectable, OnInit} from '#angular/core';
import { Subject } from 'rxjs/Subject';
import {YourService} from '....''
#Injectable()
export class ParseService implements OnInit {
public parseSubject: Subject<any> = new Subject <any>();
constructor(private service: YourService) {}
ngOnInit(){
this.service.yourGetFunction()
.map(res=> res.json())
.subscribe(
response => {
parse logic that stores result in parsedObj
parseSubject.next(parsedObj);
},
error => { this.errorMessage = <any>error});
}
}
Now in your component, you can import ParseService and subscribe to its parseSubject to get the parsed output