Server
We will start our journey by creating a simple backend using ExpressJS. The main goal is to authenticate in VPaaS and make REST API requests to generate meetings and participants with access to those meetings.
You can download the starter code from Step.01-Exercise-Server.
Register the application in VPaaS
The first step is to register our application with the VPaaS service. Once
registered, we will obtain a clientId
.
First, we will create a key pair consisting of a private key and a public key:
$ openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -out privateKey.pem
Keep the privateKey.pem
in a secure location and never share it with anybody.
It will be used to authenticate the application against the Pexip VPaaS service.
The minimum key size for VPaaS is 4096 bits.
The following step will extract the public key into a separate file:
$ openssl pkey -in privateKey.pem -pubout > publicKey.pem
Now you will need to contact Pexip and send the publicKey.pem
file. Pexip will
register the application and, once the application is registered, you will be
assigned a clientId
.
To authenticate your backend into the VPaaS service you will need both the
privateKey.pem
and the clientId
.
Configuration file
You need to define the configuration file in ./server/config/default.json
. The
application will read this file and use the parameters for authenticating into
VPaaS.
Here is an example of the configuration file in which you will need to modify at
least the clientId
:
{
"vpaas": {
"apiAddress": "https://crud.pexip.rocks",
"credentials": {
"clientId": "euopeke6qei65m5prjcvnqv2lq",
"privateKeyPath": "./config/credentials/privateKey.pem"
}
},
"server": {
"port": 3000
}
}
The meaning of these parameters is the following:
apiAddress
: URL address for the VPaaS API.clientId
: Id generated after registering the app into VPaaS.privateKeyPath
: Path to where the private key is located.port
: The port to bind to the server.
Create the JWT and generate an access token
Now we will implement our first function to authenticate into the VPaaS service.
We will open the file ./server/src/index.ts
and define a function to generate
a JWT (JSON Web Token). This token will be signed with the private key and will
contain the clientId
, the expiration time and the scope
with the permissions
that we want to use.
First of all, we need to make some imports:
import express, {type RequestHandler} from 'express';
import {v4 as uuidv4} from 'uuid';
import jwt from 'jsonwebtoken';
import {createApi, withToken} from '@pexip/vpaas-api';
And now we implement the function:
const createJwt = (): string => {
const apiAddress: string = config.get('vpaas.apiAddress');
const authEndpoint = `${apiAddress}/oauth/token`;
const clientId = config.get('vpaas.credentials.clientId');
const privateKey = fs.readFileSync(
config.get('vpaas.credentials.privateKeyPath'),
);
const scope = [
'meeting:create',
'meeting:read',
'meeting:write',
'participant:create',
'participant:read',
'participant:write',
];
const requestId = uuidv4();
const token = jwt.sign(
{
iss: clientId, // Application Client UUID
sub: clientId, // Application Client UUID
aud: authEndpoint,
scope: scope.join(' '),
},
privateKey,
{
algorithm: 'RS384',
expiresIn: '60s',
jwtid: requestId,
},
);
return token;
};
Once a JWT is created, our server will send it to VPaaS to create an
access_token
. Finally, the access_token
will be used to authenticate each
request that our server sends to VPaaS.
To simplify the authentication process, we will make use of the
@pexip/vpaas-api
library. This library is an abstraction layer that will take
care of a lot of things for us.
We will use two functions from the @pexip/vpaas-api
library: withToken
and
createApi
. The function withToken
will take as parameters the function that
we have previously created (createJWT
) and the VPaaS URL.
const api = withToken(createJwt, config.get('vpaas.apiAddress'))(createApi());
With this piece of code our server will be able to authenticate into VPaaS and
use the API through the api
object. When the access_token
expires, the
library will generate a new token automatically.
HTTP endpoints
We have implemented authentication into VPaaS and the api
object. Now we will
continue editing the file ./server/src/index.ts
and implement three HTTP
endpoints:
- Get ApiAddress: This will be used to return the VPaaS URL that is defined in the configuration file.
- Create Meeting: This endpoint will create an ephemeral meeting in the VPaaS service. . It will be removed automatically once the last participant leaves.
- Create Participant for a Meeting: This is used to create one-time credentials to allow access to a meeting.
During the rest of this lesson we will explore more deeply what each of these endpoints does.
Define all the endpoints after the the middleware definition for CORS
app.use(cors({...}))
. If you don't do this, the cross-site calls to the server
will fail.
Before implementing the endpoints we need to import the RequestHandler
:
import express, {type RequestHandler} from 'express';
Get the ApiAddress
Our frontend application will connect to two different servers: our server and VPaaS. This means that it needs to know two different URLs.
We have two ways of defining these URLs:
- Defining the two URLs in a configuration file in the frontend.
- Defining only our server URL in the frontend and make a request to recover the VPaaS URL.
If we choose the first option, this endpoint is not needed, but we would have to define the VPaaS URL both in the backend and in the frontend.
app.get('/api-address', (async (req, res) => {
res.send(config.get('vpaas.apiAddress'));
}) as RequestHandler);
A call to this endpoint will return the VPaaS URL, e.g.:
https://crud.pexip.rocks
Your VPaaS URL may be different, dependent on the region chosen when you registered your application with the VPaaS service.
Create meeting
In VPaaS meetings are created on-demand and, once the last participant leaves, the meeting is removed.
To create a meeting we use the function api.create()
and return the response
to the frontend:
app.post('/meetings', (async (req, res) => {
try {
const response = await api.create();
if (response.status === 200) {
return res.json(response.data);
} else {
return res.status(500).send('Cannot create the meeting');
}
} catch (error) {
return res.status(500).send('Cannot create the meeting');
}
}) as RequestHandler);
The response to this request will have the following structure:
{
"id": "mzcmlqfurei65dh2cimd3aag5q",
"status": "ready",
"created_at": "2024-01-16T16:07:51.436736",
"updated_at": "2024-01-16T16:07:51.436742"
}
This endpoint should be only used to create the meeting. After that, the same
id
should be shared with the rest of the participants.
Create participant for meeting
In the previous section we developed an endpoint to create a meeting.
However, before a user can join the meeting, they have to create a
participant
. A participant
is a one-time credential that is used to join a
specific meeting.
In the same way as before, we will use the @pexip/vpaas-api
library and in
particular the api.participants({meetingId: string})
function. The value of
the meetingId
input parameter is the id that we generated in the previous
section:
app.post('/meetings/:meetingId/participants', (async (req, res) => {
try {
const response = await api.participants({meetingId: req.params.meetingId});
if (response.status === 200) {
return res.json(response.data);
} else {
return res
.status(500)
.send(
`Cannot get participants from the meeting ${req.params.meetingId}`,
);
}
} catch (error) {
return res
.status(500)
.send(`Cannot get participants from the meeting ${req.params.meetingId}`);
}
}) as RequestHandler);
The result of this call will have the following structure:
{
"meeting_id": "mzcmlqfurei65dh2cimd3aag5q",
"id": "qnol2zvurei65axj5zspbecjne",
"participant_secret": "MMh8jo7t4DE-Eg2L1hULA7Gc6sEZW_Go0PpuGODZeuM",
"status": "ready",
"created_at": "2024-01-16T16:08:40.651578",
"updated_at": "2024-01-16T16:08:40.651585"
}
Through this call, the user will get the id
of the participant and the
participant_secret
that he must use to join the meeting.
Run the server
After all these modifications we have completed the implementation of a simple server.
In the next lessons we will work on the frontend, and will use the endpoints that we have just developed.
Now that everything is in place, we can run the server through the following command:
cd server
npm start
We have several ways to test the server, we can use a graphical interface such as Postman, but the faster way is to use cURL.
Get the API address:
$ curl https://localhost:3000/api-address --insecure
Create a meeting:
$ curl -X POST https://localhost:3000/meetings --insecure
Create a participant for the previous meeting:
$ curl -X POST https://localhost:3000/meetings/<meetingId>/participants --insecure
Now that our backend is ready, we will continue developing our application frontend.
You can compare your code with the solution in Step.01-Solution-Server. You can also check the differences with the previous lesson in the git diff.