byu-wabs
Brigham Young University's Web Application Bootstrap Service express middleware.
- Manages WS02 OAuth Tokens
- Keeps CAS user and WS02 user in sync
- Interoperability with legacy applications (C-Framework)
Table of Contents
Examples
Express Server
const cookieParser = require('cookie-parser'); // required by WABS middleware
const bodyParser = require('body-parser'); // required by WABS middleware
const express = require('express');
const path = require('path');
const wabs = require('byu-wabs');
const app = express();
const middleware = wabs('my-app');
// cookie parser needed for wabs authentication tools
app.use(middleware.ready(config => cookieParser(config.encryptSecret)));
// body parser needed for brownies
app.use(bodyParser.urlencoded({ extended: false, type: '*/x-www-form-urlencoded'}));
app.use(bodyParser.json());
// middleware for routes and adding req.wabs object, required for all other WABS middleware to function
app.use(middleware.init());
// create a protected endpoint
app.get('/protected', middleware.authenticated(), function(req, res) {
res.send('Access granted');
});
// have CAS and WSO2 both identify the user prior to taking this path
app.get('/sync', middleware.sync(), function(req, res) {
res.send('User is logged in: ' + !!req.wabs.user);
});
// login route (optional)
app.get('/login', middleware.login('/'));
// log out route (optional)
app.get('/logout', middleware.logout('/'));
// auth sync and html5 routing for paths that should resolve to the index file
app.use(middleware.html5Router({ indexPath: 'www/index.html' }));
// static file routing for static files
app.use(express.static(__dirname + '/www/'));
// error catching middleware
app.use(middleware.catch());
// start the server listening on the specified port
app.listen(3000, function(err) {
if (err) console.error(err.stack);
console.log('Listening on port 3000');
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My App</title>
</head>
<body>
<p>This is my app</p>
<script src="/wabs/script.js"></script> <!-- not required, just highly recommended -->
</body>
</html>
Configuration
The configuration is two part:
WSO2 Configuration
-
Define a WSO2 application at https://api.byu.edu/store and then navigate to "My Applications"
-
Create a new application.
-
Set the application callback URL. The callback URL should point to your server at the path
/wabs/oauth-code
. If your developing locally on port 3000 then your callback URL would look like this:http://localhost:3000/wabs/oauth-code
. -
Once the application is created navigate to "My Subscriptions"
-
Select your app from the dropdown.
-
Create some Sandbox or Production keys.
Middleware Configuration
When creating a WABS middleware instance you can provide it with a configuration. These are the options available:
-
appName - A unique identifier that will uniquely identify this server. If there are multiple instances of this server running in a load balanced situation, each instance should have the same
appName
. -
awsConfig - A configuration to use when creating an instance of the AWS SSM. This is used to get a WABS configuration from the parameter store. Defaults to
{ region: 'us-west-2' }
. -
basePath - The path at which all WABS services will exist. Defaults to
/wabs
. -
consumerKey - The WSO2 consumer key to use for authorization.
-
consumerSecret - The WSO2 consumer secret to use for authorization.
-
encryptSecret - A password used for encryption and decryption.
-
hardTimeout - The maximum number of minutes to allow authorization to persist from initial grant. Defaults to
600
which is equivalent to 10 hours. -
host - The protocol, domain, and port used to reach this server. If omitted then auto detection will be used.
Middlewares
The wabs package is a collection of middlewares. The core piece of middleware, init, is required for all other WABS appendage middlewares to work.
const cookieParser = require('cookie-parser'); // required by WABS middleware
const bodyParser = require('body-parser'); // required by WABS middleware
const express = require('express');
const wabs = require('byu-wabs');
const app = express();
const middleware = wabs('my-app');
// cookie parser needed for wabs authentication tools
app.use(middleware.ready(config => cookieParser(config.encryptSecret)));
// body parser needed for brownies
app.use(bodyParser.urlencoded({ extended: false, type: '*/x-www-form-urlencoded'}));
app.use(bodyParser.json());
// required for all other WABS middleware to function
app.use(middleware.init());
Below are the additional middlewares and how to use them:
authenticated
This middleware checks to see if a local session has been established. The middleware must be initialized first.
Signature: wabs#authenticated ( [ options ] ) : Function
Parameters:
-
options - An object that specifies the behavior of the middleware. Options include the following properties:
-
startAuthentication - If set to
true
and the client does not have a local session then the client will be redirected to CAS and WSO2 for authentication and authorization. Note that if set totrue
that the HTTP method hitting this endpoint will be set toGET
due to the nature of HTTP redirects. Defaults tofalse
.
-
startAuthentication - If set to
Returns a middleware function.
app.use(wabs.init());
app.use(wabs.authenticated());
catch
This middleware is best placed after all other middleware and is used to catch errors and to report them.
The error will be logged to the console with an ID and that ID is also sent to the client as a reference number so that the error can be looked up.
Signature: wabs#catch () : Function
Parameters: None
Returns: a middleware function.
app.use(wabs.catch());
clientGrantAccessToken
This middleware will get the client grant access token and store it on the request at req.wabs.clientGrantAccessToken
.
Signature: wabs#clientGrantAccessToken () : Function
Parameters: None
Returns: a middleware function.
app.use(wabs.clientGrantAccessToken(), function(req, res, next) {
console.log(req.wabs.clientGrantAccessToken);
next();
});
html5Router
This middleware will return the index.html
file for any route that does not have a file extension. It is best to include this middleware last (or at least very late) in relation to defined routes and other middleware.
Signature: wabs#html5Router () : Function
Parameters:
-
config - The router configuration options:
-
indexPath - A required option that specifies the path to the
index.html
file. -
sync - If
true
then any requests to load theindex.html
file through the html5Router middleware will synchronize authentication and authorization prior to loading theindex.html
file. Defaults totrue
.
-
Returns: a middleware function.
app.use(wabs.html5Router({ indexPath: '/path/to/index.html' }));
login
This middleware initiates a login by obtaining authentication and authorization information through CAS and WSO2. This middleware can be used as a route.
Signature: wabs#login ([ redirect [, failure ] ]) : Function
Parameters:
-
redirect - The URL to redirect to on successful login. Defaults to the current URL, but that will cause an infinite redirect in many cases.
-
failure - The URL to redirect to on failure to log in. Defaults to using the same URL as the
redirect
parameter.
Returns: a middleware function.
app.get('/login', wabs.login('/'));
logout
This middleware causes the client to perform a logout. That includes deletion of sessions for WABS, CAS, WSO2, and the C-Framework. This middleware can be used as a route.
Signature: wabs#logout ([ redirect ]) : Function
Parameters:
- redirect - The URL to redirect to on successful login. Defaults to the current URL, but that will cause an infinite redirect in many cases.
Returns: a middleware function.
app.get('/logout', wabs.logout('/'));
sync
This middleware performs a synchronization operation with CAS and WSO2 to make sure that everyone agrees on who is currently logged in. It is highly recommended that this middleware execute any time you are serving the index.html
file to reduce cached session length (in case the user did log out but your app didn't get the memo).
This middleware is intended for use in GET routes.
Signature: wabs#sync () : Function
Parameters: None
Returns: a middleware function.
app.get('/', wabs.sync(), function(req, res) {
if (req.wabs.user) {
res.send('You are logged in as: ' + req.wabs.user.netId);
} else {
res.send('You are not logged in.');
}
});
Server Request WABS Object
This object is generated with each request that is made. The object has multiple properties:
req.wabs.auth
This is an object that defines the currently authenticated user. It has the following properties:
-
accessToken - the access token associated with the active user.
-
refreshToken - the refresh token associated with the active user.
-
hardLimit - an ISO date string of when this user will no longer be able to refresh their access tokens.
-
user - a user object.
req.wabs.brownieDecode
A function that takes an encoded brownie and decodes it to to an object.
Signature: req.wabs.brownieDecode (encodedBrownie)
Parameters:
- encodedBrownie - The brownie value to decode.
Returns: a promie that resolves to the decoded brownie.
req.wabs.brownieEncode
A function that takes a brownie object and encodes it to send to the C-Framework.
Signature: req.wabs.brownieEncode (data, seed)
Parameters:
-
data - The decoded brownie object.
-
seed - The seed needed to re-encode the brownie.
Returns: a promise that resolves to the decoded brownie.
req.wabs.getClientGrantAccessToken
Get the client grant access token.
Signature: req.wabs.getClientGrantAccesstoken ()
Parameters: none
Returns: a promise that resolves to the access token.
req.wabs.login
A function that when called will initiate a login sequence using redirects.
Signature: req.wabs.login ( gateway [, success [, failure ] ])
Parameters:
-
gateway - A boolean that signifies whether to use the CAS gateway. If true and the user is not logged in they will not be directed to the CAS login page.
-
success - The URL to direct the client to after successfully logging in. Defaults to the current request URL.
-
failure - The URL to direct the client to after failure to login. Defaults to the success URL.
Returns: undefined
req.wabs.logout
A function that when called will initiate a logout sequence using redirects.
Signature: req.wabs.logout ( redirect )
Parameters:
- redirect - The URL to redirect the client to after logout. Note that currently (due to the C-Framework logout) the user will not automatically be redirected to this URL but will get a link that will direct to that URL.
Returns: undefined
req.wabs.refreshTokens
Manually refresh to code grant access tokens that are associated with the current user.
Signature: req.wabs.refreshTokens ()
Parameters: none
Returns: a promise that resolves to an HTTP status code. Before the promise resolves the information at req.wabs.auth
will be updated with the new access tokens.
req.wabs.script
A function that when called respond to the client request with the wabs client-side JavaScript file.
Signature: req.wabs.script ()
Parameters: none
Returns: undefined
req.wabs.user
This is an object that defines the current user's personal information. It has the following properties which are all self explanatory:
-
byuId
-
netId
-
personId
-
preferredFirstName
-
prefix
-
restOfName
-
sortName
-
suffix
-
surname
-
surnamePosition
Client Application Tools
The client (a.k.a. the browser) can receive several helpful tools if you remember to include /wabs/script.js
in your index.html
file. (Note that if you change the basePath
in the configuration options then this may be at a different path: for example: /<basePath>/script.js
).
Here are the tools:
byu.auth.accessToken
Get the access token that represents the user. You'll need this access token to make direct API requests against WSO2. If the user is not logged in this value will be undefined
.
const token = byu.auth.accessToken;
byu.auth.accessTokenExpires
Get a Date object for when the access tokens expires. After expiration it is likely still possible to refresh the tokens. If the user is not logged in this value will be undefined
.
const date = byu.auth.accessTokenExpires;
byu.auth.expires
Get a Date object for when the WABS session expires. This value is calculated as 9 minutes after the access token expires or when the sessions duration hard limit is reached, whichever comes first. If the user is not logged in this value will be undefined
.
const date = byu.auth.expires;
byu.auth.hardLimit
Get a Date object for when the WABS session can no longer be refreshed. If the user is not logged in this value will be undefined
. If the client goes through the server's sync
middleware then this hard limit will be updated.
const date = byu.auth.hardLimit;
byu.auth.login
Perform a login via JavaScript.
Signature: byu.auth.login ( [ redirect [, failure ] ] )
Parameters:
-
redirect - The URL to redirect to after login. If omitted then the current URL will be used.
-
failure - The URL to redirect to if the user fails to authenticate or authorize the application. Defaults to the redirect URL.
Returns: undefined
byu.auth.login();
byu.auth.logout
Perform a logout via JavaScript.
Signature: byu.auth.logout ( [ redirect ] )
Parameters:
- redirect - The URL to redirect to after logout. If omitted then the current URL will be used.
Returns: undefined
byu.auth.logout();
byu.auth.refreshToken
Refresh the user access token.
You won't generally need to call this function because the user's access token will automatically be refreshed when it expires.
Signature: byu.auth.refreshToken ( [ callback ] )
Parameters:
- callback - An optional function to call after the token has been refreshed.
Returns: undefined
byu.auth.refreshToken();
byu.brownie.clear
Remove all data from the brownie.
Signature: byu.auth.clear ( )
Parameters: none
Returns: undefined
byu.brownie.clear();
byu.brownie.get
Get the value for a specific brownie property.
Signature: byu.brownie.get ( [ property ] )
Parameters:
- property - The name of the brownie property to get.
Returns: a number, a string, null, or undefined.
const id = byu.brownie.get('personId');
byu.brownie.list
Get an object with all brownie properties and values.
Signature: byu.brownie.list ()
Parameters: none
Returns: a number, a string, null, or undefined.
const obj = byu.brownie.list();
byu.brownie.navigateTo
Navigate to a new URL. If the URL is to the C-Framework then the brownie will be sent with the redirect.
This function does not need to be called unless the URL is to a C-Framework page. The brownie data will be maintained in the tab automatically.
Signature: byu.brownie.navigateTo ( url [, target ] )
Parameters:
-
url - The URL to navigate to.
-
target - The window target. Defaults to
_self
.
Returns: undefined
const id = byu.brownie.navigateTo('http://some-url.com');
byu.brownie.set
Set a brownie value.
Signature: byu.brownie.set ( property, value )
Parameters:
-
property - The name of the property to set.
-
value - The value to set. This must be a string, number, or null.
Returns: byu.brownie
byu.brownie.set('abc', 123);
byu.brownie.unset
Remove a brownie value.
Signature: byu.brownie.unset ( property )
Parameters:
- property - The name of the property to remove.
Returns: byu.brownie
byu.brownie.unset('abc');
byu.user
Get the current user object. If the user is not authenticated and authorized then this value will be null, otherwise it will be an object with the following structure:
{
byuId: '123456789',
netId: 'abc123',
personId: '123123123',
preferredFirstName: 'Bob',
prefix: '',
restOfName: 'Bob E',
sortName: 'Smith, Bob E',
suffix: '',
surname: 'Smith',
surnamePosition: 'L'
}