Help:Toolforge/My first NodeJS OAuth tool
Overview
Node.js webservices are well supported on Toolforge. Node.js is an open-source, cross-platform JavaScript run-time environment that executes JavaScript code server-side. This tutorial is designed to get a sample Node.js application installed onto Toolforge as quickly as possible. The application is written using the Node.js API and module passport-mediawiki-oauth that allows you to authenticate using MediaWiki in your Node.js applications. The guide will teach you how to:
- Create a new tool
- Run a Node.js webservice on Kubernetes
- Allow webservice visitors to authenticate using MediaWiki via a Node.js OAuth module
Getting started
Prerequisites
Skills
- Basic knowledge of Node.js
- Basic knowledge of SSH
- Basic knowledge of the Unix command line
- Familiarity with OAuth terminology would be nice but not required
Accounts
Steps to completion
- Create a new tool account
- Create a basic Node.js webservice
- Add a configuration file
- Add support for OAuth authentication
Step-by-step guide
Step 1: Create a new tool account
- Create a new tool account.
- For the examples in this tutorial,
<TOOL NAME>
is used to indicate places where your unique tool name is used in another command.
- For the examples in this tutorial,
- SSH to
login.toolforge.org
.- If your local username is different from your shell account name on Toolforge, you will need to include your Toolforge shell account name in your ssh command (i.e. <shell_username>@login.toolforge.org). See Help:Accessing Cloud VPS instances for more information on using SSH.
- Run
become <TOOL NAME>
to change to the tool user.
Step 2: Create a basic Node.js webservice
What is Node.js?
Node.js is an open-source, cross-platform JavaScript run-time environment that executes JavaScript code server-side.
Create a "My first node.js oauth tool!" application
The webservice will run on Kubernetes. Using --backend kubernetes
is recommended.
$ mkdir -p $HOME/www/js
Create the $HOME/www/js directory for your application
$ webservice --backend=kubernetes node10 shell
This will ensure that your app uses a more recent version of node and installs dependencies smoothly.
$ cd $HOME/www/js
Change to the new directory.
$ npm init
This command will initialize your project and will download a package.json file in your app folder.
$ npm install express --save
This will install the express module which we will use in the app. In addition, it will save the package to the dependency list in `package.json`
Place the following code in $HOME/www/js/server.js
:
/*
This file is part of the Toolforge Node.js tutorial
Copyright (C) 2018 Srishti Sethi and contributors
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var express = require( "express" );
var app = express();
app.get( "/", function ( req, res ) {
res.send( "My first node.js oauth tool!" );
});
app.listen( process.env.PORT || 5000, function () {
console.log( "Node.js app listening on port 5000!" );
});
Note: The 'server.js' file above starts with a license header that places it under the GPLv3+ license.
Code on Toolforge must be licensed under an Open Source Initiative (OSI) approved license. See the Right to fork policy for more information on this Toolforge policy.
Start the webservice
$ webservice --backend kubernetes node10 start
Starting webservice. Use webservice --backend=kubernetes node10 restart to restart it after a code change.
Once the webservice is started, navigate to https://$TOOLNAME.toolforge.org/
in your web browser, and see a 'My first node.js oauth tool!' message.
Exit out of the Nodejs shell and return to the SSH session on the bastion.
$ exit
Expected file layout
├── logs ├── replica.my.cnf ├── service.manifest └── www └── js ├── node_modules ├── package.json └── server.js
Troubleshooting
- If you run into errors doing
npm install
, tryLINK=g++ npm install
- If you can't access the
kubectl
executable, could it be that you started a webservice shell and didn'texit
it? - Find you container's name by running
kubectl get pods
and use that name to check your container's logs:kubectl logs -f $MY_CONTAINER_NAME
Step 3: Add support for OAuth authentication
OAuth is a safe mechanism for authenticating a Wikimedia user in the application. For the basics, read more about OAuth on mediawiki.org.
Install dependencies and save it to package.json file
passport-mediawiki-oauth module supports making OAuth requests to MediaWiki.
$ webservice --backend=kubernetes node10 shell
If you don't see a command prompt, try pressing enter.
$ npm install ejs express-session passport passport-mediawiki-oauth --save
ejs@2.6.1 node_modules/ejs
passport@0.4.0 node_modules/passport
├── pause@0.0.1
└── passport-strategy@1.0.0
express-session@1.15.6 node_modules/express-session
├── cookie-signature@1.0.6
├── utils-merge@1.0.1
├── cookie@0.3.1
├── parseurl@1.3.2
├── on-headers@1.0.1
├── depd@1.1.2
├── crc@3.4.4
├── uid-safe@2.1.5 (random-bytes@1.0.0)
└── debug@2.6.9 (ms@2.0.0)
passport-mediawiki-oauth@0.1.0 node_modules/passport-mediawiki-oauth
├── jwt-simple@0.5.1
├── pkginfo@0.2.3
└── passport-oauth@0.1.15 (oauth@0.9.15, passport@0.1.18)
$ exit
Update the application code
Here is our new $HOME/www/js/server.js
file:
www/js/server.js |
---|
The following content has been placed in a collapsed box for improved usability. |
/*
This file is part of the Toolforge Node.js tutorial
Copyright (C) 2018 Srishti Sethi and contributors
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var express = require( "express" );
var session = require( "express-session" );
var passport = require( "passport" );
var MediaWikiStrategy = require( "passport-mediawiki-oauth" ).OAuthStrategy;
var config = require( "./config" );
var app = express();
var router = express.Router();
app.set( "views", __dirname + "/public/views" );
app.set( "view engine", "ejs" );
app.use( express.static(__dirname + "/public/views") );
app.use( passport.initialize() );
app.use( passport.session() );
app.use( session({ secret: "OAuth Session",
saveUninitialized: true,
resave: true
}) );
app.use( "/", router );
passport.use(
new MediaWikiStrategy({
consumerKey: config.consumer_key,
consumerSecret: config.consumer_secret
},
function ( token, tokenSecret, profile, done ) {
profile.oauth = {
consumer_key: config.consumer_key,
consumer_secret: config.consumer_secret,
token: token,
token_secret: tokenSecret
};
return done( null, profile );
}
) );
passport.serializeUser( function ( user, done ) {
done( null, user );
});
passport.deserializeUser( function ( obj, done ) {
done( null, obj );
});
router.get( "/", function ( req, res ) {
res.render( "index", {
user: req && req.session && req.session.user,
url: req.baseUrl
} );
} );
router.get( "/login", function ( req, res ) {
res.redirect( req.baseUrl + "/auth/mediawiki/callback" );
} );
router.get( "/auth/mediawiki/callback", function( req, res, next ) {
passport.authenticate( "mediawiki", function( err, user ) {
if ( err ) {
return next( err );
}
if ( !user ) {
return res.redirect( req.baseUrl + "/login" );
}
req.logIn( user, function( err ) {
if ( err ) {
return next( err );
}
req.session.user = user;
res.redirect( req.baseUrl + "/" );
} );
} )( req, res, next );
} );
router.get( "/logout" , function ( req, res ) {
delete req.session.user;
res.redirect( req.baseUrl + "/" );
} );
app.listen( process.env.PORT || 5000, function () {
console.log( "Node.js app listening on port 5000!" );
} );
|
The above content has been placed in a collapsed box for improved usability. |
It uses the Ejs template system that is built into Node.js that allows rendering data to views.
The code in server.js uses express.static
middleware function to serve templates from the $HOME/www/js/public/views
directory.
$ mkdir $HOME/www/js/public/views
$ edit $HOME/www/js/public/views/index.ejs
<!-- views/index.ejs -->
<meta charset="UTF-8">
<h3>My first nodejs oauth app</h3>
<% if(user) { %>
<p>Greetings, <%= user.displayName %>! <a href="<%= url %>/logout">Click to logout</a></p>
<% } else { %>
<p><a href="<%= url %>/login">Click here to login</a></p>
<% } %>
Add a configuration file to $HOME/www/js/config.js
to contain OAuth secrets
- Register a new OAuth consumer.
- As callback URL, use:
https://<TOOL NAME>.toolforge.org/auth/mediawiki/callback
As contact e-mail address, use the e-mail address linked to your Wikimedia unified account. - Keep the default grant settings ('Request authorization for specific permissions.' with just 'Basic rights' selected)
- You will be able to use your own account before the consumer has been approved.
- Copy the consumer token and secret token values that are generated. These are needed for the config.js file.[note 1]
$ touch $HOME/www/js/config.js
$ chmod 600 $HOME/www/js/config.js
$ vim $HOME/www/js/config.js
var config = {};
config.consumer_key = "the consumer token value from your OAuth consumer registration";
config.consumer_secret = "the secret token value from your OAuth consumer registration";
module.exports = config;
Restart the webservice
$ webservice --backend kubernetes node10 restart
Restarting webservice...
Once the webservice has restarted, navigate to https://$TOOLNAME.toolforge.org/
in your web browser to see the new landing page.
Try using the login and logout links to test out the OAuth integration.
See also
- Source code for this article on GitHub
- Live version of this application on Toolforge
- My first Django OAuth tool
- My first Flask OAuth tool
- New toolforge.org domain
References
Notes
- ↑ While in testing, and using a basic permission consumer, it is harmless to leave the permissions for the config file as the default. However, best practices is to restrict reading to the required users, hence the
chmod 600
.
Communication and support
Support and administration of the WMCS resources is provided by the Wikimedia Foundation Cloud Services team and Wikimedia Movement volunteers. Please reach out with questions and join the conversation:
- Chat in real time in the IRC channel #wikimedia-cloud connect, the bridged Telegram group, or the bridged Mattermost channel
- Discuss via email after you subscribed to the cloud@ mailing list