Use tokens
Updated
Retrieve tokens generated by an authentication token server to securely connect to Agora.
To protect your business, it is best practice to authenticate every client that joins a channel. This guide explains how to fetch an authentication token from your token server, use it to join a channel, and renew the token when it expires.
Understand the tech
When a user attempts to connect to an Agora channel, your app retrieves a token from the token server in your security infrastructure. Your app then sends this token to Agora SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.
The following figure shows the call flow you implement to create step-up-authentication with Agora Video Calling:
Prerequisites
Before starting, ensure that you have:
-
Implemented the Quickstart in your project.
-
Deployed a token server using either of the following guides:
Implement basic authentication
This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.
Use a token to join a channel
This section shows you how to integrate token authentication in your app.
-
Add the following to the
pubspec.yamlfile, underdependencies:dependencies: # Agora Flutter SDK, use the latest version of agora_rtc_engine agora_rtc_engine: ^6.3.0 # For making http requests http: ^0.13.5 -
Replace the contents in
/lib/main.dartwith the following code.import 'dart:convert'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() => runApp(const MyApp()); /// This widget is the root of your application. class MyApp extends StatefulWidget { /// Construct the [MyApp] const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: const Text('APIExample'), ), body: const JoinChannelVideoToken()), ); } } class JoinChannelVideoToken extends StatefulWidget { const JoinChannelVideoToken({Key? key}) : super(key: key); @override State<StatefulWidget> createState() => _State(); } class _State extends State<JoinChannelVideoToken> { late final RtcEngine _engine; bool _isReadyPreview = false; bool isJoined = false, switchCamera = true, switchRender = true; Set<int> remoteUid = {}; static const String appId = '<Your app ID>'; // Fill in the information from Agora console static const String channelId = '<Your channel name>'; // Fill in the channel name static const String hostUrl = '<Your host URL and port>'; // Fill in the server URL and port @override void initState() { super.initState(); _initEngine(); } @override void dispose() { super.dispose(); _dispose(); } Future<void> _dispose() async { await _engine.leaveChannel(); await _engine.release(); } Future<void> _initEngine() async { _engine = createAgoraRtcEngine(); await _engine.initialize(const RtcEngineContext( appId: appId, )); _engine.registerEventHandler(RtcEngineEventHandler( onJoinChannelSuccess: (RtcConnection connection, int elapsed) { setState(() { isJoined = true; }); }, onUserJoined: (RtcConnection connection, int rUid, int elapsed) { setState(() { remoteUid.add(rUid); }); }, onUserOffline: (RtcConnection connection, int rUid, UserOfflineReasonType reason) { setState(() { remoteUid.removeWhere((element) => element == rUid); }); }, onLeaveChannel: (RtcConnection connection, RtcStats stats) { setState(() { isJoined = false; remoteUid.clear(); }); }, onTokenPrivilegeWillExpire: (RtcConnection connection, String token) { _fetchToken(1234, channelId, 1, false); }, onRequestToken: (RtcConnection connection) { _fetchToken(1234, channelId, 1, true); }, )); await _engine.enableVideo(); await _engine.startPreview(); await _fetchToken(1234, channelId, 1, true); setState(() { _isReadyPreview = true; }); } Future<void> _fetchToken( int uid, String channelName, int toeknRole, bool needJoinChannel, ) async { var client = http.Client(); try { Map<String, String> headers = { 'Content-type': 'application/json', 'Accept': 'application/json', }; var response = await client.post(Uri.parse(hostUrl), headers: headers, body: jsonEncode( {'uid': uid, 'ChannelName': channelName, 'role': toeknRole})); var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map; final token = decodedResponse['token']; if (needJoinChannel) { await _engine.joinChannel( token: token, channelId: channelName, uid: uid, options: const ChannelMediaOptions( channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, clientRoleType: ClientRoleType.clientRoleBroadcaster, ), ); } else { await _engine.renewToken(token); } } finally { client.close(); } } @override Widget build(BuildContext context) { if (!_isReadyPreview) return Container(); return Stack( children: [ AgoraVideoView( controller: VideoViewController( rtcEngine: _engine, canvas: const VideoCanvas(uid: 0), ), ), Align( alignment: Alignment.topLeft, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: List.of(remoteUid.map( (e) => SizedBox( width: 120, height: 120, child: AgoraVideoView( controller: VideoViewController.remote( rtcEngine: _engine, canvas: VideoCanvas(uid: e), connection: const RtcConnection(channelId: channelId), ), ), ), )), ), ), ) ], ); } }Replace
<Your app ID>with your app ID, which must be consistent with the app ID you specified in the server configuration. Update<Your host URL and port>with the host URL and port of the local Golang server you have deployed. For example99.9.9.99:8082.
The sample code implements the following logic:
-
Calls
joinChannelto join a channel using the user ID, the channel name, and a token you obtain from the server. The user ID and channel name you specify must be consistent with the values you used to generate the token. -
The SDK triggers an
onTokenPrivilegeWillExpirecallback 30 seconds before the token expires. After receiving the callback, you obtain a new token from the server and callrenewTokento pass the newly generated token to the SDK. -
If the token expires, the SDK triggers an
onRequestTokencallback. After receiving the callback, obtain a new token from the server and calljoinChannelwith the new token to rejoin the channel.
Build and run the project on the local device, the app performs the following operations:
- Obtains a token from your token server.
- Joins the channel.
- Automatically renews the token when it is about to expire.
Note
The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
