# Create server API with Token

**URL:** <https://discourse.nodered.org/t/create-server-api-with-token/64435>\
**Category:** General\
**Tags:** http-request, security\
**Created:** [27 June 2022 20:48 UTC](https://discourse.nodered.org/t/create-server-api-with-token/64435 "2022-06-27T20:48:21Z")\
**Posts on this page:** 1\
**Showing post:** 8

<div class="post-metadata">

**Author:** ![UnborN](https://avatars.discourse-cdn.com/v4/letter/u/4491bb/32.png) [@UnborN](https://discourse.nodered.org/u/UnborN)\
**Post date:** [7 July 2022 21:01 UTC](https://discourse.nodered.org/t/create-server-api-with-token/64435/8 "2022-07-07T21:01:04Z")

</div>

> [@TotallyInformation](#):
>
> NGINX to proxy the authentication

I was thinking of recommending a proxy for the authentication. Surely it would be more secure. The thing is that most of the more advanced features of Nginx seem to be included in nginx Plus which needs a yearly license ? Thats why i started reading about [Kong Gateway](https://discourse.nodered.org/t/node-red-on-docker-compose-with-kong-gateway/63417/4) which is based on nginx but with more free plugins/features. and we love our free and open-source software lol

@LinkView_Maze that looks like an interesting solution ..  
that **node-red-contrib-httpauth** node is 7 years old! ... well ... if it works it works  
I didnt install the missing nodes but i tried to understand the flow  
As it is, you are saving a token in a file and then with the api request you see if it matches ==  
How does that work with multiple users my friend ? 😉  
maybe im wrong but in some cases you will be trying to match the token of user2 with the saved token of user1 ?

* * *

I was playing around a bit with the JWT example

added an sqlite db with a `Users` table for the credential check

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/d/8/d8d82d97bcc106737585ba2b1fd365a7fc4cd95e.png)

added the flow part for the API and the verification of the jwt token

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/b/1/b1dcde4d8c5589b510262ba8274789c5a8d2f740.png)

Used the [Postman](https://www.postman.com/) software for testing

Reguest to `https://<node-red-ip>:1880/login`  
with a JSON body `{ "username": "user1", "password": "abc123" }`  
returns the token for user1 (with test expiration 30 seconds)

`{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ1c2VyMSIsImlhdCI6MTY1NzE4ODI4NCwiZXhwIjoxNjU3MTg4MzQ0fQ.VI4uxgAUoQ9NoPv0YIKpXJQxiEw82zxj937CyjX"} `

which can be used for the API requests with Bearer Token Authorization  
`https://<node-red-ip>:1880/api`

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/6/96861bc6d71a649f0d931f007d333d71411d0919.png)

Result :

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/d/6d9db7ace6180200320e8fe7f5f540d14a4b021e.png)

After time-limit

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/5/5/55addf084200e9450331bc6cd30295f862423779.png)

**Example Flow**

```auto
[{"id":"7fdf168306547292","type":"function","z":"54efb553244c241f","name":"sign JWT","func":"// we have a user match from the db \nif (msg.payload.length == 1) {\n\n// the result from the db (should be a single element)\n let user = {\n id: msg.payload[0].id,\n username: msg.payload[0].username\n } \n\n jwt.sign( user , 'secretkey', { expiresIn: '30s' }, (err, token) => {\n msg.payload = { token };\n });\n\n}\n\nelse {\n msg.payload = \"Unauthorized\"\n msg.statusCode = \"403\"\n\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"jwt","module":"jsonwebtoken"}],"x":800,"y":1620,"wires":[["7ac97ab8768e5b00"]]},{"id":"9b8747bdc731f290","type":"http in","z":"54efb553244c241f","name":"","url":"/login","method":"post","upload":false,"swaggerDoc":"","x":190,"y":1620,"wires":[["6c2312a5ea74a554","1706dfda582cf6db"]]},{"id":"6c2312a5ea74a554","type":"debug","z":"54efb553244c241f","name":"debug 1","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":340,"y":1560,"wires":[]},{"id":"1706dfda582cf6db","type":"function","z":"54efb553244c241f","name":"prepare sql","func":"let username = msg.payload.username;\nlet password = msg.payload.password;\n\nmsg.topic = `SELECT * FROM Users WHERE username=$1 AND password=$2`\nmsg.payload = [username, password] // sql params\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":1620,"wires":[["d163b1c99735e322","d6a5e85c9ca7735d"]]},{"id":"d163b1c99735e322","type":"debug","z":"54efb553244c241f","name":"debug 2","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":540,"y":1560,"wires":[]},{"id":"7ac97ab8768e5b00","type":"http response","z":"54efb553244c241f","name":"","statusCode":"","headers":{},"x":950,"y":1620,"wires":[]},{"id":"41315abeb16b886a","type":"http in","z":"54efb553244c241f","name":"","url":"/api","method":"get","upload":false,"swaggerDoc":"","x":200,"y":1800,"wires":[["36d167c6a4ed9bdd","967236e3fb342e56"]]},{"id":"36d167c6a4ed9bdd","type":"function","z":"54efb553244c241f","name":"verify JWT","func":"// FORMAT OF TOKEN\n// Authorization: Bearer <access_token>\n\nconst bearerHeader = msg.req.headers['authorization'];\n// Check if bearer is undefined\nif (typeof bearerHeader !== 'undefined') {\n // Split at the space\n const bearerToken = bearerHeader.split(' ')[1];\n // Log the token in Debug window\n node.warn({bearerToken});\n\n // Verify Token\n jwt.verify(bearerToken, 'secretkey', (err, authData) => {\n if (err) {\n msg.statusCode = \"403\";\n msg.payload = err;\n node.send(msg)\n } else {\n ///// API fake data /////\n msg.payload = {\n data: 'This is the data',\n user: authData\n };\n node.send(msg)\n }\n });\n\n\n} else {\n // Forbidden\n msg.payload = \"Unauthorized\"\n msg.statusCode = \"403\";\n node.send(msg)\n}\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"jwt","module":"jsonwebtoken"}],"x":410,"y":1800,"wires":[["97b4bf6ee7c5794d","2ab0196e6418cd2a"]]},{"id":"97b4bf6ee7c5794d","type":"http response","z":"54efb553244c241f","name":"","statusCode":"","headers":{},"x":610,"y":1800,"wires":[]},{"id":"967236e3fb342e56","type":"debug","z":"54efb553244c241f","name":"debug 4","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":320,"y":1740,"wires":[]},{"id":"2ab0196e6418cd2a","type":"debug","z":"54efb553244c241f","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":520,"y":1740,"wires":[]},{"id":"d6a5e85c9ca7735d","type":"sqlite","z":"54efb553244c241f","mydb":"26cd2be8.1dfacc","sqlquery":"msg.topic","sql":"","name":"Sqlite","x":590,"y":1620,"wires":[["7fdf168306547292","5177c66563862a08"]]},{"id":"5177c66563862a08","type":"debug","z":"54efb553244c241f","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":1560,"wires":[]},{"id":"26cd2be8.1dfacc","type":"sqlitedb","db":"c:\\Users\\User\\.node-red\\EnergyMeters.db","mode":"RWC"}]

```

**[EDIT]**  
As Julian said .. additional layers of security may be needed depending on the application. Like :

1. hashing the passwords so they are not saved as clear text in the database
2. using https so the data are sent encrypted between the server and client
3. reverse proxy (rate limiting the requests, ip restrictions etc)

---

_[View the full topic](https://discourse.nodered.org/t/create-server-api-with-token/64435)._
