Hi again,
This is the last part in this short series of articles in which we’re building an omni-channel micro app using ONEm Framework and the free api from themoviedb.org.
If you missed the previous articles, you’ll find part 1 here and part 2 here.
All the code is on Github, check the branches labelled “part1”, “part2” and “part3”.
In this last article of the series, I will show you how to recognise different users hitting your micro-app. This will allow you to provide a more personalised user experience and tailor your micro-app’s features and functions according to the particular user. To do this, we’ll make use of the native authentication capabilities of the ONEm Framework.
In part 2, we added a search form that allowed the user to search for a particular movie by title.

Great, but now we want to allow users to maintain their own lists of their favourite movies, so they can come back to them later.
This will require two things:
- A database to store users and their favourite movies
- A way to recognise returning users so we can show their favourites when they hit the landing menu
Data Model
Our database schema will have two tables (userfavourites and movies). I’ve used dbdiagram.io to document the schema and produce the entity model (it’s also here):

You maybe wondering why the movies table is needed at all, since we could just retrieve the data directly from themoviedb.org. The reason is just to avoid hits on themoviedb API, you’ll see later on.
We’ll use MongoDb and the mongoose module to handle the db operations. Feel free to use any database of your choice:
const Mongoose = require('mongoose')
const UserFavouriteSchema = new Mongoose.Schema({
user_id: { type: String, required: true },
_movie: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'movies',
required: true
}})
const UserFavourite = Mongoose.model('userfavourites', UserFavouriteSchema)
const MovieSchema = new Mongoose.Schema({
movie_id: { type: String, required: true },
title: { type: String, required: true }
})
const Movie = Mongoose.model('movies', MovieSchema)
module.exports = {
UserFavourite,
Movie
}
Save the above in app_api/models/index.js
Identifying Users
The database implementation is pretty simple and will be familiar to many of you. But how do we know which user is accessing our micro-app? When the ONEm Framework issues callbacks to the micro-app’s callback_url, a JSON Web Token (JWT) is included in the HTTP header of each request. If you’re not familiar with JWTs, there’s an excellent description here.
The JWT issued by the ONEm Framework can be easily decoded, there are many open source modules out there to choose from. Once decoded, the sub property (or claim in JWT terminology), will contain our unique user id.
The JWT issued by ONEm Framework contains lots of goodies relating to the user accessing your micro-app and also some hints about the capabilities of the channel the user is using. A full description can be found in the ONEm Framework documentation.
Verifying the JWT
In order to verify that the token we get from the ONEm Framework is bona-fide, we should need to verify the signature. The JWT will be signed using a the token secret value we used when we created the micro app. This was not mentioned in part 1, but eager-eyed reader will have spotted this field:

The token will appear in all callbacks issued by ONEm Framework in the Authorization header like this:
Authorization: Bearer <token>
We’ll use express middleware and jwt-simple to verify the token. The following code cause the token to be extracted from the header and verified on all /api routes:
const jwt = require('jwt-simple')
...
/*
* Middleware to verify the token and save the user in req.user
*/
function getUser(req, res, next) {
if (!req.header('Authorization')) {
return res.status(401).send({ message: 'Unauthorized request' })
}
const token = req.header('Authorization').split(' ')[1]
const payload = jwt.decode(token, process.env.TOKEN_SECRET)
if (!payload) {
return res.status(401).send({ message: 'Unauthorized Request'
}
req.user = payload.sub
next()
}
api.use(getUser)
Ensure that the TOKEN_SECRET environment variable is configured in your .env file and that its value matches that of the developer portal:
PORT=3000READ_ACCESS_TOKEN=
<movie api token>
TOKEN_SECRET=87654321
Add to favourites
When a user has viewed a movie, we want to allow them to add the movie to their favourites list or go back and search again:

If the movie isn’t already a favourite of the current user, we’ll present an option to “Add to favourites”. So let’s update views/movieView.pug:
section
header #{movie.title}
img(src=movie.poster_path alt="poster")
p #{movie.overview}
p Released: #{new Date(movie.release_date).toLocaleString()}
ul
if !isFavourite
li
a(href="/user/favourite/"+movie.id+"?title="+movie.title method="post") Add to favourites
li
a(href="/") Search again
When the user selects “Add to favourites”, the ONEm Framework will issue a HTTP POST (note the method=”post”) to /api/user/favourite/{movieId}
...
const { UserFavourite, Movie } = require('../models')
...
api.post('/user/favourite/:movieId', async (req, res) => {
try {
const movieId = req.params.movieId
const title = req.query.title
const user = req.user
const movie = await Movie.findOneAndUpdate(
{ movie_id: movieId },
{ movie_id: movieId, title: title },
{ upsert: true, new: true }
)
await UserFavourite.findOneAndUpdate(
{ user_id: user, _movie: movie._id },
{ user_id: user, _movie: movie._id },
{ upsert: true, new: true }
)
const userFavourites = await UserFavourite.find({ user_id: user }).populate('_movie')
const rootTag = loadTemplate(views.VIEW_LANDING, { userFavourites: userFavourites })
const response = Response.fromTag(rootTag)
res.json(response.toJSON())
} catch (e) {
res.status(500).json({ success: false, message: 'server error' })
}
})
We grab the user id, including from the HTTP request in this line:
const user = req.user
After the favourite has been added, we display the landing view. We want that view to show the updated favourite under “My favourites”, like this:

Let’s update app_api/views/landing.pug to reflect the new requirement:
section(auto-select)
header Menu
img(src="https://www.themoviedb.org/assets/2/v4/logos/408x161-powered-by-rectangle-green-bb4301c10ddc749b4e79463811a68afebeae66ef43d17bcfd8ff0e60ded7ce99.png" alt="powered by themoviedb.org")
ul
li
a(href="/search") Search for a movie
li My favourites (#{userFavourites.length})
each favourite in userFavourites
li
a(href='/movie/'+favourite._movie.movie_id) #{favourite._movie.title}
You may have noticed in the first line we used the “auto-select” attribute. This tells ONEm Framework that if the resulting menu comprises only one item, that item is automatically selected. So if the user doesn’t have any favourites, the only option will be “Search for a movie” which will be auto-selected. No point in asking the user to choose from a menu comprising only one option.
Auto-select and other html attributes are documented here.
I have omitted some code for the sake of brevity, you can find the complete code in the git repository referenced at the top of this article. To quick start, you can use the following sequence:
$ git clone https://github.com/chrishornmem/movie-microapp.git && cd movie-microapp
$ git checkout part3
$ npm install
# Ensure the .env is created as per the instructions, then
$ node index
Once you have the new version up and running, you can also try out the SMS interface using the Test Client tab in the developer portal.


Thanks for reading, if you’re interested in learning more about ONEm Framework, you can checkout our technical documentation here.