From 317b2c2e589bb36ac2857b75e0c4849cfbbdf3ff Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:06:05 +0200 Subject: [PATCH 1/5] feat: introduce TokenSource.fromDevelopmentTokenServer and deprecate fromSandboxTokenServer Aligns with the cross-SDK rename of the sandbox token source to development token source (client-sdk-js#2032, client-sdk-swift#1077, client-sdk-unity#366). SandboxTokenServerOptions is kept as a deprecated typealias of DevelopmentTokenServerOptions and fromSandboxTokenServer delegates to the new factory, so existing code keeps compiling. The wire protocol (endpoint URL and X-Sandbox-ID header) is unchanged. Also adds a Token sources section to the README. Co-Authored-By: Claude Fable 5 --- .changeset/development-token-source.md | 5 ++ README.md | 73 +++++++++++++++++++ .../android/token/EndpointTokenSource.kt | 9 ++- .../io/livekit/android/token/TokenSource.kt | 20 ++++- 4 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 .changeset/development-token-source.md diff --git a/.changeset/development-token-source.md b/.changeset/development-token-source.md new file mode 100644 index 000000000..ecd6dacb7 --- /dev/null +++ b/.changeset/development-token-source.md @@ -0,0 +1,5 @@ +--- +"client-sdk-android": minor +--- + +Add `TokenSource.fromDevelopmentTokenServer`, the new name for the now-deprecated `TokenSource.fromSandboxTokenServer` (`SandboxTokenServerOptions` is likewise deprecated in favor of `DevelopmentTokenServerOptions`) diff --git a/README.md b/README.md index c3a625149..3c2aea21b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Use this SDK to add realtime video, audio and data features to your Android/Kotl - [SDK Size](#sdk-size) - [Usage](#usage) - [Permissions](#permissions) + - [Token sources](#token-sources) - [Publishing camera and microphone](#publishing-camera-and-microphone) - [Sharing screen](#sharing-screen) - [Rendering subscribed tracks](#rendering-subscribed-tracks) @@ -102,6 +103,78 @@ These permission must be requested at runtime. Reference the [sample app](https://github.com/livekit/client-sdk-android/blob/4e76e36e0d9f895c718bd41809ab5ff6c57aabd4/sample-app-compose/src/main/java/io/livekit/android/composesample/MainActivity.kt#L134) for an example. +### Token sources + +To connect to a room, you need a server URL and a participant token. The `TokenSource` factory +methods cover the common ways of obtaining these credentials: + +#### 1. Literal + +Use this to pass a pregenerated server URL and token. Generate tokens via the +[LiveKit CLI](https://docs.livekit.io/frontends/build/authentication/custom/#manual-token-creation) +or from your [LiveKit Cloud](https://cloud.livekit.io/) project's API key page. + +```kt +val source = TokenSource.fromLiteral("wss://your.livekit.host", "your_token") +``` + +#### 2. Development Token Server + +For development and testing. Follow the +[development token server guide](https://docs.livekit.io/frontends/build/authentication/sandbox-token-server/) +to enable your project's development token server and get the token server ID — the +`token-server-xxxxxx` value in `https://token-server-xxxxxx.sandbox.livekit.io`. + +This token generation mechanism is inherently insecure and should only be used for prototyping; +do **not** use it in production. + +```kt +val source = TokenSource.fromDevelopmentTokenServer("token-server-xxxxxx") +``` + +#### 3. Endpoint + +For production. Point to your own token endpoint URL and add any required authentication headers. +The request and response follow the standard format described in the +[endpoint token generation guide](https://docs.livekit.io/frontends/build/authentication/endpoint/). + +```kt +val source = TokenSource.fromEndpoint( + url = "https://your.token-server/api/token", + headers = mapOf("Authorization" to "Bearer "), +) +``` + +#### 4. Custom + +For fully custom logic, supply your own fetch function: + +```kt +val source = TokenSource.fromCustom { options -> + // Generate credentials via custom means here. + Result.success(TokenSourceResponse(serverUrl = "...", participantToken = "...")) +} +``` + +#### Fetching credentials and connecting + +Configurable token sources (development token server, endpoint, custom) accept +`TokenRequestOptions` per fetch; fixed sources (literal) take no options: + +```kt +val response = source.fetch( + TokenRequestOptions(roomName = "room", participantName = "participant"), +).getOrThrow() + +room.connect(response.serverUrl, response.participantToken) +``` + +To avoid refetching credentials that are still valid, wrap any token source with `.cached()`: + +```kt +val cachedSource = source.cached() +``` + ### Publishing camera and microphone ```kt diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt b/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt index 6f3cdd30a..4a55bf9f3 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt @@ -46,14 +46,17 @@ internal class EndpointTokenSourceImpl( ) : this(URL(url), method, headers) } -data class SandboxTokenServerOptions( +data class DevelopmentTokenServerOptions( val baseUrl: String? = null, ) -internal class SandboxTokenSource(sandboxId: String, options: SandboxTokenServerOptions) : EndpointTokenSource { +@Deprecated("Use DevelopmentTokenServerOptions instead", ReplaceWith("DevelopmentTokenServerOptions")) +typealias SandboxTokenServerOptions = DevelopmentTokenServerOptions + +internal class DevelopmentTokenSource(tokenServerId: String, options: DevelopmentTokenServerOptions) : EndpointTokenSource { override val url: URL = URL("${options.baseUrl ?: "https://cloud-api.livekit.io"}/api/v2/sandbox/connection-details") override val headers: Map = mapOf( - "X-Sandbox-ID" to sandboxId, + "X-Sandbox-ID" to tokenServerId, ) } diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt b/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt index 866e48f15..c770294ee 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt @@ -165,6 +165,8 @@ interface TokenSource { /** * Creates a [ConfigurableTokenSource] that fetches from a given [url] using the standard token server format. * + * For more info: [https://docs.livekit.io/frontends/build/authentication/endpoint/](https://docs.livekit.io/frontends/build/authentication/endpoint/) + * * @param method the HTTP request method to use. Defaults to POST. * @see cached * @see CachingConfigurableTokenSource @@ -178,6 +180,8 @@ interface TokenSource { /** * Creates a [ConfigurableTokenSource] that fetches from a given [url] using the standard token server format. * + * For more info: [https://docs.livekit.io/frontends/build/authentication/endpoint/](https://docs.livekit.io/frontends/build/authentication/endpoint/) + * * @param method the HTTP request method to use. Defaults to POST. * @see cached * @see CachingConfigurableTokenSource @@ -189,18 +193,28 @@ interface TokenSource { ) /** - * Creates a [ConfigurableTokenSource] that fetches from a sandbox token server for credentials, + * Creates a [ConfigurableTokenSource] that queries a development token server for credentials, * which supports quick prototyping/getting started types of use cases. * * Note: This token provider is **insecure** and should **not** be used in production. * + * For more info: [https://docs.livekit.io/frontends/build/authentication/sandbox-token-server/](https://docs.livekit.io/frontends/build/authentication/sandbox-token-server/) + * * @see cached * @see CachingConfigurableTokenSource */ - fun fromSandboxTokenServer(sandboxId: String, options: SandboxTokenServerOptions = SandboxTokenServerOptions()): ConfigurableTokenSource = SandboxTokenSource( - sandboxId = sandboxId, + fun fromDevelopmentTokenServer( + tokenServerId: String, + options: DevelopmentTokenServerOptions = DevelopmentTokenServerOptions(), + ): ConfigurableTokenSource = DevelopmentTokenSource( + tokenServerId = tokenServerId, options = options, ) + + @Suppress("DEPRECATION") + @Deprecated("Use fromDevelopmentTokenServer instead", ReplaceWith("fromDevelopmentTokenServer(sandboxId, options)")) + fun fromSandboxTokenServer(sandboxId: String, options: SandboxTokenServerOptions = SandboxTokenServerOptions()): ConfigurableTokenSource = + fromDevelopmentTokenServer(sandboxId, options) } } From c378ce1c6aa57f235b5ba61c5598c8a3c4148e14 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:33:36 +0200 Subject: [PATCH 2/5] Improved README myself --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c2aea21b..fd81df9ce 100644 --- a/README.md +++ b/README.md @@ -122,14 +122,13 @@ val source = TokenSource.fromLiteral("wss://your.livekit.host", "your_token") For development and testing. Follow the [development token server guide](https://docs.livekit.io/frontends/build/authentication/sandbox-token-server/) -to enable your project's development token server and get the token server ID — the -`token-server-xxxxxx` value in `https://token-server-xxxxxx.sandbox.livekit.io`. +to enable your project's development token server and get the token server ID from the settings page. This token generation mechanism is inherently insecure and should only be used for prototyping; do **not** use it in production. ```kt -val source = TokenSource.fromDevelopmentTokenServer("token-server-xxxxxx") +val source = TokenSource.fromDevelopmentTokenServer("your token server id") ``` #### 3. Endpoint From 83cd80d2e4bdecb460601fab31ab4272ca8c1757 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:09:07 +0200 Subject: [PATCH 3/5] fix: keep SandboxTokenServerOptions as a real class for binary compatibility A typealias only exists at compile time, so the JVM class shipped in v2.21.0-v2.27.0 would disappear from the artifact, breaking already compiled apps and Java consumers. Restoring the data class (and the original fromSandboxTokenServer signature) preserves the released ABI. Also adds the KDoc required for public APIs to the deprecated members. Co-Authored-By: Claude Fable 5 --- .../livekit/android/token/EndpointTokenSource.kt | 16 ++++++++++++++-- .../java/io/livekit/android/token/TokenSource.kt | 9 ++++++++- .../io/livekit/android/token/TokenSourceTest.kt | 4 ++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt b/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt index 4a55bf9f3..c0dfef7a4 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/token/EndpointTokenSource.kt @@ -46,12 +46,24 @@ internal class EndpointTokenSourceImpl( ) : this(URL(url), method, headers) } +/** + * Options for [TokenSource.fromDevelopmentTokenServer]. + * + * @param baseUrl optionally overrides the base url of the development token server. + */ data class DevelopmentTokenServerOptions( val baseUrl: String? = null, ) -@Deprecated("Use DevelopmentTokenServerOptions instead", ReplaceWith("DevelopmentTokenServerOptions")) -typealias SandboxTokenServerOptions = DevelopmentTokenServerOptions +/** + * Options for the deprecated [TokenSource.fromSandboxTokenServer]. + * + * @see DevelopmentTokenServerOptions + */ +@Deprecated("Use DevelopmentTokenServerOptions instead", ReplaceWith("DevelopmentTokenServerOptions(baseUrl)")) +data class SandboxTokenServerOptions( + val baseUrl: String? = null, +) internal class DevelopmentTokenSource(tokenServerId: String, options: DevelopmentTokenServerOptions) : EndpointTokenSource { override val url: URL = URL("${options.baseUrl ?: "https://cloud-api.livekit.io"}/api/v2/sandbox/connection-details") diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt b/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt index c770294ee..26b19043f 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt @@ -211,10 +211,17 @@ interface TokenSource { options = options, ) + /** + * Creates a [ConfigurableTokenSource] that queries a development token server for credentials. + * + * Note: This token provider is **insecure** and should **not** be used in production. + * + * @see fromDevelopmentTokenServer + */ @Suppress("DEPRECATION") @Deprecated("Use fromDevelopmentTokenServer instead", ReplaceWith("fromDevelopmentTokenServer(sandboxId, options)")) fun fromSandboxTokenServer(sandboxId: String, options: SandboxTokenServerOptions = SandboxTokenServerOptions()): ConfigurableTokenSource = - fromDevelopmentTokenServer(sandboxId, options) + fromDevelopmentTokenServer(sandboxId, DevelopmentTokenServerOptions(baseUrl = options.baseUrl)) } } diff --git a/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt b/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt index f7683b99d..1d405258c 100644 --- a/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt +++ b/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt @@ -187,8 +187,8 @@ class TokenSourceTest : BaseTest() { @Ignore("For manual testing only.") @Test fun testTokenServer() = runTest { - val source = TokenSource.fromSandboxTokenServer( - "", // Put sandboxId here to test manually. + val source = TokenSource.fromDevelopmentTokenServer( + "", // Put tokenServerId here to test manually. ) val options = TokenRequestOptions( roomName = "room-name", From e541f4a49d19e208cd88cde36d84230177fe2976 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:15:44 +0200 Subject: [PATCH 4/5] chore: update copyright header year via spotlessApply Co-Authored-By: Claude Fable 5 --- .../src/test/java/io/livekit/android/token/TokenSourceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt b/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt index 1d405258c..c5aedc8fa 100644 --- a/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt +++ b/livekit-android-test/src/test/java/io/livekit/android/token/TokenSourceTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 LiveKit, Inc. + * Copyright 2025-2026 LiveKit, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 94f930318965c06e3e7d633fdab7986b37b5f583 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:30:17 +0200 Subject: [PATCH 5/5] Apply suggestion from @devin-ai-integration[bot] Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../src/main/java/io/livekit/android/token/TokenSource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt b/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt index 26b19043f..f665918fa 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/token/TokenSource.kt @@ -219,7 +219,7 @@ interface TokenSource { * @see fromDevelopmentTokenServer */ @Suppress("DEPRECATION") - @Deprecated("Use fromDevelopmentTokenServer instead", ReplaceWith("fromDevelopmentTokenServer(sandboxId, options)")) + @Deprecated("Use fromDevelopmentTokenServer instead", ReplaceWith("fromDevelopmentTokenServer(sandboxId, DevelopmentTokenServerOptions(options.baseUrl))")) fun fromSandboxTokenServer(sandboxId: String, options: SandboxTokenServerOptions = SandboxTokenServerOptions()): ConfigurableTokenSource = fromDevelopmentTokenServer(sandboxId, DevelopmentTokenServerOptions(baseUrl = options.baseUrl)) }