3

I'm using twilio, Symfony 5 with the help of this tuto : https://www.twilio.com/blog/create-group-video-chat-app-symfony-php-twilio-react to create a video chat.

Upon entering the name of the room and launching the visio, I got the following error in the browser's console :

Unable to connect to Room: Invalid Access Token issuer/subject

I understand it's caused because the token generated by the route access_token must not be right.

I've created a normal twilio account then generated the API key, kept account sid, api key sid and api secret on my .env file.

Just in case I recreated a new api key to see if it changed something but didn't.

I've checked the doc for my error (https://www.twilio.com/docs/api/errors/20103) , but the solutions did not solve my problem.

Chat.js file:

import React, { useState } from "react";
import axios from "axios";
import Video from "twilio-video";

const Chat = () => {
    const [roomName, setRoomName] = useState('');
    const [hasJoinedRoom, setHasJoinedRoom] = useState(false);

    const joinChat = event => {
        event.preventDefault();
        if (roomName) {
            axios.post('/access_token', { roomName }, ).then((response) => {
                console.log('RESPONSE: ');
                console.log(response.data.token);
                connectToRoom(response.data.token);
                setHasJoinedRoom(true);
                setRoomName('');

            }).catch((error) => {
                console.log(error);
            })
        } else {
            alert("You need to enter a room name")
        }
    };

    const connectToRoom = (token) => {
        const { connect, createLocalVideoTrack } = Video;
        let connectOption = { name: roomName };

        connect(token, connectOption).then(room => {
            console.log(`Successfully joined a Room: ${room}`);
            const videoChatWindow = document.getElementById('video-chat-window');

            createLocalVideoTrack().then(track => {
                videoChatWindow.appendChild(track.attach());
            });
            room.on('participantConnected', participant => {
                console.log(`Participant "${participant.identity}" connected`);
                participant.tracks.forEach(publication => {
                    if (publication.isSubscribed) {
                        const track = publication.track;
                        videoChatWindow.appendChild(track.attach());
                    }
                });
                participant.on('trackSubscribed', track => {
                    videoChatWindow.appendChild(track.attach());
                });
            });
        }, error => {
            console.error(`Unable to connect to Room: ${error.message}`);
        });
    };

    return(
        <div className="container">
            <div className={"col-md-12"}>
                <h1 className="text-title">Symfony React Video Chat</h1>
            </div>

            <div className="col-md-6">
                <div className={"mb-5 mt-5"}>
                    {!hasJoinedRoom && (
                        <form className="form-inline" onSubmit={joinChat}>
                            <input type="text" name={'roomName'} className={"form-control"} id="roomName"
                                   placeholder="Enter a room name" value={roomName} onChange={event => setRoomName(event.target.value)}/>

                            <button type="submit" className="btn btn-primary">Join Room</button>

                        </form>
                    )}

                </div>



                <div id="video-chat-window"/>
            </div>
        </div>
    )
};

export default Chat;

TokenController file to generate user's token :

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Twilio\Jwt\AccessToken;
use Twilio\Jwt\Grants\VideoGrant;

class TokenController extends AbstractController
{

    /**
     * @Route("/token", name="token")
     */
    public function index()
    {
        return $this->render('token/index.html.twig', [
            'controller_name' => 'TokenController',
        ]);
    }

    /**
     * @param Request $req
     * @return \Symfony\Component\HttpFoundation\JsonResponse
     * @Route("access_token", name="access_token", methods={"POST"})
     */
    public function generateToken(Request $req) {

        $accountSid = getenv('ACCOUNT_SID');
        $apiKeySid = getenv('API_SID'); 
        $apiKeySecretSid = getenv('API_SECRET');

        $identity = uniqid();

        $roomName = json_decode($req->getContent());

        $token = new AccessToken(
            $accountSid,
            $apiKeySid,
            $apiKeySecretSid,
            3600,
            $identity
        );

        $grant = new VideoGrant();
        $grant->setRoom($roomName->roomName);
        $token->addGrant($grant);
        return $this->json(['token' => $token->toJWT()], 200);
    }
}

And this line in the **app.js** :

ReactDOM.render(<Chat/>, document.getElementById("root"));

With the proper imports.

UPDATE

Solution found! Thanks for the help !

getenv() method was the issue.

I now inject my .env info in my controller as parameters as follow :

services.yaml

parameters:
    app.env.ACCOUNT_SID: '%env(resolve:ACCOUNT_SID)%'
    app.env.API_KEY: '%env(resolve:API_KEY)%'
    app.env.API_SECRET: '%env(resolve:API_SECRET)%'

And recover them in the controller

Controller to recover the data


public function somename() {
    
    $sid = $this->getParameter('app.env.ACCOUNT_SID');
    $apiKey = $this->getParameter('app.env.API_KEY');
    $apiSecret = $this->getParameter('app.env.API_SECRET');

    $identity = uniqid();

    $token = new AccessToken(
        $sid,
        $apiKey,
        $apiSecret,
        3600,
        $identity
    );
}

And works like a charm

brn
  • 275
  • 1
  • 6
  • 17
  • There was a recent change in the `DotEnv` component and how it registers variables, have you checked that they are properly set on the controller?. – msg Oct 15 '20 at 16:44
  • What changes have they made ? Indeed If I paste my generated token on jwt.io the token is invalid. Any idea why? – brn Oct 16 '20 at 09:06
  • It doesn't register variables with `putenv` so your `getenv` calls may be returning `false` (see [here](https://stackoverflow.com/questions/63813272) and [here](https://stackoverflow.com/questions/57002564)). Try dumping the variables to check it out. There are no safeguards in the twilio-sdk for empty values. – msg Oct 16 '20 at 09:09
  • Alright thanks for your help, the issue come form there. The token is generated without my account id, api key and secret due to the getenv. I'll try finding my way around – brn Oct 16 '20 at 09:49
  • Create parameters in your config and [inject `ParameterBag` or the individual values](https://symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service). – msg Oct 16 '20 at 09:51
  • what is API_KEY ? is it different from API_KEY_SID ? – Hazem Taha Nov 21 '21 at 12:29

1 Answers1

6

may be it could help those who has this issue.

I have found that the default region used in the exemple code was "US1" and that I have create my API KEY in the region "IE1". Creating and using an API KEY made in the US1 region has resolve my problem and has made the exemple code work.

Now because I am in Europe region, i will try to figure how to set the default region as "IE1" in the SDK.

Sok Hout SAO
  • 61
  • 1
  • 4
  • 1
    Did you find out how it works? There is an option `{region: 'ie1'}` in the AccessToken constructor but that doesn't work for me either. – dennis Feb 17 '22 at 02:49
  • 2
    I got an answer from Twilio Support - Video is not a regionalizable product so any API key generated for a region other than US1 won't work. – dennis Feb 17 '22 at 16:43