Giter Site home page Giter Site logo

agora-token-service's Introduction

Agora Token Webservice

Written in Golang, using Gin framework to create a RESTful webservice for generating user tokens for use with the Agora.io platform.

Agora Advanced Guide: Token Management.

One-Click Deployments

Railway Render Heroku
Deploy on Railway Deploy to Render Deploy to Heroku

How to Run

Set the APP_ID, APP_CERTIFICATE and CORS_ALLOW_ORIGIN env variables.

cp .env.example .env
go run cmd/main.go

Without using .env, you can also set the environment variables as such:

APP_ID=app_id APP_CERTIFICATE=app_cert CORS_ALLOW_ORIGIN=allowed_origins go run cmd/main.go

The pre-compiled binaries are also available in releases.

Docker

#1. To build the container with app id and certificate:

docker build -t agora-token-service --build-arg APP_ID=$APP_ID --build-arg APP_CERTIFICATE=$APP_CERTIFICATE --build-arg CORS_ALLOW_ORIGIN=$ALLOWED_ORIGINS .

#2. Run the container

docker run agora-token-service

Note: for testing locally

docker run -p 8080:8080 agora-token-service

Makefile

Build and run the docker container using make. The Makefile simplifies the build and run process by reducing them to a single command. The Makefile uses the .env file to add the list of --build-arg's to the docker build command and executes the docker run command after the build is completed. To avoid unnecessary rebuilds of the token server, the Makefile sets a build_marker target to watch the dockerfile, .go source code, and .env file. This enables a single command to build and run the container that only rebuilds as needed.

#1. Set the APP_ID, and APP_CERTIFICATE as env variables. Optionaly SERVER_PORT and CORS_ALLOW_ORIGIN can also be set as env variables, but they not required and there are defaults if they are not detected.

cp .env.example .env

#2. Run make

make

Execute the cleanup target to force a rebuild:

make cleanup

Endpoints

Ping

endpoint structure

/ping

response:

{"message":"pong"} 

getToken

The getToken API endpoint allows you to generate tokens for different functionalities of the application. This section provides guidelines on how to use the getToken endpoint using HTTP POST requests.

Endpoint URL

POST /getToken

Request Body

The request body should contain a JSON payload with the required parameters for generating the tokens.

The following are the supported token types along with their required parameters:

  1. RTC Token:

    To generate an RTC token for video conferencing, include the following parameters in the request body:

    {
        "tokenType": "rtc",
        "channel": "your-channel-name",
        "role": "publisher",  // "publisher" or "subscriber"
        "uid": "your-uid",
        "expire": 3600 // optional: expiration time in seconds (default: 3600)
    }
  2. RTM Token:

    To generate an RTM token for Real-Time Messaging, include the following parameters in the request body:

    {
        "tokenType": "rtm",
        "uid": "your-uid",
        "channel": "test", // optional: passing channel gives streamchannel. wildcard "*" is an option.
        "expire": 3600 // optional: expiration time in seconds (default: 3600)
    }
  3. Chat Token:

    To generate a chat token, include the following parameters in the request body:

    {
        "tokenType": "chat",
        "uid": "your-uid", // optional: for generating a user-specific chat token
        "expire": 3600 // optional: expiration time in seconds (default: 3600)
    }

Response

Upon successful generation of the token, the API will respond with an HTTP status code of 200 OK, and the response body will contain the token in a JSON key "token".

If there is an error during token generation or if the request parameters are invalid, the API will respond with an appropriate HTTP status code and an error message in the response body.

Sample Usage

Here's an example of how to use the getToken API endpoint with a POST request using cURL:

Request:

curl -X POST -H "Content-Type: application/json" -d '{
    "tokenType": "rtc",
    "channel": "my-video-channel",
    "role": "publisher",
    "uid": "user123",
    "expire": 3600
}' "https://your-api-domain.com/getToken"

Reponse:

{
  "token": "007hbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsInN1YiI6InVzZXIxMjMiLCJpYXQiOjE2MzEwNTU4NzIsImV4cCI6MTYzMTA1OTQ3Mn0.3eJ-RGwIl2ANFbdv4SeHtWzGiv6PpC3i0UqXlHfsqEw"
}

Deprecated Methods

The following methods are deprecated but still operational. While they continue to work for backward compatibility, it is advised to refrain from using them in new implementations due to potential future removal or replacement with more efficient alternatives.

RTC Token

The rtc token endpoint requires a tokenType (uid || userAccount), channelName, and the user's uid (type varies based on tokenType). expiry(optional) Pass an integer to represent the token lifetime in seconds.

endpoint structure

/rtc/:channelName/:role/:tokenType/:rtcuid/?expiry=3600

response:

{"rtcToken":"007rtc-token-djfkaljdla"} 

RTM Token

The rtm token endpoint requires the user's rtmuid. expiry(optional) Pass an integer to represent the privelege lifetime in seconds. endpoint structure

/rtm/:rtmuid/?expiry=3600

response:

{"rtmToken":"007rtm-token-djfkaljdla"} 

RTM + RTC Tokens

The rte token endpoint generates both the rtc and rtm tokens with a single request. This endpoint requires a tokenType (uid || userAccount), channelName, the user's rtcuid (type varies String/Int based on tokenType) and rtmuid which is a String. Omitting rtmuid will assume it's the same as rtcuid. expiry(optional) Pass an integer to represent the token lifetime in seconds.

endpoint structure

/rte/:channelName/:role/:tokenType/:rtcuid/:rtmuid/?expiry=3600

response:

{
  "rtcToken":"007rtc-token-djfkaljdla",
  "rtmToken":"007rtm-token-djfkaljdla" 
} 

Chat Tokens

endpoint structure

app privileges:

chat/app/?expiry=3600

user privileges:

/chat/account/:chatid/?expiry=3600

expiry is an optional parameter for both.

response:

{
  "chatToken":"007chat-token-djfkaljdla"
} 

Contributions

Contributions are welcome, please test any changes to the Go code with the following command:

APP_ID=<YOUR_APP_ID> APP_CERTIFICATE=<YOUR_APP_CERT> go test -cover github.com/AgoraIO-Community/agora-token-service/service

agora-token-service's People

Contributors

akshatvg avatar blingblingdev avatar dependabot[bot] avatar digitallysavvy avatar ekaansharora avatar maxxfrazer avatar mupati avatar stefanpgr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

agora-token-service's Issues

Issue with muting itself

I am working on webgl build but every time the user mute itself calls and it don't unmute at all as well

message: AgoraRTCError CAN_NOT_GET_GATEWAY_SERVER: invalid token, authorized failed

Good morning,

I have a Flutter project for the web and I updated the version of agora_rtc_engine to 6.2.6, which uses the "iris-web-rtc_n423_w4182_0.3.0.js" library to work this version on the WEB, as the last version that supported it was 5.3 .1 (which I use in my project), and after updating to the new version the token generated by this project "agora-token-service" is not validating, it returns the message "AgoraRTCError CAN_NOT_GET_GATEWAY_SERVER: invalid token, authorized failed". However, if I take this token and use it in an old version, it works.

Another test I did was go to console.agora.io/token and generate a token there to test in the new version (6.2.6) and it works. The project connects to the room and opens the camera.

What am I doing wrong?

import 'dart:async';

import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/material.dart';
import 'dart:html' as html;

const appId = "my certification";
const token ="token generated by agora-token-service";
const channel = "channelId";

void main() => runApp(const MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);

@OverRide
State createState() => _MyAppState();
}

class _MyAppState extends State {
int? _remoteUid;
bool _localUserJoined = false;
late RtcEngine _engine;

@OverRide
void initState() {
super.initState();
initNow();
}

Future initNow() async {
// retrieve permissions
try {
await html.window.navigator.getUserMedia(audio: true, video: true);
} catch (e) {
print('Error checking camera: $e');
}

 //create the engine
 _engine = createAgoraRtcEngine();
 await _engine.initialize(const RtcEngineContext(
   appId: appId,
   channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
 ));

 _engine.registerEventHandler(
   RtcEngineEventHandler(
     onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
       debugPrint("local user ${connection.localUid} joined");
       setState(() {
         _localUserJoined = true;
       });
     },
     onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
       debugPrint("remote user $remoteUid joined");
       setState(() {
         _remoteUid = remoteUid;
       });
     },
     onUserOffline: (RtcConnection connection, int remoteUid,
         UserOfflineReasonType reason) {
       debugPrint("remote user $remoteUid left channel");
       setState(() {
         _remoteUid = null;
       });
     },
     onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
       debugPrint(
           '[onTokenPrivilegeWillExpire] connection: ${connection.toJson()}, token: $token');
     },
   ),
 );

 await _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
 await _engine.enableVideo();
 print('-------------------------- before video');
 await _engine.startPreview();
 print('-------------------------- then video');

 print(token);
 await _engine.joinChannel(
   token: token,
   channelId: channel,
   uid: 123456789,
   options: const ChannelMediaOptions(),
 );

}

@OverRide
void dispose() {
super.dispose();

 _dispose();

}

Future _dispose() async {
await _engine.leaveChannel();
await _engine.release();
}

// Create UI with local view and remote view
@OverRide
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Now Video Call'),
),
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.topLeft,
child: SizeBox(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(uid: 0),
),
)
: const CircularProgressIndicator(),
),
),
),
],
),
);
}

// Display remote user's video
Widget _remoteVideo() {
if (_remoteUid != null) {
return AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine,
canvas: VideoCanvas(uid: _remoteUid),
connection: const RtcConnection(channelId: channel),
),
);
} else {
return const Text(
'Please wait for remote user to join',
textAlign: TextAlign.center,
);
}
}
}

Generating Subscriber token type fails

When trying to generate token type SUBSCRIBER
`const Role = {
// for live broadcaster
PUBLISHER: 1,

// default, for live audience
SUBSCRIBER: 2,

};Getting error Unhandled error TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type number (0)
at new NodeError (internal/errors.js:322:7)
at Function.from (buffer.js:334:9)
at Object.ByteBuf.that.putString (/workspace/agora/AccessToken2.js:318:37)
at ServiceRtc.pack (/workspace/agora/AccessToken2.js:63:47)
at /workspace/agora/AccessToken2.js:233:65
at Array.forEach ()
at AccessToken2.build (/workspace/agora/AccessToken2.js:232:38)
at Function.buildTokenWithUserAccount (/workspace/agora/RtcTokenBuilder2.js:64:22)
at Function.buildTokenWithUid (/workspace/agora/RtcTokenBuilder2.js:31:21)
at /workspace/agoratoken.js:34:39 {
code: 'ERR_INVALID_ARG_TYPE'`

Generation Code :
`const channelName = uid;
const tokenTTL = 60 * 60 * 23; // 23 hours

const expires = Date.now() + (tokenTTL * 1000);
const expiresInSeconds = Math.floor(expires / 1000);
const viewerToken = RtcTokenBuilder.buildTokenWithUid(
appID,
appCertificate,
channelName,
0,
RtcRole.SUBSCRIBER,
expiresInSeconds,
);
console.log("Viewer token: " + viewerToken);`

Add support for optional trailing slash.

Go Gin supports optional trailing slashes in the API endpoints.

I faced a CORS error and did not realise for some time that this was because I missed a / in the endpoint on the client side.

On the server side, there was a 301 response which was a redirect internally within Go.

If we could make it optional, it would be a good enhancement.

Heroku Deploy - 404

Trying to test out the single click deploy and everything builds fine.

When I try to ping the URL given by heroku to my app, I get 404.

I am assuming there is something obvious I am missing.

Any advice is much appreciated.

Getting error for TokenType because it is not clearly mentioned how to get the token with example links

@maxxfrazer @digitallysavvy @EkaanshArora @hxingpax @Mupati
I tried this:
https://nikah----match.herokuapp.com/rtc/:test/:publisher/:userAccount/:0/?expireTime

and got:
{"error":"Error Generating RTC token - failed to generate RTC token for Unknown Tokentype: :userAccount","status":400}

Also tried:
https://nikah----match.herokuapp.com/rtc/:test/:publisher/:uid/:0/?expireTime

and got:
{"error":"Error Generating RTC token - failed to generate RTC token for Unknown Tokentype: :uid","status":400}

You should at least have some example links in the Readme file.

Deploy to Heroku is Broken

I am trying to deploy to Heroku but the deployment fails with the following error:

-----> Building on the Heroku-22 stack
-----> Determining which buildpack to use for this app
-----> Go app detected
-----> Fetching jq... done
-----> Fetching stdlib.sh.v8... done
-----> 
       Detected go modules via go.mod
-----> 
       Detected Module Name: github.com/AgoraIO-Community/agora-token-service
-----> 
-----> New Go Version, clearing old cache
-----> Installing go1.16.15
-----> Fetching go1.16.15.linux-amd64.tar.gz... done
-----> Determining packages to install
go: downloading github.com/AgoraIO-Community/go-tokenbuilder v1.0.0
go: downloading github.com/gin-gonic/gin v1.7.2
go: downloading github.com/joho/godotenv v1.3.0
go: downloading github.com/gin-contrib/sse v0.1.0
go: downloading github.com/mattn/go-isatty v0.0.12
go: downloading github.com/go-playground/validator/v10 v10.4.1
go: downloading github.com/golang/protobuf v1.3.3
go: downloading github.com/ugorji/go v1.1.7
go: downloading github.com/ugorji/go/codec v1.1.7
go: downloading gopkg.in/yaml.v2 v2.2.8
go: downloading golang.org/x/sys v0.1.0
go: downloading github.com/go-playground/universal-translator v0.17.0
go: downloading github.com/leodido/go-urn v1.2.0
go: downloading golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
go: downloading github.com/go-playground/locales v0.13.0
       
       Detected the following main packages to install:
       		github.com/AgoraIO-Community/agora-token-service/cmd
       
-----> Running: go install -v -tags heroku github.com/AgoraIO-Community/agora-token-service/cmd 
github.com/gin-gonic/gin/internal/bytesconv
github.com/go-playground/locales/currency
github.com/AgoraIO-Community/go-tokenbuilder/accesstoken
github.com/gin-contrib/sse
github.com/AgoraIO-Community/go-tokenbuilder/rtctokenbuilder
github.com/AgoraIO-Community/go-tokenbuilder/rtmtokenbuilder
github.com/gin-gonic/gin/internal/json
github.com/go-playground/locales
github.com/go-playground/universal-translator
github.com/leodido/go-urn
golang.org/x/crypto/sha3
github.com/golang/protobuf/proto
github.com/go-playground/validator/v10
github.com/ugorji/go/codec
gopkg.in/yaml.v2
golang.org/x/sys/unix
# golang.org/x/sys/unix
../codon/tmp/cache/go-path/pkg/mod/golang.org/x/[email protected]/unix/syscall.go:83:16: undefined: unsafe.Slice
../codon/tmp/cache/go-path/pkg/mod/golang.org/x/[email protected]/unix/syscall_linux.go:2255:9: undefined: unsafe.Slice
../codon/tmp/cache/go-path/pkg/mod/golang.org/x/[email protected]/unix/syscall_unix.go:118:7: undefined: unsafe.Slice
../codon/tmp/cache/go-path/pkg/mod/golang.org/x/[email protected]/unix/sysvshm_unix.go:33:7: undefined: unsafe.Slice
note: module requires Go 1.17
github.com/joho/godotenv
github.com/gin-gonic/gin/binding
github.com/gin-gonic/gin/render
 !     Push rejected, failed to compile Go app.
 !     Push failed

Questions about token service

Hello ๐Ÿ‘‹

I just wanted to ask two questions about the agora token service:

  1. Since audio/video call features of flutter apps require notifications, and many devs are using FCM (Firebase Cloud Messaging) for this option, wouldn't it make more sense to use a firebase cloud function for generating the tokens? Instead of this golang service (which I personally like) ?

  2. If this golang service is mandatory (and firebase cloud functions are discouraged), which service would be better to deploy for production? Heroku? Railway? Render? And which plan would be the ideal one?

Thanks in advance!

Fixed Error on token: but Can use the UIKit on assembleRelease mode. Works on debug mode only...

I am using the agora-rn-uikit and this is how my component is
import AgoraUIKit from 'agora-rn-uikit';
import { PropsInterface } from 'agora-rn-uikit/src/Contexts/PropsContext';
import React, { memo } from 'react';
import { SafeAreaView } from 'react-native';

import { agora_appid } from '@env';

const Call = ({ route }) => {
console.log(route)
const CONFIG: PropsInterface = {
callbacks: {
EndCall: () => {},
},
rtcProps: {
appId: agora_appid,
channel: 'test',
tokenUrl: 'https://agora-token-server-yxfs.onrender.com'
// channel: route.params.channel,
// token: route.params.token,
// token: '006f02c42153ca346a69b4495043c8491a6IAB1h7fK9rpjoAb0H7OYBJvnca/iUQUf8BG2DeCgQR57RuAS44q379yDIgDERRUFANzHYwQAAQCQmMZjAgCQmMZjAwCQmMZjBACQmMZj'
// tokenUrl: 'https://agora-token-service-production-c1ac.up.railway.app'
},
rtmProps: {
},
rtmCallbacks: {},
};
return (
<SafeAreaView style={{ flex: 1 }}>
<AgoraUIKit
connectionData={CONFIG.rtcProps}
settings={{
displayUsername: true,
}}
// rtcCallbacks={CONFIG.callbacks}
/>

);
};

export default memo(Call);

and

when I use the tokenUrl props I done even get any response. the ui will just be blank black

Can anyone suggest how I use the token generated properly??

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.