SSO Endpoint without OAuth2orize



  • Hi everyone,

    I'm basically attempting to create a very simple OAuth2 Endpoint without using OAuth2orize, which is the plugin normally recommended. If I understood correctly, the whole process consists of:
    1 - Get authorization code
    2 - Get user token with authorization code
    3 - Get user info with the token

    My NodeBB plugin has the following config:

    constants = Object.freeze({
    			type: 'oauth2',	// Either 'oauth' or 'oauth2'
    			name: 'myplugin',	// Something unique to your OAuth provider in lowercase, like "github", or "nodebb"
    			oauth: {
    				requestTokenURL: '',
    				accessTokenURL: '',
    				userAuthorizationURL: '',
    				consumerKey: '',
    				consumerSecret: ''
    			},
    			oauth2: {
    				authorizationURL: 'http://localhost:3000/oauth',
    				tokenURL: 'http://localhost:3000/oauth/token',
    				clientID: 'abc',
    				clientSecret: '12345'
    			},
    			userRoute: 'http://localhost:3000/oauth/api'	// This is the address to your app's "user profile" API endpoint (expects JSON)
    		})
    

    And in my app, the following routes:

    // Used to render a simple form for the user to login
    app.all('/oauth', function(req, res){
      res.render('../views/oauth.ejs', {query : req.query})
    })
    
    // Redirects user to the callback with a token
    app.all('/oauth/token', function(req, res){
      var t = req.body.redirect_uri + '?code=' + req.body.code
      res.redirect(t)
    })
    
    // Finds the user with the access_token provided and returns its info
    app.all('/oauth/api', function(req, res){
      var userInfo = JSON.parse(req.query.access_token)
      Users.findOne({email: userInfo.email}, function(err, user){
        res.json({email: user.email, id: user._id, name: user.name})
      })
    })
    
    // POST received from the form logged by the user
    // --- pretty sure it's wrong 
    app.all('/oauth/authorize', function(req, res){
      Users.findOne({ email: req.body.email.toLowerCase()}, function(err, result) {
        if (isOk(err, null, res)) {
          if (!result || result.activation_state == "inactive") {
            log('Email not found')
            res.redirect('http://localhost:4567/login')
          } else {
            var password = req.body.password + result.salt
            if (bcrypt.compareSync(password, result.crypted_password)) {
              var t = "?code=" + JSON.stringify({email: result.email, name: result.first_name, id: result._id})
              res.redirect(req.body.url + t)
            } else {
              log('Wrong passwords')
              res.redirect('http://localhost:4567/login')
            }
          }
        }
      })
    })
    

    The flow above works like this:
    1 - User clicks on icon in NodeBB
    2 - User gets redirected to form page (/oauth)
    3 - User logs in (POST to /oauth/authorize) and gets redirected to /callback?code=XXXXX
    4 - NodeBB sends request to /oauth/token and well... I redirect it back to /callback?code=XXXXX - This is probably wrong
    5 - NodeBB sends request to /oauth/api passing the access_token in req.query. I use that to get the user info in my MongoDB database and return the parameters required by NodeBB.

    The routes above do not work very well, probably because I'm not redirecting to NodeBB's callback URL properly, therefore, my endpoint routes are wrong. As you probably noticed, I'm not using sessions nor encrypted tokens or authorization codes as I really wanted it to be very simple. I've attempted looking at Passport.js to see how OAuth2orize actually handles the requests, but I couldn't replicate it. I also looked around in this forum and couldn't find anyone trying to do the same thing. Most people would actually just go with the solution NodeBB SSO Plugin + OAuth2orize.

    Anyways, I've been struggling for a while now with this issue, so any insight would be really helpful.

    Thank you very much for this amazing forum and good job for all the developers involved :)

    Regards!


  • Admin

    @rdomzim I'm going to try to help, because I built the "Log in via NodeBB.org" login on this site (which I think is disabled now), but I spent far too long figuring it out, and I remember almost none of it now.

    In any case, OAuth2orize overloads a single route with two different actions. If you call the callbackURL with a code, it pings your endpoint again to get the token.

    If you send back the token, THEN the token is saved and NodeBB makes the call to grab the user data.

    So when you respond to the call to /oauth/token, you should... all the callback back with a token... not a code.



  • @julian So, in my /oauth/token route, I should do like below?

    // Redirects user to the callback with a token
    app.all('/oauth/token', function(req, res){
      var t = req.body.redirect_uri + '?token=' + req.body.code
      res.redirect(t)
    })
    

    This example doesn't work, because NodeBB pings back /oauth/token without req.query.access_token. So maybe I'm using a wrong name for the token in the querystring or I should do a simple res.send(200, token) or something like that.

    I'm mixing a couple concepts in OAuth only to make this authentication simple, without having to save tokens or authorization codes. In my endpoint, the code and the token are the same, but I think I just need to fix the responses in my endpoint in order to get this working.

    I will see if I dig into passport to discover how the response should be when it pings /oauth/token.

    Regarding the flow of my routes, does it sound reasonable at least?

    Thanks for the help, Julian! :)



  • @julian Got it working!! :+1: The routes ended up like this:

    app.all('/oauth/token', function(req, res){
      res.send(200, 'access_token=' + req.body.code)
    })
    
    app.all('/oauth/api', function(req, res){
      var userInfo = JSON.parse(req.query.access_token)
      Users.findOne({email: userInfo.email}, function(err, user){
        res.json(user)
      })
    })
    

    NodeBB received the user data and I modified your plugin to handle the data accordingly. Finally, your plugin is currently asking for a templates directory, which I copy/pasted from the Facebook SSO plugin. Is that mandatory?

    Best regards! Thank you so much, Julian!!


  • Admin

    Yay! Glad you for it working :)

    A template page is not necessary if you are hard-coding the id and secret. Just remove the relevant parts of the code.

    Actually, looking at the code now, there's nothing that points to ACP pages anymore -- are you sure you're up to date?



  • @julian You are correct. Your GitHub plugin repository is fine, but if I install the plugin through npm install or NodeBB Admin Panel, then it comes with a plugin.json that has the line "templates": "./templates", which then causes me the error. So basically, your Github is at 0.2.3 but in NodeBB it shows that 0.2.2 is the latest:

    upload-509a28e5-b95d-4519-80b8-5c91ff279b17

    Maybe because I'm using 0.6.0? But not a big issue... Now I just need to figure out how to replace the Font Awesome icon with a custom one :D


  • Admin

    @rdomzim ah, that plugin should really be removed from npm, actually...



  • Hi Julian,

    Currently we are trying to integrate SSO with respect to our nodejs application. Steps which we have followed are:

    1. Installed "nodebb" module on app server
    2. Then installed "nodebb-plugin-sso-oauth-master" plugin
    3. Restarted nodebb

    Currently i am trying to login a user from "nodeapp" to "nodebb" forum. Consider nodeapp is hosted on same server with different port(http://example.com:3000/users/login) and nodebb with another port(http://example.com:4567).

    We are following token based approach to login in our nodejs app and same token can be used to login nodebb. Somehow i am not getting the flow, how to go about it.

    Can you please suggest how to make a call to forum from my nodejsapp with a same login token.


  • Admin

    @nitinayir8 The sso-oauth plugin is merely a skeleton, you cannot install it as-is, you will need to fork it and modify it to communicate with your app's OAuth2 provider.



  • @julian can you please guide us, what exactly we have todo to establish user login through our app. May be a flowchart or a sample code to get more idea about work flow. Currently we are stuck and not getting the workflow.

    Thanks,
    Nitin



  • @nitinayir8 @julian I second this request. We're also trying to establish oAuth integration with nodeBB, but the documentation on this is too limited.



  • @julian : even I am looking out for the same..it would be great if you could provide us with proper documentation



  • what oauth strategy are you using? Oauth2 you need to add the url to get the code, the url for the access token, and then a user api url which will return user information based off of the oauth2 access token.



  • OAuth2. Tried several OAuth2 servers for the test environment. So far no luck, currently on oauth2orize.

    How does nodeBB know to address the nodebb-plugin-sso-oauth plugin when requesting http://example.com/auth/my_plugin(/callback)? Currently it returns a "Not found" error. What helps a bit is overwriting strategies.url in line 125 with my own oauth2orize login URL 'http://oauth.example.com/login'. I can login now, but won't be redirected back to nodeBB. Manually opening http://example.com/auth/my_plugin/callback afterwards returns the "Not found" error, again.

    There are crucial parts missing here, is there any working example code to use the plugin with any OAuth 2 server (e.g. oauth2orize) out there? A quick tutorial and/or code example would be very helpful. I'm thinking on switching to another forum solution as well. NodeBB seems to be a nice idea, but without detailed documentation its not worth implementing it.


  • Admin

    Hi guys, my apologies for not replying sooner as I've been away.

    The sso-oauth plugin is a skeleton plugin, which means that it will get you about 90% of the way there, but I can't create a "generic" OAuth provider plugin because every OAuth2 provider is different.

    When you fork the plugin and install it into your NodeBB:

    1. You will need to change the values in the constants.oauth or constants.oauth2 section (and change type depending on whether you are using oauth 1 or 2).
    2. The "callback url" is inferred from your NodeBB setup. If the url in your config is example.org/forum, then the callback url will be example.org/forum/auth/$NAME/callback, where $NAME is what is set in the constants section.
    3. When you visit /auth/$NAME, it should kickstart the OAuth2 process by redirecting the user to constants.requestTokenURL. You shouldn't have to modify line 125.

  • Admin

    As an addendum, I'm not going to lie, configuring an OAuth2 provider is rage-inducing. You may want to consider a "login-override" instead, which would allow you to verify user credentials via the username/password login box on NodeBB, effectively bypassing NodeBB's username/password check in favour of your own:


Log in to reply
 


Looks like your connection to NodeBB was lost, please wait while we try to reconnect.