rust-sdks icon indicating copy to clipboard operation
rust-sdks copied to clipboard

[WIP] Rust TokenSource implementation

Open 1egoman opened this issue 2 months ago • 0 comments

Talking with @ladvoc, it seemed like it would be a good exercise to port the TokenSource to rust as a first pass to see what new additional complexity is exposed in a less client focused context. Plus longer term, this could be useful as part of the broader rust client sdk core project.

For a working example, see examples/token_source. You'll want to run it with some environment variables populated - something maybe like this would work: LIVEKIT_URL=1 LIVEKIT_API_KEY=2 LIVEKIT_API_SECRET=3 SERVER_URL=4 API_KEY=5 API_SECRET=6 RUST_LOG=info cargo run

[!WARNING] General caveat: I've yet to write a substantial amount of rust professionally, though personally I've written a fair bit. Because of this it's fairly likely I'm doing some things badly / in non idiomatic ways. Feel free to point any of that kind of stuff out!

High level summary

The literal, endpoint, sandboxTokenServer, and custom implementations all work very similar to their web, swift, and android counterparts.

The big new concept this change introduces is the TokenSourceMinter / TokenSourceMinterCustom. Roughly what they look like:

let fetch_options = TokenSourceFetchOptions::default().with_agent_name("voice ai quickstart");

// A TokenSourceMinter is a configurable token source that lets a user "mint" new tokens with a given set of options:
let minter = TokenSourceMinter::default();
let _ = minter_literal_custom.fetch(&fetch_options).await;

// There's also a mechanism to allow users to pass different sets of credentials to the minter either from literal
// strings (below), environment variables (the default), or via a manual closure evaluated at fetch time.
let minter_literal = TokenSourceMinter::new(MinterCredentials::new("server url", "api key", "api secret"));
let _ = minter_literal_credentials.fetch(&fetch_options).await;

// One other type of TokenSource I added - I'm not sure if this one is useful / a good idea though...
//
// A TokenSourceCustomMinter is a fixed TokenSource which calls its provided closure with a preconfigured
// access token builder which allows you to manually update and return a new generated token. It seems like
// this might be a better approach for one off use cases while a `TokenSourceMinter` would be better for use
// with a future agents sdk which could pass in an agent name / etc.
let custom_minter =
    TokenSourceCustomMinter::new(|access_token| {
        access_token
            .with_identity("rust-bot")
            .with_name("Rust Bot")
            .with_grants(access_token::VideoGrants {
                room_join: true,
                room: "my-room".to_string(),
                ..Default::default()
            })
            .to_jwt()
    });
let _ = custom_minter.fetch().await;

Large remaining things to work through

  • [x] Add some sort of caching / jwt validation logic to TokenSourceConfigurable or maybe a TokenSourceCached composition layer like swift/android or something like that
  • [ ] Get async closures working within TokenSourceCustom
  • [ ] Get async closures working within TokenSourceLiteral
  • [x] Error handling
  • [ ] Tests

1egoman avatar Oct 15 '25 20:10 1egoman