If you want the credentials (cookie authentication token) to be passable through a call, you need to add { withCredentials: true } in your httpclient call. Something like this:
import { HttpClient } from '#angular/common/http';
...
constructor(private httpclient: HttpClient) { }
this.httpclient.get(url, { withCredentials: true })
I would just like to know if there is a way to preset { withCredentials: true } with every single call. I don't want to have to add { withCredentials: true } every time I make a call.
Here is a related question, but I am not sure if this works with HttpClient?
Create a HttpInterceptor:
#Injectable()
export class CustomInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({
withCredentials: true
});
return next.handle(request);
}
}
#NgModule({
bootstrap: [AppComponent],
imports: [...],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: CustomInterceptor ,
multi: true
}
]
})
export class AppModule {}
You have two option here -
HttpInterceptor
auth.service.ts
If you are writing any standard application which require credential validation now or later then you will need AuthService. However you can ignore right now if you want.
// src/app/auth/auth.service.ts
import { Injectable } from '#angular/core';
import decode from 'jwt-decode';
#Injectable()
export class AuthService {
public getToken(): string {
return localStorage.getItem('token');
}
public isAuthenticated(): boolean {
// get the token
const token = this.getToken();
// return a boolean reflecting
// whether or not the token is expired
return tokenNotExpired(null, token);
}
}
app.module.ts
Provide HTTP_INTERCEPTORS which will intercept all request of yours.
// src/app/app.module.ts
import { HTTP_INTERCEPTORS } from '#angular/common/http';
import { TokenInterceptor } from './../auth/token.interceptor';
#NgModule({
bootstrap: [AppComponent],
imports: [...],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true
}
]
})
export class AppModule {}
token.interceptor.ts
This is Interceptor through which each HTTP request will pass through.
// src/app/auth/token.interceptor.ts
import { Injectable } from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '#angular/common/http';
import { AuthService } from './auth/auth.service';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(public auth: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${this.auth.getToken()}`
}
});
return next.handle(request);
}
}
Overwrite the standard HttpClient
app.module.ts
#NgModule({
providers: [ // expose our Services and Providers into Angular's dependency injection
{provide: HttpClient, useClass: ExtendedHttpService},
]
})
export class AppModule {
}
extended-http.service.ts
import {Injectable, Injector} from '#angular/core';
import {Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import {Router} from '#angular/router';
import {AuthService} from './auth.service';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class ExtendedHttpService extends HttpClient {
private router: Router;
private authService: AuthService;
constructor(backend: XHRBackend, defaultOptions: RequestOptions, private injector: Injector) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
if (typeof url === 'string') {
if (!options) {
options = {headers: new Headers()};
}
this.setHeaders(options);
} else {
this.setHeaders(url);
}
//console.log("url: " , url , ", Options:" , options);
return super.request(url, options).catch(this.catchErrors());
}
private catchErrors() {
return (res: Response) => {
if (this.router == null) {
this.router = this.injector.get(Router);
}
if (res.status === 401 || res.status === 403) {
//handle authorization errors
//in this example I am navigating to login.
console.log("Error_Token_Expired: redirecting to login.");
this.authService.logout();
}
return Observable.throw(res);
};
}
private setHeaders(objectToSetHeadersTo: Request | RequestOptionsArgs) {
if (this.authService == null) {
this.authService = this.injector.get(AuthService);
}
//add whatever header that you need to every request
//in this example I could set the header token by using authService that I've created
objectToSetHeadersTo.headers.set('Authorization', this.authService.getAuthToken());
}
}
Related
In a project, I work with 2 HTTP interceptors: 1 to add a JWT token to each request, the other to intercept an incoming 401 error status.
I call a separate program to get all feedback for my app in this service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { environment } from '#environments/environment';
import { Feedback } from '#app/_models/feedback';
#Injectable({ providedIn: 'root' })
export class FeedbackService {
constructor(
private http: HttpClient
) {}
getAll() {
return this.http.get<Feedback[]>(`${environment.apiUrl}/feedback`);
}
getById(id: string) {
return this.http.get<Feedback>(`${environment.apiUrl}/feedback/${id}`);
}
delete(id: string) {
return this.http.delete(`${environment.apiUrl}/feedback/${id}`);
}
}
The JWT interceptor:
import { Injectable } from '#angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '#environments/environment';
import { AuthorizationService } from 'src/shared/authorization.service';
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private auth: AuthorizationService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add auth header with jwt if user is logged in and request is to the api url
const authenticatedUser = this.auth.getAuthenticatedUser();
if (authenticatedUser == null) {
return;
}
authenticatedUser.getSession( (err, session) => {
if (err) {
console.log(err);
return;
}
const isApiUrl = request.url.startsWith(environment.apiUrl);
const token = session.getIdToken().getJwtToken();
const headers = new Headers();
headers.append('Authorization', token);
if (this.auth.isLoggedIn() && isApiUrl) {
request = request.clone({
setHeaders: {
Authorization: token,
}
});
}
return next.handle(request);
});
}
}
The Error interceptor:
import { Injectable } from '#angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AccountService } from '#app/_services';
#Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private accountService: AccountService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log(next.handle(request));
return next.handle(request).pipe(catchError(err => {
if (err.status === 401) {
// auto logout if 401 response returned from api
this.accountService.logout();
}
const error = err.error.message || err.statusText;
return throwError(error);
}));
}
}
When I provide both interceptors in my app.module,
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
I always get an error saying the following below. This happens because next.handle(request) apparently is undefined, and I don't really know why. Using only the Error interceptor works with no issue.
ERROR TypeError: Cannot read property 'pipe' of undefined
at ErrorInterceptor.intercept (error.interceptor.ts:14)
at HttpInterceptorHandler.handle (http.js:1958)
at HttpXsrfInterceptor.intercept (http.js:2819)
at HttpInterceptorHandler.handle (http.js:1958)
at HttpInterceptingHandler.handle (http.js:2895)
at MergeMapSubscriber.project (http.js:1682)
at MergeMapSubscriber._tryNext (mergeMap.js:46)
at MergeMapSubscriber._next (mergeMap.js:36)
at MergeMapSubscriber.next (Subscriber.js:49)
at Observable._subscribe (subscribeToArray.js:3)
Using only the JwtInterceptor gives following error, which I can't figure out where it's coming from. Of course, I would want to use both. Am I missing something while configuring the multiple interceptors?
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at subscribeTo (subscribeTo.js:27)
at subscribeToResult (subscribeToResult.js:11)
at MergeMapSubscriber._innerSub (mergeMap.js:59)
at MergeMapSubscriber._tryNext (mergeMap.js:53)
at MergeMapSubscriber._next (mergeMap.js:36)
at MergeMapSubscriber.next (Subscriber.js:49)
at Observable._subscribe (subscribeToArray.js:3)
at Observable._trySubscribe (Observable.js:42)
at Observable.subscribe (Observable.js:28)
at MergeMapOperator.call (mergeMap.js:21)
Rewrite your JwtInterceptor:
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable, from } from 'rxjs';
import { environment } from '#environments/environment';
import { AuthorizationService } from 'src/shared/authorization.service';
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private auth: AuthorizationService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.getSessionWithAuthReq(request, next));
}
async getSessionWithAuthReq(request: HttpRequest<any>, next: HttpHandler){
const authenticatedUser = this.auth.getAuthenticatedUser();
if (authenticatedUser) {
const authRequest: HttpRequest<any> = await new Promise( (resolve) => {
authenticatedUser.getSession( (err, session) => {
if (err) {
console.log(err);
// want to go on without authenticating if there is an error from getting session
return resolve(request);
}
const isApiUrl = request.url.startsWith(environment.apiUrl);
const token = session.getIdToken().getJwtToken();
const headers = new Headers();
headers.append('Authorization', token);
if (this.auth.isLoggedIn() && isApiUrl) {
const req = request.clone({
setHeaders: {
Authorization: token,
}
});
return resolve(req);
}
return resolve(request);
});
});
return next.handle(authRequest).toPromise();
}
return next.handle(request).toPromise();
}
}
I am trying to upgrade this to latest but getting error to display the data. i need to refactor from Jsonp to HttpClient, and HttpParams for below code. Any help would be great.
import { Injectable } from '#angular/core';
import {Jsonp, URLSearchParams} from '#angular/http';
import 'rxjs/Rx';
#Injectable()
export class MyService {
apikey: string;
constructor(private _jsonp: Jsonp) {
this.apikey = 'my_api_key';
console.log('it works');
}
getData() {
var search = new URLSearchParams();
search.set('sort_by','popularity.desc');
search.set('api_key', this.apikey);
return this._jsonp.get('url', {search})
.map(res => {
return res.json();
})
}
}
This should be able to fix your problem. Please check doc for more info
In you module
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
imports: [
BrowserModule,
// import HttpClientModule after BrowserModule.
HttpClientModule,
],
declarations: [
AppComponent,
],
bootstrap: [ AppComponent ]
})
export class AppModule {}
In your service
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class MyService {
apikey: string;
constructor(private http: HttpClient){
this.apikey = 'my_api_key';
}
getData(): Observable<any> {
const params = new HttpParams()
.set('sort_by', popularity.desc)
.set('api_key', this.apikey);
return this.http.get('url', {params});
}
}
I am getting an error of Cannot read property 'get' of undefined TypeError: Cannot read property 'get'. Which needs to post some data from an endpoint to the table as illustrated on the example from the GitHub.
Here is the api service that I call the get method.
import { Injectable } from '#angular/core';
import { Http, Headers, Request, RequestOptions, RequestMethod, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import { AuthService } from './auth.service';
import { environment } from '../../environments/environment';
#Injectable()
export class ApiService {
private baseUrl = environment.apiUrl;
constructor(private http:Http, private authService: AuthService) { }
get(url: string){
return this.request(url, RequestMethod.Get);
}
post(url: string, body: Object){
return this.request(url, RequestMethod.Post, body);
}
put(url: string, body: Object){
return this.request(url, RequestMethod.Put, body);
}
delete(url: string){
return this.request(url, RequestMethod.Delete);
}
request(url: string, method: RequestMethod, body?: Object){
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', `Bearer ${this.authService.getToken()}`);
const requestOptions = new RequestOptions({
url: `${this.baseUrl}/${url}`,
method: method,
headers: headers
});
if(body){
requestOptions.body = body;
}
const request = new Request(requestOptions);
return this.http.request(request)
.map((res: Response) => res.json())
}
}
This is the exact line of code in another service that consume the api GET
staticQuery(): Observable<IUser[]> {
return this.api2.get('auth/account/users')
.map((res: Response) => {
return res.json();
});
}
Here is the complete code of the second service
import { Provider, SkipSelf, Optional, InjectionToken } from '#angular/core';
import { Response, Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { HttpInterceptorService, RESTService } from '#covalent/http';
import { ApiService } from '../../../../services/api.service';
import { AuthService } from '../../../../services/auth.service';
export interface IUser {
_id: string;
email:string;
createdAt: Date;
profile: {
name: string;
gender: string;
location: String;
picture: {
// data: Buffer;
contentType: string;
}
}
}
export class UserService extends RESTService<IUser> {
constructor(private _http: HttpInterceptorService, api: string,
private authService: AuthService,
private api2: ApiService) {
super(_http, {
baseUrl: api,
path: '/dashboard/users',
});
}
staticQuery(): Observable<IUser[]> {
return this.api2.get('auth/account/users')
.map((res: Response) => {
return res.json();
});
}
}
export const USERS_API: InjectionToken<string> = new InjectionToken<string>('USERS_API');
export function USER_PROVIDER_FACTORY(
parent: UserService, interceptorHttp: HttpInterceptorService, api: string,authService: AuthService,
api2: ApiService): UserService {
return parent || new UserService(interceptorHttp, api,authService,api2);
}
export const USER_PROVIDER: Provider = {
// If there is already a service available, use that. Otherwise, provide a new one.
provide: UserService,
deps: [[new Optional(), new SkipSelf(), UserService], HttpInterceptorService, USERS_API],
useFactory: USER_PROVIDER_FACTORY,
};
You can see all the code here for a previous question I had asked earlier here
My module
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { RouterModule } from '#angular/router';
import { MatSnackBarModule, MatIconModule, MatListModule, MatTooltipModule, MatCardModule, MatButtonModule,
MatToolbarModule, MatInputModule, MatSlideToggleModule, MatMenuModule,MatSelectModule } from '#angular/material';
import { CovalentLoadingModule, CovalentDialogsModule, CovalentMediaModule, CovalentLayoutModule,
CovalentSearchModule, CovalentCommonModule } from '#covalent/core';
import { UsersComponent } from './users.component';
import { UsersFormComponent } from './form/form.component';
import { userRoutes } from './users.routes';
import { UserService, IUser, USER_PROVIDER, USERS_API } from './services/user.service';
import { MyaccountComponent } from './myaccount/myaccount.component';
import { AlluserComponent } from './allusers/allusers.component';
export { UsersComponent, UsersFormComponent, UserService, IUser, USER_PROVIDER, USERS_API };
import { ImageUploadModule } from "angular2-image-upload";
import { AuthService } from '../../../services/auth.service';
import { ApiService } from '../../../services/api.service';
#NgModule({
declarations: [
UsersComponent,
UsersFormComponent,
MyaccountComponent,
AlluserComponent
], // directives, components, and pipes owned by this NgModule
imports: [
// angular modules
CommonModule,
FormsModule,
RouterModule,
// material modules
MatSnackBarModule,
MatIconModule,
MatListModule,
MatTooltipModule,
MatCardModule,
MatButtonModule,
MatToolbarModule,
MatInputModule,
MatSlideToggleModule,
MatMenuModule,
MatSelectModule,
// MdFormFieldModule,
// covalent modules
CovalentLoadingModule,
CovalentDialogsModule,
CovalentMediaModule,
CovalentLayoutModule,
CovalentSearchModule,
CovalentCommonModule,
// extra
userRoutes,
ImageUploadModule.forRoot(),
], // modules needed to run this module
providers: [
{ provide: USERS_API, useValue: ''},
USER_PROVIDER,
AuthService,
ApiService,
],
})
export class UsersModule {}
api.service.ts
import { Injectable, Inject } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/operator/retry';
import { CacheService } from './cache.service';
import { AuthService } from '../services/auth.service';
import { CookieService } from 'angular2-cookie/core';
#Injectable()
export class ApiService {
constructor(
public _http: Http,
private _auth: AuthService,
private _cookie: CookieService,
#Inject('isBrowser') public isBrowser: boolean
) {}
get(){
console.log(this._cookie.get('Token'));//undefined
}
}
controller.component.ts
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ApiService } from './api.service';
import { ReviewComponent } from '../shared/+review/review.component';
import { CookieService } from 'angular2-cookie/core';
// import { ModelService } from '../shared/model/model.service';
#Component({
selector: 'mall',
templateUrl: './mall.component.html',
styleUrls:['./mall.component.css'],
providers: [ ApiService, CookieService ]
})
export class MallComponent implements OnInit {
constructor(private _api: ApiService, private route: ActivatedRoute, private _cookie: CookieService) {}
ngOnInit(){
this._cookie.get('Token');// Token => value
this._api.get(); //Token => undefined
}
}
I don't understand this behavior. The cookie exist when i access in controller directly but is undefined when i access through service.
Is there any way to access cookie through services?
using https://github.com/salemdar/angular2-cookie with angular universal.
Maybe this?
ngOnInit(){
this._cookie.put('Token', WHATEVER_TOKEN_IS);// Token => value
console.log(this._api.get('Token')); //Token => undefined
}
and then
api-service
export class ApiService {
constructor(
readonly _http: Http,
private _auth: AuthService,
private _cookie: CookieService,
#Inject('isBrowser') public isBrowser: boolean
) {}
get() {
const token = this._cookie.get('Token');
console.log(token);
return token;
}
}
This might be late, but I went through the same problem.
I was not defining the base path as "/". So what was happening is that the cookie was being set for the default path where I was.
Eg. I was at site.com/auth/
Cookie would get saved at path "/auth"
If I save a cookie like
this.cookieService.set('token', token, null, "/");
then problem is solved.
Hope this helps further devs.
It was my mistake to add CookieService in component providers which initiate a new instance of service which was causing the issue.
#Component({
selector: 'mall',
templateUrl: './mall.component.html',
styleUrls:['./mall.component.css'],
providers: [ ApiService, CookieService ] //<--- here
})
CookieService should only be imported into AppComponent(root) to make a single instance available to other components.
I need to preload data on some url, so
I've resolver:
import {Injectable} from '#angular/core';
import {Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, } from '#angular/router';
import {Observable} from 'rxjs/Rx';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class ProfileResolver implements Resolve<any> {
constructor(
private _http: Http
) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<any>|any {
return this._http.get('/session').map(data => {
console.log(data);
return data;
});
}
}
and component:
import {Component} from '#angular/core';
import {RouterModule, ActivatedRoute} from '#angular/router';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
#Component({
selector: '[profile]',
host: {
class: 'profile-page app'
},
template: require('./profile.html')
})
export class Profile {
constructor(
private route:ActivatedRoute
) {
this.route.data.subscribe(data => {
console.log(data);
});
}
profile;
}
Route config:
{
path: 'profile',
loadChildren: () => System.import('../profile/profile.module'),
resolve: {
profile: ProfileResolver
},
},
Console.log in resolver shows received data, but in component it's empty object, whats wrong? When I used this code in rc4, all was fine.
Also if I change return _http.get(...) to simple value, like return "123"; this code will work. Thanks in advance.
Fetching your data can (and should) be done via
this.profile = this.route.snapshot.data['profile'];
Entire and complete explanation can be found here:
http://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html