Building a web application with Thing+ platform
These days buzzwords get thrown around a lot about the “4th industrial revolution”. While it’s simply a collection of upcoming technology to make our lives easier, the barrier of entry to new tech is always high.
Thing+ platform was developed as a way to lower that barrier, it’s a Platform as a service that provide you with ready-made embedded software for Internet of
Things devices and a web service that manages those devices.
This post is a starter’s guide to building a web application that use Thing+ from scratch. It’s assumed the reader is somewhat familiar with IoT terminologies, and have a basic grasp of programming.
Preparation
- You need to register with Thing+ (to authorise
with it later) - Having a raspberry Pi and install gateway software on it is a plus, otherwise you can create a virtual gateway in Thing+
- Chrome, postman plugin, interceptor plugin
- NodeJS
- Git
Starting the repository
- Name your app, let’s say
thingplus-webapp
npm install express-generator -g
express --view=pug thingplus-webapp
cd thingplus-webapp
git init
vi .gitignore
, paste the following
# Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Typescript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env
npm install
Register, login
Registration page
We don’t need to involve ourself in user management since we can use user information from Thing+ Let’s create a register
link first, add this line to app.js
app.use('/register', require('./routes/register'));
Add register.js
to the routes
folder with the following content
'use strict'; const auth = require('../auth'); const express = require('express'); const router = express.Router(); /* GET register page */ router.get('/', function (req, res) { res.redirect(auth.thingPlus.signupUri); }); module.exports = router;
This simply create a new page that will redirect to Thing+’s sign up page Let’s link to it from the index page, modify index.pug
to the layout
folder
extends layout block content div.container-fluid if user p Welcome, #{user.fullName} p a.btn.btn-primary(role="button", href="/gateways") Manage gateways p a.btn.btn-primary(role="button", href="/rules") Manage rules p a.btn.btn-primary(role="button", href="/trains") Manage trains p a.btn.btn-primary(role="button", href="/logout") Logout else p Welcome to #{appName}, ready to get started? p a.btn.btn-primary(role="button", href="/login") Login now p Don't have an account? p a.btn.btn-primary(href="/register") Register now
This will show various internal commands if you are logged in, and show the register link if you are not logged in.
Login and callback route
routes
folder- Login.js
'use strict'; const auth = require('../auth'); const querystring = require('querystring'); const express = require('express'); const session = require('../session'); const router = express.Router(); router.use(session); /* GET login page */ router.get('/', function (req, res, next) { if (req.session && req.session.token) { res.redirect('/'); } else { res.redirect( auth.thingPlus.authorizationUri + '?' + querystring.stringify({ client_id: auth.thingPlus.clientId, response_type: 'code', redirect_uri: auth.thingPlus.redirectUri(req.headers.host) })); } }); module.exports = router;
- Callback.js
'use strict'; const auth = require('../auth'); const express = require('express'); const http = require('https'); const request = require('request'); const session = require('../session'); const router = express.Router(); router.use(session); function saveUserName(token, req, res) { var options = { url: auth.thingPlus.userUri, auth: { bearer: req.session.token }, json:true, }; request.get(options, function(error, mesage, body) { req.session.userName = body.data.loginId; req.session.save(); res.redirect('/'); }); } /* GET callback page */ router.get('/', function (req, response) { var options = { 'method': 'POST', 'hostname': auth.thingPlus.apiHost, 'port': null, 'path': auth.thingPlus.accessTokenUri, 'headers': { 'content-type': 'application/json' } }; var postRequest = http.request(options, function (res) { var chunks = []; res.on('data', function (chunk) { chunks.push(chunk); }); res.on('end', function () { var body = Buffer.concat(chunks).toString(); var json = JSON.parse(body); req.session.token = json.access_token; saveUserName(req.session.token, req, response); // response.redirect('/'); }); }); postRequest.write(JSON.stringify({ code: req.query.code, client_id: auth.thingPlus.clientId, client_secret: auth.thingPlus.clientSecret, redirect_uri: auth.thingPlus.redirectUri(req.headers.host), grant_type: 'authorization_code' })); postRequest.end(); }); module.exports = router;
- Auth.js
'use strict'; const apiHost = 'api.thingplus.net'; const baseUri = 'https://' + apiHost + '/v2/'; module.exports = { thingPlus: { clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, apiHost: apiHost, accessTokenUri: '/v2/oauth2/token', redirectUri: function (appAddress) { return 'http://' + appAddress + '/callback'; }, baseUri: baseUri, authorizationUri: baseUri + 'oauth2/authorize', gatewaysUri: baseUri + 'gateways', userUri: baseUri + 'users/me', signupUri: 'https://thingplus.net/signup/', scopes: [ 'user-profile', 'user-profile-read', 'gateway', 'gateway-read', 'gateway-update', 'timeline-read', 'tag', 'tag-read', 'rule', 'rule-read', 'service-read', 'site-read', 'billing-read' ] } }
- auth.js contains the address of Thing+ API
- login.js uses Thing+’s login page, and redirects to callback.js after you are logged in
- callback.js extracts the token from Thing+ and use it for your subsequent requests
Authorisation
Grab your jetpacks, we are going to work with Postman next
Register client ID and secret key
This step prepares your application for OAuth. This step does not involve user credentials, all credentials mentioned
are specific for your application. Normally you should only do this ONCE per application, unless
your application require complex ACL and roles management
- Open Chrome browser then Sign in to the Thing+ Portal
- Launch Postman
- Enable interceptor
- Getting a Client ID and Secret
- Select Getting a client ID and secret on the collection
- Select the Body tab
- Select raw
- Put your OAuth client ID in the
reqId
field. This should be unique across all applications connected
to Thing+ - Put your OAuth client secret in the
clientSecret
field. These two fields should not be exposed to anyone.
Keep it secret - Put the name of your applicaiton into the
name
field. This is used to identify your application. You
should put either your company or service name - Change the field
scopes
to determine the rights for your application. Read more about acceptable scopes
here - Click the Send button
- Result should display 201 Created
- Disable interceptor
Get OAuth Access token
This step authorizes the user with Thing+ via your application
An Access token Expires in 15 days. This may be changed later without prior notification. Please check back often
- Prepare login page for your application
- When the user logins, redirect them to this URL
https://api.thingplus.net/v2/oauth2/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}
- Replace
{CLIENT_ID}
with Thing+ OAuth Client ID to received at Step 1-4 - Replace
{REDIRECT_URI}
with your callback URL. This URL should we able to take a ‘code’ parameter. For
examplehttp://yoururl/?code={AUTHORIZATION_CODE}
- User should click the ‘Allow’ button
- Thing+ will redirectsback to your
{REDIRECT_URI}
with the “code” in query string - Exchange code for an OAuth Access token
This step should be automated in your code, the method below is for demonstration purpose only
- Select Exchange code for an OAuth Access token from the collection
- Select Body tab
- Select x-www-form-urlencoded
- Add
{AUTHORIZATION_CODE}
you received from the last step tocode
field - Fill in the client ID for your appliction in
client_id
- Fill in the client secret for your application in
client_secret
- Fill in the redirect URL. This URL should be the same with the redirect URL from step 2
- Click the ‘Send’ button
- Result should say “200 OK” and provide you with an access_token
Use authorisation in your app
When you are sending requests to Thing+ API, be sure to include the token you acquired from step 2 into the header with
Authorization: Bearer {AccessToken}
Putting it all together
You can now open your app, and click register or login, you’ll see a different page according to your login status.
Congratulations, you have succeeded in using Thing+’s authorisation mechanism and called some of its API.
What’s next
Add a virtual (or real) gateway, read more about the API, and try some of it.
Once you are comfortable with that, go ahead and create some rules.
You can refer to a ready-made application in this repository: thingplus-webapp
Have fun IoT-ing!