Authentication is done through a client side (http-only) cookie. You need to activate the authentication inside src/manifest.json, by setting enabled
true:
src/manifest.json
"app-authentication": {
"enabled": false,
"onlySsl": true,
"session-cookie": false,
"ttl-sec": 1800,
"loginView": "login",
"strategies": [
{
"strategy": "",
"loginView": "login"
},
{
"strategy": "general2",
"validateFunc": "src/authentication/validate"
}
]
}
After that, it takes 4 steps to enable a full featured authenticated web app:
- Define routes for authentication
- Define rules for who is authenticated
- Enable and disable users for being authenticated
- Define a fall-back view for visiters who access a page without being authenticated
Setting up your page for authentication
1. Define inside the router if a route should be authenticated
Example src/routes.js
with authentication
const routes = [
{
method: 'GET',
path: '/private-page1',
config: {
auth: {
strategy: 'general'
},
handler: function(request, reply) {
reply.reactview('management/rsi');
}
}
},
{
method: 'GET',
path: '/private-page2',
config: {
auth: {
strategy: 'general',
scope: 'admin'
},
handler: function(request, reply) {
reply.reactview('management/rsi');
}
}
}
];
module.exports = routes;
Every route needs a auth.strategy
which should match a strategy that is defined in manifest.json
.
2. Define rules for who is authenticated
This can be done by 2 different ways, which can be combined:
A. By giving all users a scope
at their athentication-cookie
B. By making use of a validateFunc
, which location is set in manifest.json
A. Using scope
This is the most easiest way and is handy when you do not need to finegrain authentication per single user specifically. You just need to give the users a scope
property at their authentication-cookie. If the scope mathes the route, the get authenticated.
B. Using validateFunc
If you have defined a validateFunc
for a strategy (inside src/manifest.json), then every request to a route that has this strategy, will be passed through to the validateFunc.
The validateFn should be a CommonJS module that might looks like this:
Example validateFn
const r = require('rethinkdb');
const validate = async function(request, reply, authCookie) {
let validated = false,
connection, cookieProps, record, userId;
if (authCookie.isLoggedIn()) {
try {
cookieProps = authCookie.getProps();
userId = cookieProps.id;
if (userId) {
connection = await r.connect(databaseConfig);
record = await r.table('users').get().run(connection);
validated = (cookieProps.password===record.password) && !record.userBlocked;
}
}
catch (err) {
console.warn(err);
}
// connection might not exists when an error is thrown during r.connect()
// check this before you close the connection
if (connection) {
connection.close();
}
return validated;
};
module.exports = validate;
The validateFn
will be invoked by itsa-react-server
with 3 arguments. The third is the authentication cookie of the request.
- Enable and disable users for being authenticated
In order to manage the authentication cookie, there are 3 special methods available:
- request.getAuthCookie()
- reply.login()
- reply.logout()
Enable and disable a user from being authentication should be done by sending a from and handle it by an Action
.
Example login Action
const r = require('rethinkdb');
const actionFn = async (request, reply, options, language, manifest) => {
let status, message, connection, records, user;
const payload = request.payload,
email = payload.email,
password = payload.password,
stayLoggedin = payload.stayLoggedin;
try {
connection = await dbLineaService.getConnection();
records = await r.table('users')
.getAll(email, {index: 'email'})
.limit(1)
.run(connection);
user = records[0];
}
catch (err) {
console.warn(err);
}
// connection might not exists when an error is thrown during r.connect()
// check this before you close the connection
if (connection) {
connection.close();
}
if (user && (user.password===password)) {
status: 'OK',
reply.login({
id: user.id,
password: user.password,
scope: user.scope
}, !stayLoggedin);
}
else {
status: 'ERROR',
message: 'invalid login'
}
return {
status,
message
};
};
module.exports = actionFn;
Example logout Action
const actionFn = async (request, reply, options, language, manifest) => {
reply.logout();
return {status: 'OK'};
};
module.exports = actionFn;
- Define a fall-back view for visiters who access a page without being authenticated
Whenever a user visits a page and has no authentication, itsa-react-server will return the view
that is defined by app-authentication.loginView (inside src/manifest.json). This should act as a login form where the user can enter its credentials to access the page. If the login-action
returns with {status: 'OK'}
, then the web app will retry to access the original page and returns the authenticated view.