Skip to content

Commit f7ad2e0

Browse files
Add token_source examples
Add one example per token source type (literal, endpoint, sandbox, custom, caching) that connects to a room, plus a shared common.h for the logging delegate and connect/observe session loop. The sandbox example reads its sandbox ID from the environment so no IDs are committed. These build against a local install of the SDK's token source API branch via -DLIVEKIT_LOCAL_SDK_DIR; see token_source/README.md. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 17d2cc7 commit f7ad2e0

9 files changed

Lines changed: 729 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ endfunction()
8282
include(ExampleDeps)
8383

8484
add_subdirectory(basic_room)
85+
add_subdirectory(token_source)
8586
add_subdirectory(simple_room)
8687
add_subdirectory(simple_rpc)
8788
add_subdirectory(simple_data_stream)

token_source/CMakeLists.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright 2026 LiveKit, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# One executable per token source type. Each shares common.h for the logging
16+
# delegate and the connect/observe session loop.
17+
18+
# Resolve the SDK lib directory so -llivekit_ffi is found at link time.
19+
get_filename_component(_lk_cmake_dir "${LiveKit_DIR}" DIRECTORY) # .../lib/cmake
20+
get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) # .../lib
21+
22+
set(_token_source_examples
23+
token_source_literal literal.cpp
24+
token_source_endpoint endpoint.cpp
25+
token_source_sandbox sandbox.cpp
26+
token_source_custom custom.cpp
27+
token_source_caching caching.cpp
28+
)
29+
30+
list(LENGTH _token_source_examples _count)
31+
math(EXPR _last "${_count} - 1")
32+
foreach(_i RANGE 0 ${_last} 2)
33+
list(GET _token_source_examples ${_i} _target)
34+
math(EXPR _src_idx "${_i} + 1")
35+
list(GET _token_source_examples ${_src_idx} _src)
36+
37+
add_executable(${_target} ${_src})
38+
target_include_directories(${_target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
39+
target_link_libraries(${_target} PRIVATE ${LIVEKIT_CORE_TARGET})
40+
target_link_directories(${_target} PRIVATE "${_lk_lib_dir}")
41+
42+
livekit_copy_windows_runtime_dlls(${_target})
43+
endforeach()
44+
45+
unset(_token_source_examples)

token_source/README.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Token sources
2+
3+
These examples show the different ways to obtain the credentials
4+
(**WebSocket URL** + **participant JWT**) the SDK needs to join a room. Each
5+
example builds its own executable, constructs a single kind of
6+
[`TokenSource`](https://github.com/livekit/client-sdk-cpp/blob/main/include/livekit/token_source.h),
7+
connects to a room, logs participant join/leave for a few seconds, then
8+
disconnects.
9+
10+
> Token sources are for the **initial connection only**. Once connected, the
11+
> LiveKit server refreshes the session token internally — your token source is
12+
> not called again unless you connect again. See the SDK's
13+
> [authentication docs](https://github.com/livekit/client-sdk-cpp/blob/main/docs/authentication.md).
14+
15+
## The types
16+
17+
| Example | Type | When to use |
18+
|---|---|---|
19+
| `literal.cpp` | `LiteralTokenSource` | You already have a URL + JWT (minted out of band, e.g. `lk token create`). The SDK consumes them as-is. |
20+
| `endpoint.cpp` | `EndpointTokenSource` | Recommended for production. The SDK POSTs request options to your backend token endpoint, which returns the URL + a fresh JWT. API keys stay server-side. |
21+
| `sandbox.cpp` | `SandboxTokenSource` | Local development only. Uses LiveKit Cloud's sandbox token server. Not for production. |
22+
| `custom.cpp` | `CustomTokenSource` | You have an internal auth/token system. Plug in your own async callback that returns credentials. |
23+
| `caching.cpp` | `CachingTokenSource` | A decorator that adds JWT-aware caching around any configurable source (endpoint/sandbox/custom) to cut down on fetch calls. |
24+
25+
`LiteralTokenSource` is *fixed* (no per-call options); the others are
26+
*configurable* and accept
27+
[`TokenRequestOptions`](https://github.com/livekit/client-sdk-cpp/blob/main/include/livekit/token_source.h)
28+
(room name, participant identity, agent dispatch, ...).
29+
30+
## Configuration
31+
32+
All inputs come from environment variables, so no secrets or sandbox IDs are
33+
committed to source.
34+
35+
| Variable | Used by | Notes |
36+
|---|---|---|
37+
| `LIVEKIT_URL` | literal, custom | WebSocket URL, e.g. `ws://localhost:7880`. |
38+
| `LIVEKIT_TOKEN` | literal, custom | Participant JWT. |
39+
| `LIVEKIT_TOKEN_ENDPOINT` | endpoint, caching | Token endpoint URL. Default `http://127.0.0.1:3000/createToken`. |
40+
| `LIVEKIT_TOKEN_ENDPOINT_METHOD` | endpoint, caching | Optional HTTP method (default `POST`). |
41+
| `LIVEKIT_TOKEN_ENDPOINT_HEADERS` | endpoint, caching | Optional newline-separated `Name: Value` headers. |
42+
| `LIVEKIT_SANDBOX_ID` | sandbox | Required. Sandbox ID from LiveKit Cloud. |
43+
| `LIVEKIT_AGENT_NAME` | sandbox | Optional agent to dispatch into the room. |
44+
| `LIVEKIT_AGENT_METADATA` | sandbox | Optional metadata for the dispatched agent. |
45+
46+
## Building
47+
48+
The token source API is newer than the latest published SDK release, so build
49+
these examples against a **local SDK build** of the
50+
[`feature/token_source_api`](https://github.com/livekit/client-sdk-cpp/tree/feature/token_source_api)
51+
branch rather than a downloaded release.
52+
53+
```bash
54+
# 1. Build and install the SDK from the feature branch.
55+
git clone https://github.com/livekit/client-sdk-cpp.git
56+
cd client-sdk-cpp
57+
git checkout feature/token_source_api
58+
git submodule update --init --recursive
59+
./build.sh release --bundle --prefix "$HOME/livekit-sdk-install"
60+
61+
# 2. Configure the examples against that local install.
62+
cd /path/to/cpp-example-collection
63+
cmake -S . -B build -DLIVEKIT_LOCAL_SDK_DIR="$HOME/livekit-sdk-install"
64+
cmake --build build
65+
```
66+
67+
Once the token source API ships in a release, you can drop
68+
`-DLIVEKIT_LOCAL_SDK_DIR` and use the normal download flow (optionally pinning
69+
`-DLIVEKIT_SDK_VERSION`).
70+
71+
## Running
72+
73+
The built binaries live under `build/token_source/`:
74+
75+
```bash
76+
# Literal: bring your own URL + token
77+
export LIVEKIT_URL=ws://localhost:7880
78+
export LIVEKIT_TOKEN=<participant-jwt>
79+
./build/token_source/token_source_literal
80+
81+
# Endpoint / caching: point at your token endpoint
82+
export LIVEKIT_TOKEN_ENDPOINT=http://127.0.0.1:3000/createToken
83+
./build/token_source/token_source_endpoint
84+
./build/token_source/token_source_caching
85+
86+
# Sandbox: development-only, ID from the environment
87+
export LIVEKIT_SANDBOX_ID=<your-sandbox-id>
88+
./build/token_source/token_source_sandbox
89+
90+
# Custom: callback returns credentials (this example reads the env)
91+
export LIVEKIT_URL=ws://localhost:7880
92+
export LIVEKIT_TOKEN=<participant-jwt>
93+
./build/token_source/token_source_custom
94+
```
95+
96+
Generate a development token with the
97+
[LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/) (a dev server started
98+
with `livekit-server --dev` uses `devkey` / `secret`):
99+
100+
```bash
101+
export LIVEKIT_TOKEN=$(lk token create \
102+
--api-key devkey --api-secret secret \
103+
-i my-participant --join --room my-room \
104+
--valid-for 24h --token-only)
105+
```

token_source/caching.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2026 LiveKit, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Caching token source: a decorator that adds JWT-aware caching to any
18+
// configurable token source (endpoint, sandbox, or custom).
19+
//
20+
// Repeated fetches for the same request options reuse the cached token until it
21+
// nears expiry or a force-refresh is requested, cutting down calls to your
22+
// backend. This example wraps an EndpointTokenSource.
23+
//
24+
// Environment:
25+
// LIVEKIT_TOKEN_ENDPOINT Token endpoint URL
26+
// (default http://127.0.0.1:3000/createToken)
27+
// LIVEKIT_TOKEN_ENDPOINT_METHOD Optional HTTP method (default POST)
28+
// LIVEKIT_TOKEN_ENDPOINT_HEADERS Optional newline-separated "Name: Value" headers
29+
30+
#include <livekit/livekit.h>
31+
#include <livekit/token_source.h>
32+
33+
#include <iostream>
34+
#include <string>
35+
#include <utility>
36+
37+
#include "common.h"
38+
39+
namespace {
40+
41+
using namespace token_source_example;
42+
43+
bool cachingTokenSourceConnect() {
44+
std::string endpoint_url = getenvOrEmpty("LIVEKIT_TOKEN_ENDPOINT");
45+
if (endpoint_url.empty()) {
46+
endpoint_url = "http://127.0.0.1:3000/createToken";
47+
}
48+
49+
std::cout << "Caching token source wrapping endpoint: " << endpoint_url << "\n";
50+
51+
// Build the inner source, then wrap it. CachingTokenSource::wrap takes
52+
// ownership of the inner source via unique_ptr.
53+
auto inner = livekit::EndpointTokenSource::fromUrl(endpoint_url, endpointOptionsFromEnv());
54+
auto token_source = livekit::CachingTokenSource::wrap(std::move(inner));
55+
56+
livekit::TokenRequestOptions request_options;
57+
request_options.participant_identity = "robot-a";
58+
59+
livekit::Room room;
60+
ParticipantLogDelegate delegate;
61+
room.setDelegate(&delegate);
62+
if (!room.connect(*token_source, request_options, livekit::RoomOptions())) {
63+
std::cerr << "Failed to connect to room\n";
64+
return false;
65+
}
66+
std::cout << "Connected to room: " << room.roomInfo().name << " (caching token source)\n";
67+
68+
return runConnectedSession(room);
69+
}
70+
71+
} // namespace
72+
73+
int main() {
74+
livekit::initialize(livekit::LogLevel::Info);
75+
const bool ok = cachingTokenSourceConnect();
76+
livekit::shutdown();
77+
return ok ? 0 : 1;
78+
}

0 commit comments

Comments
 (0)