How to login user with Meteor in Server side after Receive A callback from IDP

383 Views Asked by At

I need authenticate a user using passport-saml v2.0 and login after the authenticaton callback successful responds to IDP post request.

I’am using React in front-end with flow router and Picker in back-end.

I Start the authentication in client side when i click in a button and this create a window with make a request to /auth/saml.

The code work, and my user is successful added to mongodb, but now, after the userHandler function return the userId and the token i want to use Meteor.loginWithToken to login, but this function don’t work in server side.

The main problem is use the user information retrived by the IDP in callback function and login using Meteor with that information.

Server side: Code to initiate the authentication using passport js and passport-saml strategy

import { Picker} from 'meteor/meteorhacks:picker';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import passport from 'passport';
import saml from 'passport-saml';
import expressSession from 'express-session';

Picker.middleware(bodyParser.json());
Picker.middleware(bodyParser.urlencoded({extended:false}));
Picker.middleware(cookieParser());
Picker.middleware(expressSession({
  secret: 'secret', 
  resave: false, 
  saveUninitialized: true,
}));

var samlStrategy = new saml.Strategy({
    callbackUrl: process.env.ROOT_URL + '/login/callback', //call back in service provider to handle with the IDP response
    entryPoint: 'http://localhost:8080/simplesaml/saml2/idp/SSOService.php', //link for IDP
    issuer: 'saml-poc', //id da entidade provedora
    identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', //formato requisitado pela entidade
    decryptionPvk: process.env.CERTPVK,
    privateCert: process.env.CERTPVK,
    cert: process.env.IDPKEY,
    validateInResponseTo: false,
    disableRequestedAuthnContext: true,
}, function(profile, done) {
    return done(null, profile);
});

passport.use('samlStrategy', samlStrategy);
Picker.middleware(passport.initialize({}));
Picker.middleware(passport.session({}));

//serialize the user
passport.serializeUser(function(user, done) {
  console.log('-----------------------------');
  console.log('serialize user');
  console.log(user);
  console.log('-----------------------------');
  done(null, user);
});

//deserialize
passport.deserializeUser(function(user, done) {
  console.log('-----------------------------');
  console.log('deserialize user');
  console.log(user);
  console.log('-----------------------------');
  done(null, user);
});

Picker.route('/auth/saml', 
  function(params, req, res, next) {
    console.log('Start login handler');
    passport.authenticate('samlStrategy', {
      session: false,
    }, 
    function(err, user, info) {
      if (err) {
        console.log("Error1");
        return next(err);
      }
      if (!user) {
        console.log("Error2");
        return res.json(401, {
            error: 'Auth Error!'
        });
      }
    }
    )(req, res, next);
  }
);

Picker.route('/auth/callback',
  function (params, req, res, next) {
    console.log('-----------------------------');
    console.log('/Start login callback ');
    passport.authenticate('samlStrategy', {
      session: false,
  },
  async function (err, user, info) {
    if (err) {
      return next(err);
    }
    if (!user) {
      return res.json(401, {
          error: 'Auth Error!'
      });
    }

    const {userId, token} = await userHandler(user);

    res.write("<script>window.close();</script>");
    res.flushHeaders();
    res.end('Sucess');

  })(req, res, next);
});

const userHandler = (user) => new Promise((resolve, reject) => {

  let usr = Accounts.findUserByEmail(user.email);
  const company = "company";
  const profile = { name: "Vinicius França" };
  const stampedToken = Accounts._generateStampedLoginToken();

  if(!usr){
    Meteor.call('adminUser_addNewUser', user.email, null, profile, company, "user", undefined, undefined, true, (err,res) => {
      if(!res){
        SweetAlert('OPS...', 'Error', 'error');
      }
      Accounts.findUserByEmail(user.email).then((_usr) => {
        Accounts._insertLoginToken(_usr._id, stampedToken);
        resolve({userId: _usr._id, token: stampedToken});
      })
    });
  } else {
    Accounts._clearAllLoginTokens(usr._id);
    Accounts._insertLoginToken(usr._id, stampedToken);
    resolve({userId: usr._id, token: stampedToken});
  }
});

Client side:

logginSAML() {
    this.setState({logginSSO: true});
    let authWindow = window.open(
      window.location.href + "/auth/saml",
      "Window SSO",
      "resizable=no,scrollbars=yes,status=yes",
      "height=1000px",
      "width=100px",
      "_blank"
    );
    const _this = this;

    window.onunload = window.onbeforeunload = (e) => {
      //is not working
      //call login with token here
      _this.setState({logginSSO: false});
      console.log('q loucura man');
      e.returnValue = '';
      delete e['returnValue'];
    };
  }
1

There are 1 best solutions below

0
On

I solved the problem using BroadCastChannel from javascript to send user information as workaround, consequently, loged in client side using Meteor methods.