Initially, I had a function that simply checked for the presence of a token and, if it was not present, sent the user to the login header. Now I need to implement the logic of refreshing a token when it expires with the help of a refreshing token. But I get an error 401. The refresh function does not have time to work and the work in the interceptor goes further to the error. How can I fix the code so that I can wait for the refresh to finish, get a new token and not redirect to the login page?
TokenInterceptor
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "#angular/common/http";
import {Injectable, Injector} from "#angular/core";
import {AuthService} from "../services/auth.service";
import {Observable, throwError} from "rxjs";
import {catchError, tap} from "rxjs/operators";
import {Router} from "#angular/router";
import {JwtHelperService} from "#auth0/angular-jwt";
#Injectable({
providedIn: 'root'
})
export class TokenInterceptor implements HttpInterceptor{
private auth: AuthService;
constructor(private injector: Injector, private router: Router) {}
jwtHelper: JwtHelperService = new JwtHelperService();
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.auth = this.injector.get(AuthService);
const accToken = this.auth.getToken();
const refToken = this.auth.getRefreshToken();
if ( accToken && refToken ) {
if ( this.jwtHelper.isTokenExpired(accToken) ) {
this.auth.refreshTokens().pipe(
tap(
() => {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${accToken}`
}
});
}
)
)
} else {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${accToken}`
}
});
}
}
return next.handle(req).pipe(
catchError(
(error: HttpErrorResponse) => this.handleAuthError(error)
)
);
}
private handleAuthError(error: HttpErrorResponse): Observable<any>{
if (error.status === 401) {
this.router.navigate(['/login'], {
queryParams: {
sessionFailed: true
}
});
}
return throwError(error);
}
}
AuthService
import {Injectable} from "#angular/core";
import {HttpClient, HttpHeaders} from "#angular/common/http";
import {Observable, of} from "rxjs";
import {RefreshTokens, Tokens, User} from "../interfaces";
import {map, tap} from "rxjs/operators";
#Injectable({
providedIn: 'root'
})
export class AuthService{
private authToken = null;
private refreshToken = null;
constructor(private http: HttpClient) {}
setToken(authToken: string) {
this.authToken = authToken;
}
setRefreshToken(refreshToken: string) {
this.refreshToken = refreshToken;
}
getToken(): string {
this.authToken = localStorage.getItem('auth-token');
return this.authToken;
};
getRefreshToken(): string {
this.refreshToken = localStorage.getItem('refresh-token');
return this.refreshToken;
};
isAuthenticated(): boolean {
return !!this.authToken;
}
isRefreshToken(): boolean {
return !!this.refreshToken;
}
refreshTokens(): Observable<any> {
const httpOptions = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + this.getRefreshToken()
})
};
return this.http.post<RefreshTokens>('/api2/auth/refresh', {}, httpOptions)
.pipe(
tap((tokens: RefreshTokens) => {
localStorage.setItem('auth-token', tokens.access_token);
localStorage.setItem('refresh-token', tokens.refresh_token);
this.setToken(tokens.access_token);
this.setRefreshToken(tokens.refresh_token);
console.log('Refresh token ok');
})
);
}
}
In your example you never subscribe to your refreshTokens().pipe() code. Without a subscription, the observable won't execute.
req = this.auth.refreshTokens().pipe(
switchMap(() => req.clone({
setHeaders: {
Authorization: `Bearer ${this.auth.getToken()}`
}
}))
)
This will first call refreshToken and run the tap there, then emit request with the new this.auth.getToken(), note that accToken still have old value as the code is not rerun.
You have to do something like that:
const firstReq = cloneAndAddHeaders(req);
return next.handle(firstReq).pipe(
catchError(
err => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401 || err.status === 403) {
if (firstReq.url === '/api2/auth/refresh') {
auth.setToken('');
auth.setRefreshToken('');
this.router.navigate(['/login']);
} else {
return this.auth.refreshTokens()
.pipe(mergeMap(() => next.handle(cloneAndAddHeaders(req))));
}
}
return throwError(err.message || 'Server error');
}
}
)
);
The implementation of cloneAndAddHeaders should be something like this:
private cloneAndAddHeaders(request: HttpRequest<any>): HttpRequest<any> {
return request.clone({
setHeaders: {
Authorization: `YourToken`
}
});
}
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'm trying to Post data to rest API. I'm using Postman for my rest API.
Getting Cannot read property 'subscribe' of undefined on POST HTTP Call in console log:
My rest API: "dradiobeats.x10host.com/api/areas"
userService.ts:
import { Injectable, Input } from "#angular/core";
import { HttpClient, HttpHeaders } from "#angular/common/http";
import { userArray } from "./users.model";
import { Observable } from "rxjs";
const httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
Authorization:
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImYyOTc3OTBmODc3ODlhYzg3MGE2ZmU3YTY0YzY2YmIwOGU4M2Q0ZmQzY2IyNmNiNWU3NDEzMTFmZjExMDk4NTA5NWUzN2IxN2I5YmI2YmFjIn0.eyJhdWQiOiIyIiwianRpIjoiZjI5Nzc5MGY4Nzc4OWFjODcwYTZmZTdhNjRjNjZiYjA4ZTgzZDRmZDNjYjI2Y2I1ZTc0MTMxMWZmMTEwOTg1MDk1ZTM3YjE3YjliYjZiYWMiLCJpYXQiOjE1NzU4NzM4MzksIm5iZiI6MTU3NTg3MzgzOSwiZXhwIjoxNjA3NDk2MjM5LCJzdWIiOiIyIiwic2NvcGVzIjpbXX0.J3nMXcPpqlRVvIRkrVAMblSUwdlXFmrkn9SPD2WE1DwdiqAMdhay8zAeD550ta9qWiNxHOKMAWF8t3H9cIgItaB9ZX2CxoxzS5P1nJFzit8qxiB-gzJL3mpybrnLtrKGjxsM5i_lBvdJsnhWzi15jKWIu-RNxUYPnXCjuxnXYEiyoJg17hsYUh4910VfFWx4R3WvH7WOvczF53IDKyX5fSTt4WSJUqciuNepkO6Klc8sj_yPmDPQltUjUXSSplkOQ8sL5uHk7PmzSjIfoR8RC0A-YQqI9mbZMTyJ0IyKoAHvRHF8q1cW5qfUmLXTgxcCTmFPqXqIlcAoOoJMCxke5fl0PuK0rgU7dxouATk_3B6cio7-7Zgps0iopDpk2nm-o40mjSiOUGb2kyKckYN09orYuan5wEd1KJ873ejKEgBWOhJu4gQFps8M9VoDXncAqMxeBqbUY1UZENx_n6uduQ_SAY4rgIUFCixfNc5Y_-HLDa108u4-z3APGbdxrhEdZXyHz9xQTaLrWcU_iCJ5g_ObT5VGZHtawZbfOYm2ZZpjPiCZpXunhrsbAcHBX64akWcehmT2gUJqPsxvaObKN3nayML1NHtdZGgAHUE89clhIH610Fod0C_jMTqpU7IkY9aSU781HsQVlHNw3qGbTinWfYPDBG0Lkp9NnmRe9BU",
Accept: "application/json"
})
};
#Injectable({
providedIn: "root"
})
export class UsersService {
users$: userArray[];
apiUrl = "http://dradiobeats.x10host.com/api/areas";
delUrl = "http://dradiobeats.x10host.com/api/areas";
constructor(private _http: HttpClient) {}
getUsers() {
return this._http.get<userArray[]>(this.apiUrl);
}
deleteUser(id: userArray): Observable<userArray> {
const url = `${this.apiUrl}/${id}`;
console.log();
return this._http.delete<userArray>(url, httpOptions);
}
onSubmit(users$: userArray): Observable<userArray> {
console.log(users$);
this._http.post<userArray>(this.apiUrl, users$, httpOptions);
}
}
add-post.component.ts:
import { Component, OnInit } from "#angular/core";
import { UsersService } from "src/app/users.service";
import { userArray } from "src/app/users.model";
#Component({
selector: "app-add-posts",
templateUrl: "./add-posts.component.html",
styleUrls: ["./add-posts.component.css"]
})
export class AddPostsComponent implements OnInit {
name: string;
description: string;
domain: string;
picture: string;
id: number = 29;
constructor(private userService: UsersService) {}
users: userArray[];
ngOnInit() {}
onSubmit() {
const users$ = {
name: this.name,
description: this.description,
domain: this.domain,
picture: this.picture
};
this.userService.onSubmit(users$).subscribe();
}
}
Can someone please help?
You need to return the http call observable
Try:
onSubmit(users$: userArray): Observable<userArray> {
console.log(users$);
return this._http.post<userArray>(this.apiUrl, users$, httpOptions);
}
As onSubmit() has an Observable signature, it must return an observable. You must change your function to 'return this.http.post ...'
You forgot to return, As only observable can be subscribe
import { Injectable, Input } from "#angular/core";
import { HttpClient, HttpHeaders } from "#angular/common/http";
import { userArray } from "./users.model";
import { Observable } from "rxjs";
const httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
Authorization:
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImYyOTc3OTBmODc3ODlhYzg3MGE2ZmU3YTY0YzY2YmIwOGU4M2Q0ZmQzY2IyNmNiNWU3NDEzMTFmZjExMDk4NTA5NWUzN2IxN2I5YmI2YmFjIn0.eyJhdWQiOiIyIiwianRpIjoiZjI5Nzc5MGY4Nzc4OWFjODcwYTZmZTdhNjRjNjZiYjA4ZTgzZDRmZDNjYjI2Y2I1ZTc0MTMxMWZmMTEwOTg1MDk1ZTM3YjE3YjliYjZiYWMiLCJpYXQiOjE1NzU4NzM4MzksIm5iZiI6MTU3NTg3MzgzOSwiZXhwIjoxNjA3NDk2MjM5LCJzdWIiOiIyIiwic2NvcGVzIjpbXX0.J3nMXcPpqlRVvIRkrVAMblSUwdlXFmrkn9SPD2WE1DwdiqAMdhay8zAeD550ta9qWiNxHOKMAWF8t3H9cIgItaB9ZX2CxoxzS5P1nJFzit8qxiB-gzJL3mpybrnLtrKGjxsM5i_lBvdJsnhWzi15jKWIu-RNxUYPnXCjuxnXYEiyoJg17hsYUh4910VfFWx4R3WvH7WOvczF53IDKyX5fSTt4WSJUqciuNepkO6Klc8sj_yPmDPQltUjUXSSplkOQ8sL5uHk7PmzSjIfoR8RC0A-YQqI9mbZMTyJ0IyKoAHvRHF8q1cW5qfUmLXTgxcCTmFPqXqIlcAoOoJMCxke5fl0PuK0rgU7dxouATk_3B6cio7-7Zgps0iopDpk2nm-o40mjSiOUGb2kyKckYN09orYuan5wEd1KJ873ejKEgBWOhJu4gQFps8M9VoDXncAqMxeBqbUY1UZENx_n6uduQ_SAY4rgIUFCixfNc5Y_-HLDa108u4-z3APGbdxrhEdZXyHz9xQTaLrWcU_iCJ5g_ObT5VGZHtawZbfOYm2ZZpjPiCZpXunhrsbAcHBX64akWcehmT2gUJqPsxvaObKN3nayML1NHtdZGgAHUE89clhIH610Fod0C_jMTqpU7IkY9aSU781HsQVlHNw3qGbTinWfYPDBG0Lkp9NnmRe9BU",
Accept: "application/json"
})
};
#Injectable({
providedIn: "root"
})
export class UsersService {
users$: userArray[];
apiUrl = "http://dradiobeats.x10host.com/api/areas";
delUrl = "http://dradiobeats.x10host.com/api/areas";
constructor(private _http: HttpClient) {}
getUsers() {
return this._http.get<userArray[]>(this.apiUrl);
}
deleteUser(id: userArray): Observable<userArray> {
const url = `${this.apiUrl}/${id}`;
console.log();
return this._http.delete<userArray>(url, httpOptions);
}
onSubmit(users$: userArray): Observable<userArray> {
console.log(users$);
return this._http.post<userArray>(this.apiUrl, users$, httpOptions);
}
}
I'm using angular 6 and have a class to join my API tokens to every http request. When getIdToken() returns successfully everything will be ok, but if it returns unsuccessfully, my app will be stop.
How can i handle mergeMap function when getToken gets failed?
I am confused about handling mergeMap observable function.
This is my class :
import { Injectable } from '#angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,
HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry';
import 'rxjs/add/operator/mergeMap';
import { of } from 'rxjs';
import { UserService } from '../user/user.service';
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
private token;
constructor(private userService: UserService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.userService.getToken().mergeMap(
(token) => {
request = request.clone({ headers: request.headers.set('Authorization', token) });
return next.handle(request);
}
);
}
}
And these are my getToken functions from userService class:
public async getIdToken() {
if (this.getCurrentUser() !== null) {
try {
const session = await this.getCurrentUserSession();
return session.getIdToken().getJwtToken();
} catch (err) {
return Promise.reject(err);
}
} else {
return Promise.reject('No Current User');
}
}
public getToken(): Observable<any> {
return Observable.fromPromise(this.getIdToken());
}
Try adding a catch right before your mergeMap like this :
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.userService.getToken()
.catch(error => // manage your error here)
.mergeMap(
(token) => {
request = request.clone({ headers: request.headers.set('Authorization', token) });
return next.handle(request);
}
);
}
i have an anagular service called authservice ,which looks like this:
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { JwtHelperService} from '#auth0/angular-jwt';
#Injectable()
export class AuthService {
public DecdedToken: any;
userToken: any;
public userid: any;
helper: any = new JwtHelperService();
baseUrl = 'http://localhost:5000/api/auth/';
// userToken: any;
constructor(private http: Http , private jwthelpservicee: JwtHelperService) {}
login(model: any) {
return this.http.post(this.baseUrl + 'login', model, this.requestOptions()).map((response: Response) => {
const user = response.json();
if (user && user.stringToken) {
this.userToken = user.stringToken;
localStorage.setItem('token', user.stringToken);
this.DecdedToken = this.helper.decodeToken(user.stringToken);
this.userid = this.DecdedToken.nameid;
// console.log(this.userid);
// onsole.log('so far so good');
}
}).catch(this.HandleError);
}
register(model: any) {
return this.http.post(this.baseUrl + 'register', model, this.requestOptions()).catch(this.HandleError);
}
private requestOptions() {
const headers = new Headers({ 'Content-type': 'application/json' });
return new RequestOptions({ headers: headers });
}
IsLoggedIn() {
return !this.jwthelpservicee.isTokenExpired();
}
private HandleError(error: any) {
const applicationerror = error.headers.get('Application-Error');
if (applicationerror) {
return Observable.throw(applicationerror);
}
const serverError = error.json();
let modelStateErrors = '';
if (serverError) {
for (const key in serverError) {
if (serverError[key]) {
modelStateErrors += serverError[key] + '\n';
}
}
}
return Observable.throw(modelStateErrors || 'server error');
}
}
in login method DecodedToken gets its value.
i have a resolver too, in which i am trying to get a value of a property from authsercvice
import { Resolve, Router, ActivatedRouteSnapshot } from '../../../node_modules/#angular/router';
import { User } from '../_Models/user';
import { Injectable } from '../../../node_modules/#angular/core';
import { UserService } from '../_services/User.service';
import { AlertifyService } from '../_services/alertify.service';
import { Observable } from '../../../node_modules/rxjs';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import { AuthService } from '../_services/auth.service';
#Injectable()
export class MemberEditResolver implements Resolve<User> {
constructor(private userservice: UserService ,
private router: Router ,
private alertify: AlertifyService,
private authservice: AuthService) {
}
resolve(route: ActivatedRouteSnapshot): Observable<User> {
console.log(this.authservice.userid);
return this.userservice.getuser(this.authservice.DecdedToken.nameid).catch(error => {
this.alertify.error('error');
this.router.navigate(['/members']);
return Observable.of(null);
});
}
}
as you can see i have defined DecdedToken as a public property, inside the authservice this property has value, but when i want to access it from resolver ,i get null or undefined...
i try to call a service's function(loginFb in auth.service.ts) from a component (fb.component.ts).
It seems that i imported everything and init the service. but still getting a 'loginfb' undefined error.
my auth.service.ts:
import { Injectable } from '#angular/core';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class AuthService {
constructor(private http: Http) {}
loginFb(uid, accessToken): boolean{
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let body = JSON.stringify({"uid":uid,"accessToken": accessToken});
this.http.post('http://localhost:3002/signinfb',body,{headers:headers})
.toPromise()
.then(response => {
localStorage.setItem('id_token', response.json().id_token);
return true;
})
.catch(this.handleError);
return false;
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
in my fb.component.ts:
import { Component, OnInit } from '#angular/core';
import { Router } from "#angular/router";
import { AuthService } from './auth.service';
declare const FB:any;
#Component({
selector: 'facebook-login',
providers: [AuthService],
template: `
<div>
<button class="btn" (click)="onFacebookLoginClick()">
Sign in with Facebook
</button>
</div>
`,
})
export class FacebookLoginComponent implements OnInit{
constructor(private authService: AuthService) {
}
ngOnInit() {
FB.init({
appId : '234244113643991',
cookie : false,
xfbml : true,
version : 'v2.7'
});
}
statusChangeCallback(response) {
if (response.status === 'connected') {
let uid = response.authResponse.userID;
let accessToken = response.authResponse.accessToken;
// window.alert(uid+"|"+accessToken);
if (this.authService.loginFb(uid,accessToken)){
window.alert("GOOD!");
}else{
}
}else if (response.status === 'not_authorized') {
}else {
}
}
onFacebookLoginClick() {
FB.login(this.statusChangeCallback,
{scope: 'public_profile,email,user_friends,'});
}
}
I am getting:
Subscriber.ts:241 Uncaught TypeError: Cannot read property 'loginFb' of undefined(…)
Can someone help me with this problem? thanks!
You need to bind this to the proper context with statusChangeCallback
FB.login(this.statusChangeCallback.bind(this),
or make statusChangeCallback an arrow function
statusChangeCallback = (response) => {