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));
}
}
3
Answers
Try using Link like http://localhost:port/api/users/facebook
and set header
import header and set
header.append(‘Content-Type’,’application/json’);
This is what I have done to avoid Angular to call facebook auth dialogue API as AJAX request.
Use
'window.location="http://localhost:3000/auth/facebook"';
in your Angular Controller Function from where you send the request to your Express Server which containspassport.authenticate
stuff.It works for me!
I have exactly the same problem.
I tried to solve it the similar way amey discribed, but it ends with express creating two different sessions: one for authed client and one for other calls from frontside app.