Unable to get SSO working for NodeJS in BlueMix

2019-09-14 02:07发布

问题:

Here's the error I keep getting "CWOAU0062E: The OAuth service provider could not redirect the request because the redirect URI was not valid. Contact your system administrator to resolve the problem."

var express = require('express');

// Add for SSO
var cookieParser = require('cookie-parser');
var session = require('express-session');
var passport = require('passport');
var OpenIDConnectStrategy = require('passport-idaas-openidconnect').IDaaSOIDCStrategy;
var redis = require('redis');
var RedisStore = require('connect-redis')(session);

// cfenv provides access to your Cloud Foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');
// get the app environment from Cloud Foundry
var appEnv = cfenv.getAppEnv();

// create a new express server
var app = express();
var services = JSON.parse(process.env.VCAP_SERVICES || null);
// get configuration for redis backing service and connect to service
var redisConfig = appEnv.getService(/Redis.*/);
if(redisConfig == null) {
  console.log('ERROR: Failed to create REDDISCONFIG!!!');
} else {
  var redisPort = redisConfig.credentials.port;
  var redisHost = redisConfig.credentials.hostname;
  var redisPasswd = redisConfig.credentials.password;

  var redisclient = redis.createClient(redisPort, redisHost, {no_ready_check: true});
  redisclient.auth(redisPasswd, function (err) {
      if (err) {
        throw err;
      }
  });

  redisclient.on('connect', function() {
      console.log('Connected to Redis');
  });
}

// define express session services, etc for SSO
app.use(cookieParser());
// app.use(session({resave: 'true', saveUninitialized: 'true' , secret: 'keyboard cat'}));
if(redisConfig != null) {
  app.use(session({
    store: new RedisStore({ client: redisclient }),
    resave: 'true',
    saveUninitialized: 'true',
    secret: 'top secr8t'
  }));
}

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser(function(user, done) {
   done(null, user);
});

passport.deserializeUser(function(obj, done) {
   done(null, obj);
});

// find config object for the SSO services from VCAP_SERVICES through cfenv/appEnv
var ssoConfig = services.SingleSignOn[0];
//appEnv.getService(/Single Sign On.*/)
if(ssoConfig == null) {
  console.log('ERROR: Failed to instantiate SSOCONFIG. Its not available!!!');
} else {
  var client_id = ssoConfig.credentials.clientId;
  var client_secret = ssoConfig.credentials.secret;
  var authorization_url = ssoConfig.credentials.authorizationEndpointUrl;
  var token_url = ssoConfig.credentials.tokenEndpointUrl;
  var issuer_id = ssoConfig.credentials.issuerIdentifier;
}
// you MUST change the host route to match your application name
// var callback_url = 'https://scaleSSO-TOR0815.mybluemix.net/auth/sso/callback';
var callback_url = 'https://krishnodejs.mybluemix.net/auth/sso/callback';

var OpenIDConnectStrategy = require('passport-idaas-openidconnect').IDaaSOIDCStrategy;
var Strategy = new OpenIDConnectStrategy({
                 authorizationURL : authorization_url,
                 tokenURL : token_url,
                 clientID : client_id,
                 scope: 'openid',
                 response_type: 'code',
                 clientSecret : client_secret,
                 callbackURL : appEnv.url + '/auth/sso/callback',
                //  callbackURL : callback_url,
                 skipUserProfile: true,
                 issuer: issuer_id},
    function(accessToken, refreshToken, profile, done) {
                process.nextTick(function() {
        profile.accessToken = accessToken;
        profile.refreshToken = refreshToken;
        done(null, profile);
            })
});

passport.use(Strategy);
app.get('/login', passport.authenticate('openidconnect', {}));

function ensureAuthenticated(req, res, next) {
    if(!req.isAuthenticated()) {
      // req.session.originalUrl = 'https://krishnodejs.mybluemix.net';
        res.redirect('/login');
    } else {
        return next();
    }
}

app.get('/auth/sso/callback',function(req,res,next) {
        var redirect_url = 'https://krishnodejs.mybluemix.net/hello'; 
      // req.session.originalUrl;
            passport.authenticate('openidconnect',{
                 successRedirect: redirect_url,
                 failureRedirect: '/failure',
          })(req,res,next);
        });


app.get('/hello', ensureAuthenticated, function(req, res) {
  res.send('Hello, '+ req.user['id'] + '!'); }
);

app.get('/failure', function(req, res) {
             res.send('login failed'); });

// serve the files out of ./public as our main files
app.use(express.static(__dirname + '/public'));

// start server on the specified port and binding host
app.listen(appEnv.port, function() {

    // print a message when the server starts listening
  console.log("server starting on " + appEnv.url);
});

I have the following URL in return-URL settings of SSO "https://krishnodejs.mybluemix.net/hello"

Any advice suggestion to fix is more than welcome.

Redirection URL that fails has my callback URL right, except for the weird &scope=openid....but I guess, that may not be a problem

I looked into server side logs for error. But there were none. Leaves me no clue to where the problem is

"https://ssotest-gx1592z76o-cl12.iam.ibmcloud.com/idaas/oidc/endpoint/default/authorize?response_type=code&client_id=EdzctxPuQ4&redirect_uri=https://krishnodejs.mybluemix.net/auth/sso/callback&scope=openid openid"

回答1:

For people who have similar issues, watch out for redirect_url parameter that comes back from BlueMix on login.

In my case I was setting this URL in 2 places

  • var callback_url = 'https://krishnodejs.mybluemix.net/auth/sso/callback', in code
  • "Configure Application" section in BlueMix SSO Integrate page, in UI

Although, both the links point to the same URL, for some reason application picks the one from code. The way I was able to figure that out was from what came back in parameter callback_url (as above) First, redirect URL we provide in code need not be a complete URL. In my case it should have been 'bluemix/callback'. I fixed that. Did it work, nope. But things moved from one issue to another. One forward step, if I can say so. What was the next issue? URL was coming back right, but the protocol was not. Instead of https, it was always returned http, so the page was still not loading. Finally, I got rid of setting this URL from code and drive the entire stuff from URL in UI. I set URL in "Configure application" to "https://krishnodejs.mybluemix.net/auth/bluemix/callback". If you are wondering, what my code looked like, after these changes, here you go

var OpenIDConnectStrategy = require('passport-idaas-openidconnect').IDaaSOIDCStrategy;
var OpenIDStrategy = new OpenIDConnectStrategy({
    authorizationURL : authorization_url,
    tokenURL : token_url,
    clientID : client_id,
    scope: 'openid',
    response_type: 'code',
    clientSecret : client_secret,
    // callbackURL : callback_url,
    skipUserProfile: true,
    issuer: issuer_id
}, function(accessToken, refreshToken, profile, done) {
    process.nextTick(function() {
        profile.accessToken = accessToken;
        profile.refreshToken = refreshToken;
        done(null, profile);
    });
});
passport.use(OpenIDStrategy);

}

As you can see I commented out the code that passes callback_url. Bingo, everything worked. Lesson learnt: Watch for 'redirect_url'. If the URL comes back wrong, then you are missing to set it right, either in code or in UI. My next attempt will be to not set it at all in UI and completely drive it from code. For the moment thought I am past this problem and I can move on. Hope this helps.



回答2:

CWOAU0062E: The OAuth service provider could not redirect the request because the redirect URI was not valid. Contact your system administrator to resolve the problem. typically indicates that in the Return-to URL has not been correctly configured in the Single Sign On service.

So please update the Single Sign On's Return-to URL under the Integrate section of the service so that it matches the callback_url in your code and then restarting the App