Below is a command to the clone the source code for the application used in this tutorial
git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions
After a user has successfully entered their login credentials, mobile banking apps use a token
and sessionId
created by the server to represent a user's identity. The token
is stored in Redis for the duration of a user session and also sent in the login response to the banking application client (mobile/ browser). The client application then sends the token
with every request to server and server validates it before processing the request.
Redis Stack supports the JSON data type and allows you to index and querying JSON and more. So your session store is not limited to simple key-value stringified data.
The session store houses critical information related to each user as they navigate an application for the duration of their session. Mobile banking session data may include, but is not limited to following information:
user
, admin
, supervisor
, super-admin
, etc.express-session
and connect-redis-stack
libraries integration is demonstrated in this tutorial)Read our ebook that answers the question: Are JSON Web Tokens (JWT) Safe? It discusses when and how to safely use JWTs, with battle-tested solutions for session management.
Below is a command to the clone the source code for the application used in this tutorial
git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions
Download the above source code and run following command to start the demo application
docker compose up
After docker up & running, open http://localhost:8080/ url in browser to view application
This application leverages Redis core data structures, JSON, TimeSeries, Search and Query features. The data seeded is later used to show a searchable transaction overview with realtime updates as well as a personal finance management overview with realtime balance and biggest spenders updates.
On application startup in app/server.js
, a cron is scheduled to create random bank transactions at regular intervals and seed those transactions in to Redis.
//cron job to trigger createBankTransaction() at regular intervals
cron.schedule('*/10 * * * * *', async () => {
const userName = process.env.REDIS_USERNAME;
createBankTransaction(userName);
//...
});
balanceAfter
value is recorded in a TimeSeries with the key balance_ts
for every transaction.fromAccountName
member within the sorted set bigspenders
is incremented by the transaction amount. Note that this amount can be positive or negative.let balance = 100000.0;
const BALANCE_TS = 'balance_ts';
const SORTED_SET_KEY = 'bigspenders';
export const createBankTransaction = async () => {
//to create random bank transaction
let vendorsList = source.source; //app/transactions/transaction_sources.js
const random = Math.floor(Math.random() * 9999999999);
const vendor = vendorsList[random % vendorsList.length]; //random vendor from the list
const amount = createTransactionAmount(vendor.fromAccountName, random);
const transaction = {
id: random * random,
fromAccount: Math.floor((random / 2) * 3).toString(),
fromAccountName: vendor.fromAccountName,
toAccount: '1580783161',
toAccountName: 'bob',
amount: amount,
description: vendor.description,
transactionDate: new Date(),
transactionType: vendor.type,
balanceAfter: balance,
};
//redis json feature
const bankTransaction = await bankTransactionRepository.save(transaction);
console.log('Created bankTransaction!');
// ...
};
const createTransactionAmount = (vendor, random) => {
let amount = createAmount(); //random amount
balance += amount;
balance = parseFloat(balance.toFixed(2));
//redis time series feature
redis.ts.add(BALANCE_TS, '*', balance, { DUPLICATE_POLICY: 'first' });
//redis sorted set as secondary index
redis.zIncrBy(SORTED_SET_KEY, amount * -1, vendor);
return amount;
};
Sample bankTransaction data view using RedisInsight
Download RedisInsight to view your Redis data or to play with raw Redis commands in the workbench.
Redis is integrated into many session management libraries, We will be using connect-redis-stack library for this demo which provides Redis session storage for your express-session application.
The following code illustrates configuring Redis sessions and with express-session
.
import session from 'express-session';
import { RedisStackStore } from 'connect-redis-stack';
/* configure your session store */
const store = new RedisStackStore({
client: redis, //redis client
prefix: 'redisBank:', //redis key prefix
ttlInSeconds: 3600, //session expiry time
});
const app = express();
// ...
app.use(
session({
store: store, //using redis store for session
resave: false,
saveUninitialized: false,
secret: '5UP3r 53Cr37', //from env file
}),
);
//...
app.listen(8080, () => console.log('Listening on port 8080'));
Let's look at the /perform_login
API code which is triggered on the click of Login button from login page
Since connect-redis-stack is an express middleware, a session is automatically created at the start of the request, and updated at the end of the HTTP(API) response if req.session
variable is altered.
app.post('/perform_login', (req, res) => {
let session = req.session;
console.log(session);
/*
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
*/
//hardcoded user for demo
if (req.body.username == 'bob' && req.body.password == 'foobared') {
//on successful login (for bob user)
session = req.session;
session.userid = req.body.username; //create session data
res.redirect('/index.html');
} else {
res.redirect('/auth-login.html');
}
});
In above code - session.userid
variable is assigned with a value on successful login (for "bob" user), so a session is created in Redis with assigned data and only Redis key (sessionId) is stored in client cookie.
connect.sid
(containing only sessionId)Now on every other API request from client, connect-redis-stack library makes sure to load session details from redis to req.session
variable based on the client cookie (sessionId).
Consider the below /transaction/balance
API code to demonstrate session storage.
We have to modify the req.session
variable to update session data. Let's add more session data like current balance amount of the user .
/* fetch all transactions up to an hour ago /transaction/balance */
transactionRouter.get('/balance', async (req, res) => {
const balance = await redis.ts.range(
BALANCE_TS,
Date.now() - 1000 * 60 * 5,
Date.now(),
);
let balancePayload = balance.map((entry) => {
return {
x: entry.timestamp,
y: entry.value,
};
});
let session = req.session;
if (session.userid && balancePayload.length) {
//adding latest BalanceAmount to session
session.currentBalanceAmount = balancePayload[balancePayload.length - 1]; //updating session data
}
res.send(balancePayload);
});
currentBalanceAmount
field ('x' denoting timestamp and 'y' denoting balance amount at that timestamp)Hopefully, this tutorial has helped you visualize how to use Redis for better session management, specifically in the context of mobile banking. For additional resources related to this topic, check out the links below: