Facebook Login Yielding URL: Blocked (not whitelisted) - angular

I signed up for Facebook Social login and I am using v2.10 of the API.
I can get the login window to open up just fine, but I get the following error.
URL blocked: This redirect failed because the redirect URI is not
white-listed in the app's client OAuth settings. Make sure that the
client and web OAuth logins are on and add all your app domains as
valid OAuth redirect URIs.
I have tried everything to get it working, but can't seem to crack the issue.
** Environment
Windows 10
Angular 5
Node 8.10
Pingable at https://localhost:5000 and https://scormify.app
What I have tried:
Adding all possible variations of the redirect uri. Including the uri listed in the window. (&redirect_uri=https://staticxx.facebook.com/connect/xd_arbiter/r/FdM1l_dpErI.js?version=42#cb=f2332406d58ffb)
Per This issue, added a hostfile configuration to redirect to a non-localhost
Tried to track down any debugging information. Tried the network inspector to see if it was reaching out anywhere, etc.
Read through the snark in this issue
Consulted this article
Double checked domains under the top-level app settings.
Image showing all variations of the callback url, including the faux domain entered into the host file and the redirect uri from the window (staticxx....) and the actual localhost callback url.
host file
127.65.43.21 scormify.app (also did netsh detailed here)
FB SDK Init FYI only, dont think this has anything to do with it.
```
window.fbAsyncInit = function () {
FB.init({
appId: 'xxx',
cookie: true,
xfbml: true,
version: 'v2.10'
});
FB.AppEvents.logPageView();
};
```
Angular 5 Svc Code I don't think this is the problem either, FYI only.
linkFb() {
return FB.login(result => {
if (result.authResponse) {
return this.http.post(`https://localhost:5000/api/v1/account/link/fb/callback`, { access_token: result.authResponse.accessToken }).map((res: ApiResponse) => res)
}
}, { scope: 'public_profile,email' });
}
Routes
.post(
'/account/link/fb',
passport.authenticate('facebook-token', { session: false }),
account.linkFb
)
.get('/account/link/fb/callback', account.linkFbCb)
Passport Strategy
passport.use(
new FacebookStrategy(
{
clientID: config.FACEBOOK_APPID,
clientSecret: config.FACEBOOK_SECRET,
callbackURL: `https://localhost:5000/api/v1/account/link/fb/callback`,
profileFields: ['name', 'email', 'link'],
passReqToCallback: true
},
(req, accessToken, refreshToken, profile, done) => {
console.log(req);
console.log(profile);
// make profile;
// make provider
//store.upsertUser();
}
)
);
};

Related

Angular/Express/Passport - Authenticating with Google: No 'Access-Control-Allow-Origin

Context
I am building a stateless application using Angular, Express & PassportJS and want to authenticate users using their Google account. After authenticating a user, my goal is to use JWT tokens to be able to have a stateless application.
Angular 2 side
Upon clicking the Google sign in button, the following code gets executed in my service:
login() {
return this.http.get('http://localhost:3000/api/auth/google');
}
Express side
On express, the following gets executed:
// This gets executed first after clicking the sign in using Google button.
router.get('/api/auth/google', passport.authenticate('google', {
session: false,
scope: ['profile', 'email']
}));
// Google sends me back here (callback).
passport.use(new GoogleStrategy({
clientID: 'omitted',
clientSecret: 'omitted',
callbackURL: '/api/auth/google/callback'
}, function (accessToken, refreshToken, profile, cb) {
// Here, I obtain the profile and create a JWT token which I send back to the user.
let jwtToken = generateToken(); // Code omitted for simplicity.
cb(null, jwtToken); // assume success
}
));
// After creating a JWT token, this will get executed.
router.get('/api/auth/google/callback', (req, res, next) => {
passport.authenticate('google', (err, jwtToken) => {
res.status(201).json(jwtToken);
})(req, res);
});
Error:
After clicking the 'sign in with Google' button (i.e. making the get request from Angular), I receive the following error:
Failed to load https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=(long_string_here): No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
What I have tried
I have seen similar issues on Angular and React applications and while there are answers (which I have tried), there has been no 'accepted answer' that actually solves this issue. I'm truly appalled why this is such a problem:
The strange part is: if I directly call window.location.href = http://localhost:3000/api/auth/google instead of making a get request, it works fine. However, then the issue is that I am not able to obtain my JWT token as I am being redirected completely.
I have also tried to enabled CORS in many different ways in the backend and allowed all kinds of origins. This does not change anything.
An important note here is that my Angular application is running on port 4200 and my Express application on port 3000. However, even if I compile my Angular project and put it in the public folder of Express and serve it, I still get the same CORS issues!
call like this from login,
flogin(){
window.open('/auth/facebook',"mywindow","location=1,status=1,scrollbars=1, width=800,height=800");
let listener = window.addEventListener('message', (message) => {
//message will contain facebook user and details
});
}
in server,
i'm hoping you've passport facebook-stratagy working
app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email'] }));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/auth/fail' }),
function(req, res) {
var responseHTML = '<html><head><title>Main</title></head><body></body><script>res = %value%; window.opener.postMessage(res, "*");window.close();</script></html>'
responseHTML = responseHTML.replace('%value%', JSON.stringify({
user: req.user
}));
res.status(200).send(responseHTML);
});

Display an error in Angular 2 after unsuccessful Passport.js OAuth authentication

Background
I have a MEAN (Angular 2) app with two routes for authentication. One that Google, for instance, uses as a callback and one that's used for initiating the authentication.
router.route('/google/callback')
.get(passport.authenticate('google',
{ successRedirect: '/profile', failureRedirect: '/signup', failureFlash: true }
));
router.route('/google')
.get(passport.authenticate('google',
{ scope: ['https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'] }
));
In this middleware, I check if the profile email that's returned from Google matches one in the database. If it matches, a user already exists so I want to go back to the sign up page and display an error message to the user.
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: `http://${process.env.HOST}:${process.env.PORT}/auth/google/callback`,
passReqToCallback: true
}, function(req, accessToken, refreshToken, profile, done) {
// Check if email address is already in DB
// If so, bubble up error to user
}));
Most of the routing is done in Angular and handled in Express like this:
app.get('/*', function(req, res) {
res.sendFile(path.resolve(__dirname + '/index.html'));
});
Question
How can I notify Angular that there was an error found in the above middleware so I can properly display a message?
Attempts
I tried using connect-flash but wasn't able to get the message on the client side.
I tried adding an error parameter to the URL but it either doesn't make it or it breaks the route because of the way routing is set up.

Issue on redux-oidc signoutRedirect

Question / Issue
I'm using Identity Server for Single Sign On, my Client application is in ReactJs embedded with Redux. I'm using redux-oidc npm node module to implement the Identity Server functionality as mentioned in https://github.com/IdentityModel/oidc-client-js/wiki
My oidc config is
var settings = {
authority: 'https://localhost:2025/core',
client_id: 'TVA',
silent_redirect_uri: 'http://localhost:3000/silent-renew.html',
post_logout_redirect_uri: 'http://localhost:3000',
redirect_uri: `http://localhost:3000/callback`,
response_type: 'id_token token',
scope: 'openid payroll',
acr_values: `tenant:Payroll`,
accessTokenExpiringNotificationTime: 4,
automaticSilentRenew: true,
filterProtocolClaims: true
};
If I use the in-built method signoutRedirect, its again redirecting to the application's initial landing page, but if I directly trigger the end session it redirects to the Identity Server Login Page (i.e., Once the application is successfully logout, the Identity Server redirects to the root URI and then it redirects to the Login page).
Issue: Its not Signout the application
userManager.signoutRedirect()
.catch(function (error) {
console.error('error while signing out user', error);
});
Fine: Signout's perfectly
const path = 'https://localhost:2025/core/connect/endsession?id_token_hint=${token}&post_logout_redirect_uri=http://localhost:3000';
document.location.href = path;
Kindly assist me how to signout using userManager.signoutRedirect() ???

node.js, passport-wordpress: The required “redirect_uri” parameter is missing

Trying to create a demo using passport-wordpress
https://www.npmjs.org/package/passport-wordpress
passport-wordpress allows you to login to a node.js app using your credentials at wordpress.com
I set up my Wordpress app at developer.wordpress.com/apps:
OAuth Information
Client ID <removed>
Client Secret <removed>
Redirect URL http://greendept.com/wp-pass/
Javascript Origins http://wp-node2.herokuapp.com
Type Web
Request token URL https://public-api.wordpress.com/oauth2/token
Authorize URL https://public-api.wordpress.com/oauth2/authorize
Authenticate URL https://public-api.wordpress.com/oauth2/authenticate
In my node.js app:
var CLIENT_ID = <removed>;
var CLIENT_SECRET = <removed>;
passport.use(new WordpressStrategy({
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ WordpressId: profile.id }, function (err, user) {
return done(err, user);
});
}
When I try to authorize, it goes to this URL (as one line, I've divided into two here for readability):
https://public-api.wordpress.com/oauth2/authorize?
response_type=code&redirect_uri=&client_id= removed
I can see that the redirect_uri is missing in that URL, so it's not surprising that I get this error:
Invalid request, please go back and try again.
Error Code: invalid_request
Error Message: The required "redirect_uri" parameter is missing.
Not sure where or how in my code I should be submitting the redirect_uri.
You need to pass a callback url as option.
From passport-wordpress
The strategy requires a verify callback, which accepts these credentials and
calls done providing a user, as well as options specifying a client ID,
client secret, and callback URL.
And from lib/strategy.js
Examples:
passport.use(new WordpressStrategy({
clientID: '123-456-789',
clientSecret: 'shhh-its-a-secret',
callbackURL: 'https://www.example.net/auth/wordpress/callback'
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate(..., function (err, user) {
done(err, user);
});
}
));

Using Express and Node, how to maintain a Session across subdomains/hostheaders

I have a single node server that responds to requests and redirects a user based on host headers. The usage is that the static/home site lives at www and each user has their own sub domain (i.e. www.example.com and site.example.com). The routing is as per site.js.
When the user is not logged in they are redirected to login.
I am discovering that the session is not maintained when the user is redirected to their sub domain. I guess this is expected, but I am wondering if there is a way to maintain the same session across both sub domains.
I was hoping that if they were logged in and returned to www.example.com they would see a different view that included a link to logout / their dashboard, etc. My workaround at the moment, I'm thinking, is to just create the session on their subdomain and if they do return to www it will just be as if they are not logged in.
Anyone dealt with this before or have answers on how to handle sessions in this manner?
I think the issue may be in users.js where I redirect to 'http://site.example.com' as its not a relative path...
Here is the relevant code (the user lookup is done using MongoDB and I've left it out as its working fine - the line that calls this service is users.authenticate)...
server.js:
app.configure ->
app.set "views", "#{__dirname}/views"
app.set "view engine", "jade"
app.use express.bodyParser()
app.use express.methodOverride()
app.use express.cookieParser()
app.use express.session {
key: "KEY",
secret: "SECRET",
store: new MemoryStore(),
cookie: {
domain: 'example.com',
maxAge : 1000*60*60*24*30*12
}
}
app.use express.static "#{__dirname}/public"
app.use express.logger "short"
app.use express.favicon "#{__dirname}/public/img/favicon.ico"
app.use app.router
site.js:
module.exports = (app) ->
app.get '/', (req, res) ->
console.log "/ hit with #{req.url} from #{req.headers.host}"
domains = req.headers.host.split "."
org = if domains then domains[0] else "www"
if org == "www"
res.render "index", { layout: null }
else
if req.session.user
console.log "session established"
res.render "app", { layout: null }
else
console.log "no session"
res.redirect "http://www.example.com/accounts/login"
users.js:
users = require('../services/users')
module.exports = (app) ->
app.get "/accounts/login", (req, res) ->
res.render "login", { layout: null, locals: { returnUrl: req.query.returnUrl } }
app.post "/accounts", (req, res) ->
users.authenticate app, req.body.login, req.body.password, (user) ->
if user
req.session.user = user
res.redirect "http://#{user.orgName}.example.com"
else
res.render "login", { layout: null, locals: { returnUrl: req.body.url } }
app.get "/accounts/logout", (req, res) ->
console.log "deleting user from session"
delete req.session.user
res.redirect "http://www.example.com
To test it locally on OSX, I have added www.example.com and site.example.com in to my hosts file so that the DNS lookups get handled locally.
First of all to allow browser to make cross-domain requests you need to set headers on server side. This solution works for normal request as well as AJAX.
In your express configure function:
Express 4.0:
var express = require('express');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.use(session({
secret: 'yoursecret',
cookie: {
path: '/',
domain: 'yourdomain.com',
maxAge: 1000 * 60 * 24 // 24 hours
}
}));
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});
Access-Control-Allow-Origin can be set to '*' if no cross-domain cookies exchange for sessions needed.
To have cookies and session shared cross-domain you need to set specific Access-Control-Allow-Origin to actually domain where request is made from, that's why req.headers.origin - is perfect for that.
Using domain it wont work well on localhost - so make sure you disable it in development environment, and enable on production. It will enable shared cookies across top and sub domains.
This is not all.
Browsers it self won't send cookies over cross domain requests, and this have to be forced.
In jQuery you can add extra parameter in $.ajax() request:
xhrFields: { withCredentials: true }
For non jQuery, just have XHR constructor and set this parameter:
xhr.withCredentials = true;
And you are ready to do cross-domain with shared session.
Did you make sure you have your cookies set to the top-level domain so it can be read by all subdomains? Then it's just a matter or persisting your session data in memory, a db, whatever as usual. I don't have my dev machine up and running, but it'll be something like this in your app.configure().
app.use(express.cookieParser());
app.use(express.session({
key: 'A_SESSION_KEY',
secret: 'SOMETHING_REALLY_HARD_TO_GUESS',
store: new express.session.MemoryStore,
cookie: {
path : '/',
domain : 'yourdomain.com',
httpOnly : true,
maxAge : 1000*60*60*24*30*12 //one year(ish)
}
}));
Note: If using Express 4 and the new cookie-session module, the code looks like
{
secret: <session_secret> ,
store: <session store> ,
domain: '.domain.com',
}
This bit me, but the API has changed.

Resources