Skip to content

Getting started

Setting up an Identity Provider (IDP) for JWT-based authentication

The platform authenticate application clients, admins or operators using JWT-based tokens:

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

See: JSON Web Token (JWT)

For applications authentication to work, it is necessary to set up an Identity Provider (IDP) which is responsible for authenticating users and providing them with valid JWT tokens.
Nowadays, most organizations use a third-party IDP as it simplifies the process and ensures that the authentication is secure and compliant with industry standards.

If you need additional guidance, we recommend you to explore the following solutions:

Please note that if your organization's policies or compliance requirements do not allow re-using user's authentication tokens, you have the flexibility to implement an intermediate Security Token Service (STS) with the purpose of generating short-lived JWT authentication tokens for your users.

To authenticate WebSocket and HTTP clients, you need to set up authentication configuration in the authKey and adminKey properties.

Here's an example of an application that uses the RS256 JWT verification algorithm:

{
  ...
  "appId": "b5cvf368e1fc006e95521000764a5706",
  "name": "RS256 Example App",
  "description": "This is an example application that uses RS256 JWT-based authentication",
  "adminKey": {
    "key": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGBy...RzY1gqGo=\n-----END PUBLIC KEY-----",
    "algorithms": [
      "RS256"
    ],
    "issuer": []
  },
  "authKey": {
    "key": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGBy...RzY1gqGo=\n-----END PUBLIC KEY-----",
    "algorithms": [
      "RS256"
    ],
    "issuer": []
  },
  "adminSourceAddress": [
    "0.0.0.0/0"
  ],
  "clientSourceAddress": [
    "0.0.0.0/0"
  ],
  "enableWebSocketInboundTopic": true
}
Here is another using HS256 signatures:

RS256 vs HS256: What's The Difference?

...
  "adminKey": {
    "key": "cX1$wJT71Mölsd98.r97dC4DX",
    "algorithms": [
      "HS256"
    ],
    "issuer": []
  },
  "authKey": {
    "key": "xc5as087=)8a3p=?oidä+#ds$",
    "algorithms": [
      "HS256"
    ],
    "audience": "www.example.org",
    "issuer": []
  }
...

The subscription and publication workflow

Applications provide a secure and isolated environment for authenticated clients to receive messages on topics of their choosing:

const ACCESS_TOKEN = 'VALID JWT TOKEN HERE'

const ws = new WebSocket(`wss://genesis.r7.21no.de/apps/${APP_ID}?access_token=${ACCESS_TOKEN}`)
ws.onmessage = (event) => {
  // Parse incoming WebSocket messages
  const { topic, messageType, data } = JSON.parse(event.data)

  if (topic === 'main' && messageType === 'welcome') {
    // SESSION IS NOW READY, YOU CAN SUBSCRIBE TO APP TOPICS...
  }
}

subscribe or unsubscribe to/from application topics:

...
ws.onmessage = (event) => {
  // Parse incoming WebSocket messages
  const { topic, messageType, data } = JSON.parse(event.data)

  if (topic === 'main' && messageType === 'welcome') {
    // SESSION IS NOW READY, YOU CAN SUBSCRIBE TO APP TOPICS...

    ws.send(JSON.stringify({
      type: 'subscribe',
      data: {
        topic: 'dashboard1'
      }
    }))
  }
}
...
ws.send(JSON.stringify({
  type: 'unsubscribe',
    data: {
      topic: 'dashboard1'
    }
}))

To publish a message into an application topic, a valid JWT token with the Publisher role have to he provided in the Authorization header when calling the publish HTTP endpoint.
Additionally, the token must contain the allowedTopics claim with allowed topics or * to allow publishing to any topic.

Please note that the given token require to pass the verification process using the application adminKey configuration.

{
  "roles": [
    "Publisher"
  ],
  "allowedTopics": ["*"],
  "iat": 1673028921,
  "exp": 1673028926,
  "iss": "https://signer.example.com",
  "sub": "service-name"
}

Expected header format:

const headerValue = `Authorization: Bearer ${JWT_TOKEN_HERE}`

The following example demonstrates how to publish messages into an application topic using the Node.js. Examples include the issuing of JWT authentication tokens with different signing algorithms:

const REQUIRED_PUBLISHER_ROLE = 'Publisher'

const axios = require('axios').default
const jwt = require('jsonwebtoken')
const fs = require('fs')

function publish (config, topic, message) {
  console.info(`Publishing message into ${config.appId}/${topic} topic...`)

  const AUTH_TOKEN = getAuthToken(config.adminSigningKey, 5)
  const options = {
    method: 'POST',
    url: `https://${config.serverUrl}/api/topics/${config.appId}/publish`,
    headers: { 'Content-Type': 'application/json', Authorization: AUTH_TOKEN },
    data: { 
      topic, 
      message, 
      compress: false 
    }
  }

  return axios.request(options)
}

function getAuthToken (secret, expiresIn) {
  return 'Bearer ' + jwt.sign({ roles: [REQUIRED_PUBLISHER_ROLE], allowedTopics: ['*'] }, secret, {
    algorithm: 'RS256',
    expiresIn,
    issuer: 'https://signer.example.com',
    subject: 'service-name'
  })
}

publish(
  {
    serverUrl: 'genesis.r7.21no.de',
    appId: '00000d863vb5baa987e8768ed3drrr09',
    // private key of "adminKey.key" (public.pem)
    adminSigningKey: fs.readFileSync('./private.pem')
  },
  'main', ,
  'Hello World!'
)
  .then(() => console.log('Message published!'))
  .catch(err => console.error(err))

Creating RSA keys for JWT RS256 signing and verification using https://jwt-keys.21no.de:

curl "https://jwt-keys.21no.de/api/generate/RS256?bits=2048" | jq '.'

const REQUIRED_PUBLISHER_ROLE = 'Publisher'

const axios = require('axios').default
const jwt = require('jsonwebtoken')

function publish (config, topic, message) {
  console.info(`Publishing message into ${config.appId}/${topic} topic...`)

  const AUTH_TOKEN = getAuthToken(config.adminSigningKey, 5)
  const options = {
    method: 'POST',
    url: `https://${config.serverUrl}/api/topics/${config.appId}/publish`,
    headers: { 'Content-Type': 'application/json', Authorization: AUTH_TOKEN },
    data: { 
      topic, 
      message, 
      compress: false 
    }
  }

  return axios.request(options)
}

function getAuthToken (secret, expiresIn) {
  return 'Bearer ' + jwt.sign({ roles: [REQUIRED_PUBLISHER_ROLE], allowedTopics: ['*'] }, secret, {
    algorithm: 'HS256',
    expiresIn,
    issuer: 'https://signer.example.com',
    subject: 'service-name'
  })
}

publish(
  {
    serverUrl: 'genesis.r7.21no.de',
    appId: '00000d863vb5baa987e8768ed3drrr09',
    // value from "adminKey.key"
    adminSigningKey: '**********'
  },
  'main',
  'Hello World!'
)
  .then(() => console.log('Message published!'))
  .catch(err => console.error(err))
Creating secret key for JWT HS256 signing and verification using https://jwt-keys.21no.de:
curl "https://jwt-keys.21no.de/api/generate/HS256?bytes=32" | jq '.'

const REQUIRED_PUBLISHER_ROLE = 'Publisher'

const axios = require('axios').default
const jwt = require('jsonwebtoken')
const fs = require('fs')

function publish (config, topic, message) {
  console.info(`Publishing message into ${config.appId}/${topic} topic...`)

  const AUTH_TOKEN = getAuthToken(config.adminSigningKey, 5)
  const options = {
    method: 'POST',
    url: `https://${config.serverUrl}/api/topics/${config.appId}/publish`,
    headers: { 'Content-Type': 'application/json', Authorization: AUTH_TOKEN },
    data: { 
      topic, 
      message, 
      compress: false 
    }
  }

  return axios.request(options)
}

function getAuthToken (secret, expiresIn) {
  return 'Bearer ' + jwt.sign({ roles: [REQUIRED_PUBLISHER_ROLE], allowedTopics: ['*'] }, secret, {
    algorithm: 'ES512',
    expiresIn,
    issuer: 'https://signer.example.com',
    subject: 'service-name'
  })
}

publish(
  {
    serverUrl: 'genesis.r7.21no.de',
    appId: '00000d863vb5baa987e8768ed3drrr09',
    // private key of "adminKey.key"
    adminSigningKey: fs.readFileSync('./private.pem')
  },
  'main',
  'Hello World!'
)
  .then(() => console.log('Message published!'))
  .catch(err => console.error(err))

Creating ECDSA keys for JWT ES512 signing and verification https://jwt-keys.21no.de:

curl "https://jwt-keys.21no.de/api/generate/ES512" | jq '.'

Restricting access to your application endpoints

To enhance security, administrators have the option to restrict access to the platform by defining allowed CIDR masks or IP addresses for both administrators and clients. This added layer of protection ensures that only authorized networks have access to the platform, further safeguarding sensitive data and communications:

  • HTTP requests to server admin endpoints from IP addresses outside the ranges defined in adminSourceAddress are rejected with 401 HTTP Status Code
  • Client connections from IP addresses outside the ranges defined in clientSourceAddress are rejected and the WebSocket connection is immediately terminated

Configuration example:

...
"adminSourceAddress": [
  "10.1.0.0/16",
  "172.15.0.5",
  "172.15.0.10"
  "!172.15.6.66"
],
"clientSourceAddress": [
  "10.2.0.0/16",
  "!172.15.6.66"
]
...

Up to 10 CIDR ranges or IP addresses can be defined on each property

By default, the following ALLOW FROM ANYWHERE configuration is applied:

...
"adminSourceAddress": ["0.0.0.0/0"],
"clientSourceAddress": ["0.0.0.0/0"]
...

Please note that configurations settings above are oriented to restrict access to the applications and not to console or management APIs