I am trying to add facebook login feature for my web app. Which is being developed in express js, mongodb and angular 2.
Client part of the app is generated through angular-cli (which is running on port 4200). To connect it to the express app (which is running on port 4300) I am using proxy config provided by angular-cli itself.
I have searched web and nothing is working for me. Please help me out.
This is the Error I am getting while clicking on facebook login button
XMLHttpRequest cannot load https://www.facebook.com/dialog/oauth?response_type=code&redirect_uri=http%…alhost%3A4200%2Fapi%2Fuser%2Ffacebook%2Fcallback&client_id=404659989876073. Redirect from 'https://www.facebook.com/dialog/oauth?response_type=code&redirect_uri=http%…alhost%3A4200%2Fapi%2Fuser%2Ffacebook%2Fcallback&client_id=404659989876073' to 'https://www.facebook.com/login.php?skip_api_login=1&api_key=404659989876073…_&display=page&locale=en_GB&logger_id=9f59d18b-00cb-48a9-a544-7e49a66acfe4' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
I can understand that this is CORS issue, I have searched a lot of place but all the solutions which are available online are not working for me. Like allowing cross origin in express, using third party library for it etc.
Here is my app.ts
import * as express from 'express';
import { json, urlencoded } from 'body-parser';
import * as path from 'path';
import * as cors from 'cors';
import * as compression from 'compression';
import * as mongoose from 'mongoose';
import * as ejs from 'ejs';
import * as passport from 'passport';
import * as session from 'express-session';
import { MONGODB_URI } from './config';
import { AuthConfig } from './api/auth/passport';
import { thingRouter } from './api/thing/';
import { userRouter } from './api/user';
const app: express.Application = express();
app.disable('x-powered-by');
app.use(json());
app.use(compression());
app.use(urlencoded({ extended: true }));
app.use(session({ secret: 'my-secret-key', resave: true, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
mongoose.connect(process.env.MONGODB_URI || MONGODB_URI);
// allow cors only for local dev
app.use(cors({
origin: 'http://localhost:4200'
}));
AuthConfig(passport);
// app.set('env', 'production');
// api routes
app.use('/api/thing', thingRouter);
app.use('/api/user', userRouter);
if (app.get('env') === 'production') {
// in production mode run application from dist folder
app.use(express.static(path.join(__dirname, '/../client')));
app.engine('html', ejs.renderFile);
app.set('view engin', 'html');
}
// catch 404 and forward to error handler
app.use(function (req: express.Request, res: express.Response, next) {
res.render(path.join(__dirname, '/../client/index.html'));
});
// production error handler
// no stacktrace leaked to user
app.use(function (err: any, req: express.Request, res: express.Response, next: express.NextFunction) {
res.status(err.status || 500);
res.json({
error: {},
message: err.message
});
});
export { app }
passport.ts
let LocalStrategy = require('passport-local').Strategy;
let FacebookStrategy = require('passport-facebook').Strategy;
import { FacebookAuth } from '../../config';
import { User } from '../user/user.model'
import * as uuid from 'uuid';
let passportConfig = function (passport) {
passport.serializeUser((user, done) => {
console.log('serializeing user');
done(null, user.id);
});
passport.deserializeUser((id, done) => {
console.log('deserializing user');
User.findOne({ id: id }, (err, user) => {
done(err, user);
})
});
// Facebook Strategy
passport.use(new FacebookStrategy({
clientID: FacebookAuth.clientId,
clientSecret: FacebookAuth.clientSecret,
callbackURL: FacebookAuth.callbackURL // for my app - 'http://localhost:4200/api/user/facebook/callback'
},
(token, refreshToken, profile, done) => {
console.log('in passport.js'); //never printed
console.log('token', token); //never printed
console.log('refreshToken', refreshToken); //never printed
console.log('profile', profile); // never printed
User.findOne({ 'facebook.id': profile.id}, (err, user) => {
if (err) { return done(err) }
if (user) { return done(null, user) }
let newUser = new User();
newUser.id = uuid.v4();
newUser.facebook.id = profile.id;
newUser.facebook.token = token;
newUser.name = profile.name.givenName + ' ' + profile.name.familyName;
newUser.email = profile.emails[0].value;
newUser.save(err => {
if (err) { return done(err) }
return done(null, newUser);
})
})
}));
}
export let AuthConfig = passportConfig;
route file
import { Router } from 'express';
import { UserController } from './user.controller';
const userRouter: Router = Router();
const controller: UserController = new UserController();
userRouter.get('/facebook', controller.facebook);
userRouter.get('/facebook/callback', controller.facebookCallback);
export { userRouter };
User controller file
import { Router, Response, Request, NextFunction } from 'express';
import * as passport from 'passport';
import * as uuid from 'uuid';
import { User } from './user.model';
export class UserController {
constructor() { }
facebook(req: Request, res: Response, next: NextFunction) {
console.log('got the request in facebook()'); //this get printed
passport.authenticate('facebook')(req, res, next);
}
facebookCallback(req: Request, res: Response, next: NextFunction) {
console.log('got the call in facebookCallback()');
passport.authenticate('facebook', (err, user) => {
console.log('passport callback\'s callback');
if (err) {
return res.status(200)
.json({
title: 'error',
data: err
})
}
return res.status(200)
.json({
title: 'logged in',
data: user
})
})
}
}
**HTML for facebook icon with click event **
<div class="col-sm-2 col-sm-offset-4 fb" (click)="facebookLogin()">
<i class="fa fa-facebook-square"></i>
</div>
Angular 2 component (function) which is working fine
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ToastyService, ToastyConfig, ToastOptions, ToastData } from 'ng2-toasty';
import { AuthService } from './../../services/auth/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less']
})
export class LoginComponent implements OnInit {
constructor(private toastyService: ToastyService,
private router: Router,
private toastyConfig: ToastyConfig,
private authService: AuthService) {
this.toastyConfig.theme = 'bootstrap';
}
ngOnInit() {
}
facebookLogin() {
this.authService.facebookLogin()
.subscribe(
data => {
console.log('login.component data', data);
},
err => {
console.log('login.component err', err);
}
)
}
}
Auth Service
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/Rx';
@Injectable()
export class AuthService {
constructor(private http: Http) {
}
facebookLogin() {
return this.http.get('/api/user/facebook')
.map(response => response.json())
.catch(err => Observable.throw(err.json));
}
}
Try using Link like http://localhost:port/api/users/facebook and set header import header and set header.append('Content-Type','application/json');