Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/development-token-source.md
Original file line number Diff line number Diff line change
@@ -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`)
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -102,6 +103,77 @@ 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 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("your token server id")
```

#### 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 <auth>"),
)
```

#### 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,29 @@ 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,
)

/**
* Options for the deprecated [TokenSource.fromSandboxTokenServer].
*
* @see DevelopmentTokenServerOptions
*/
@Deprecated("Use DevelopmentTokenServerOptions instead", ReplaceWith("DevelopmentTokenServerOptions(baseUrl)"))
data class SandboxTokenServerOptions(
val baseUrl: String? = null,
)

internal class SandboxTokenSource(sandboxId: String, options: SandboxTokenServerOptions) : EndpointTokenSource {
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<String, String> = mapOf(
"X-Sandbox-ID" to sandboxId,
"X-Sandbox-ID" to tokenServerId,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -189,18 +193,35 @@ 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,
)

/**
* 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, DevelopmentTokenServerOptions(options.baseUrl))"))
fun fromSandboxTokenServer(sandboxId: String, options: SandboxTokenServerOptions = SandboxTokenServerOptions()): ConfigurableTokenSource =
fromDevelopmentTokenServer(sandboxId, DevelopmentTokenServerOptions(baseUrl = options.baseUrl))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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",
Expand Down
Loading