Skip to content

Commit 2dff496

Browse files
authored
Simplify enabling E2EE with Session API (#1053)
1 parent 075c151 commit 2dff496

5 files changed

Lines changed: 51 additions & 2 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="fixed" "Guard Session.start() against concurrent calls"

.changes/simplify-session-e2ee

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
minor type="added" "Simplify enabling E2EE with Session API"

lib/src/agent/session.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class Session extends DisposableChangeNotifier {
150150
final _TokenSourceConfiguration _tokenSourceConfiguration;
151151

152152
final Agent _agent = Agent();
153+
bool _isStarting = false;
153154
Agent get agent => _agent;
154155

155156
SessionError? get error => _error;
@@ -175,12 +176,20 @@ class Session extends DisposableChangeNotifier {
175176
EventsListener<RoomEvent>? _roomListener;
176177
Timer? _agentTimeoutTimer;
177178

179+
/// Enables or disables end-to-end encryption for the session.
180+
///
181+
/// Requires that encryption was configured via [SessionOptions] (by passing
182+
/// `encryption:`) or that the [Room] was created with [E2EEOptions].
183+
/// Throws [LiveKitE2EEException] if encryption was not configured.
184+
Future<void> setEncryptionEnabled(bool enabled) => room.setE2EEEnabled(enabled);
185+
178186
/// Starts the session by fetching credentials and connecting to the room.
179187
Future<void> start() async {
180-
if (room.connectionState != ConnectionState.disconnected) {
188+
if (_isStarting || room.connectionState != ConnectionState.disconnected) {
181189
logger.info('Session.start() ignored: room already connecting or connected.');
182190
return;
183191
}
192+
_isStarting = true;
184193

185194
_setError(null);
186195
_agentTimeoutTimer?.cancel();
@@ -229,6 +238,8 @@ class Session extends DisposableChangeNotifier {
229238
_setError(SessionError.connection(error));
230239
_setConnectionState(ConnectionState.disconnected);
231240
_agent.disconnected();
241+
} finally {
242+
_isStarting = false;
232243
}
233244
}
234245

lib/src/agent/session_options.dart

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@
1313
// limitations under the License.
1414

1515
import '../core/room.dart';
16+
import '../e2ee/options.dart';
17+
import '../options.dart';
1618

1719
/// Options for creating a [Session].
1820
class SessionOptions {
1921
/// The underlying [Room] used by the session.
22+
///
23+
/// If neither [room] nor [encryption] is provided, a default [Room] is
24+
/// created. Passing both throws [ArgumentError] — configure E2EE on the
25+
/// [Room] directly if you need a custom [Room] with encryption.
2026
final Room room;
2127

2228
/// Whether to enable audio pre-connect with [PreConnectAudioBuffer].
@@ -30,11 +36,33 @@ class SessionOptions {
3036
/// to a failed state.
3137
final Duration agentConnectTimeout;
3238

39+
/// Creates [SessionOptions].
40+
///
41+
/// Pass [encryption] to configure end-to-end encryption on the internally
42+
/// created [Room]. Use [E2EEOptions.sharedKey] for the common shared-key
43+
/// case. For advanced setups (custom [RoomOptions], per-participant keys),
44+
/// build a [Room] yourself and pass it via [room] instead.
45+
///
46+
/// Passing both [room] and [encryption] throws [ArgumentError].
3347
SessionOptions({
3448
Room? room,
49+
E2EEOptions? encryption,
3550
this.preConnectAudio = true,
3651
this.agentConnectTimeout = const Duration(seconds: 20),
37-
}) : room = room ?? Room();
52+
}) : room = _buildRoom(room, encryption);
53+
54+
static Room _buildRoom(Room? room, E2EEOptions? encryption) {
55+
if (room != null && encryption != null) {
56+
throw ArgumentError(
57+
'SessionOptions: pass either `room` or `encryption`, not both. '
58+
'To use encryption with a custom Room, configure E2EE on the Room directly.',
59+
);
60+
}
61+
if (encryption != null) {
62+
return Room(roomOptions: RoomOptions(encryption: encryption));
63+
}
64+
return room ?? Room();
65+
}
3866

3967
SessionOptions copyWith({
4068
Room? room,

lib/src/e2ee/options.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@ class E2EEOptions {
2424
final BaseKeyProvider keyProvider;
2525
final EncryptionType encryptionType = EncryptionType.kGcm;
2626
const E2EEOptions({required this.keyProvider});
27+
28+
/// Creates [E2EEOptions] configured with a shared-key [BaseKeyProvider]
29+
/// derived from the given passphrase.
30+
static Future<E2EEOptions> sharedKey(String key) async {
31+
final keyProvider = await BaseKeyProvider.create();
32+
await keyProvider.setSharedKey(key);
33+
return E2EEOptions(keyProvider: keyProvider);
34+
}
2735
}

0 commit comments

Comments
 (0)