Module livekit.api.access_token

Functions

def camel_to_snake(t: str)
def snake_to_lower_camel(t: str)

Classes

class AccessToken (api_key: Optional[str] = None, api_secret: Optional[str] = None)
Expand source code
class AccessToken:
    ParticipantKind = Literal["standard", "egress", "ingress", "sip", "agent"]

    def __init__(
        self,
        api_key: Optional[str] = None,
        api_secret: Optional[str] = None,
    ) -> None:
        api_key = api_key or os.getenv("LIVEKIT_API_KEY")
        api_secret = api_secret or os.getenv("LIVEKIT_API_SECRET")

        if not api_key or not api_secret:
            raise ValueError("api_key and api_secret must be set")

        self.api_key = api_key  # iss
        self.api_secret = api_secret
        self.claims = Claims()

        # default jwt claims
        self.identity = ""  # sub
        self.ttl = DEFAULT_TTL  # exp

    def with_ttl(self, ttl: datetime.timedelta) -> "AccessToken":
        self.ttl = ttl
        return self

    def with_grants(self, grants: VideoGrants) -> "AccessToken":
        self.claims.video = grants
        return self

    def with_sip_grants(self, grants: SIPGrants) -> "AccessToken":
        self.claims.sip = grants
        return self

    def with_identity(self, identity: str) -> "AccessToken":
        self.identity = identity
        return self

    def with_kind(self, kind: ParticipantKind) -> "AccessToken":
        self.claims.kind = kind
        return self

    def with_name(self, name: str) -> "AccessToken":
        self.claims.name = name
        return self

    def with_metadata(self, metadata: str) -> "AccessToken":
        self.claims.metadata = metadata
        return self

    def with_attributes(self, attributes: dict[str, str]) -> "AccessToken":
        self.claims.attributes = attributes
        return self

    def with_sha256(self, sha256: str) -> "AccessToken":
        self.claims.sha256 = sha256
        return self

    def to_jwt(self) -> str:
        video = self.claims.video
        if video.room_join and (not self.identity or not video.room):
            raise ValueError("identity and room must be set when joining a room")

        claims = dataclasses.asdict(
            self.claims,
            dict_factory=lambda items: {snake_to_lower_camel(k): v for k, v in items},
        )
        claims.update(
            {
                "sub": self.identity,
                "iss": self.api_key,
                "nbf": calendar.timegm(datetime.datetime.utcnow().utctimetuple()),
                "exp": calendar.timegm(
                    (datetime.datetime.utcnow() + self.ttl).utctimetuple()
                ),
            }
        )
        return jwt.encode(claims, self.api_secret, algorithm="HS256")

Class variables

var ParticipantKind

Methods

def to_jwt(self) ‑> str
def with_attributes(self, attributes: dict[str, str]) ‑> AccessToken
def with_grants(self, grants: VideoGrants) ‑> AccessToken
def with_identity(self, identity: str) ‑> AccessToken
def with_kind(self, kind: Literal['standard', 'egress', 'ingress', 'sip', 'agent']) ‑> AccessToken
def with_metadata(self, metadata: str) ‑> AccessToken
def with_name(self, name: str) ‑> AccessToken
def with_sha256(self, sha256: str) ‑> AccessToken
def with_sip_grants(self, grants: SIPGrants) ‑> AccessToken
def with_ttl(self, ttl: datetime.timedelta) ‑> AccessToken
class Claims (identity: str = '', name: str = '', video: VideoGrants = <factory>, sip: SIPGrants = <factory>, attributes: dict[str, str] = <factory>, metadata: str = '', sha256: str = '', kind: str = '')

Claims(identity: str = '', name: str = '', video: livekit.api.access_token.VideoGrants = , sip: livekit.api.access_token.SIPGrants = , attributes: dict[str, str] = , metadata: str = '', sha256: str = '', kind: str = '')

Expand source code
@dataclasses.dataclass
class Claims:
    identity: str = ""
    name: str = ""
    video: VideoGrants = dataclasses.field(default_factory=VideoGrants)
    sip: SIPGrants = dataclasses.field(default_factory=SIPGrants)
    attributes: dict[str, str] = dataclasses.field(default_factory=dict)
    metadata: str = ""
    sha256: str = ""
    kind: str = ""

Class variables

var attributes : dict[str, str]
var identity : str
var kind : str
var metadata : str
var name : str
var sha256 : str
var sipSIPGrants
var videoVideoGrants
class SIPGrants (admin: bool = False, call: bool = False)

SIPGrants(admin: bool = False, call: bool = False)

Expand source code
@dataclasses.dataclass
class SIPGrants:
    # manage sip resources
    admin: bool = False
    # make outbound calls
    call: bool = False

Class variables

var admin : bool
var call : bool
class TokenVerifier (api_key: Optional[str] = None, api_secret: Optional[str] = None, *, leeway: datetime.timedelta = datetime.timedelta(seconds=60))
Expand source code
class TokenVerifier:
    def __init__(
        self,
        api_key: Optional[str] = None,
        api_secret: Optional[str] = None,
        *,
        leeway: datetime.timedelta = DEFAULT_LEEWAY,
    ) -> None:
        api_key = api_key or os.getenv("LIVEKIT_API_KEY")
        api_secret = api_secret or os.getenv("LIVEKIT_API_SECRET")

        if not api_key or not api_secret:
            raise ValueError("api_key and api_secret must be set")

        self.api_key = api_key
        self.api_secret = api_secret
        self._leeway = leeway

    def verify(self, token: str) -> Claims:
        claims = jwt.decode(
            token,
            self.api_secret,
            issuer=self.api_key,
            algorithms=["HS256"],
            leeway=self._leeway.total_seconds(),
        )

        video_dict = claims.get("video", dict())
        video_dict = {camel_to_snake(k): v for k, v in video_dict.items()}
        video_dict = {
            k: v for k, v in video_dict.items() if k in VideoGrants.__dataclass_fields__
        }
        video = VideoGrants(**video_dict)

        sip_dict = claims.get("sip", dict())
        sip_dict = {camel_to_snake(k): v for k, v in sip_dict.items()}
        sip_dict = {
            k: v for k, v in sip_dict.items() if k in SIPGrants.__dataclass_fields__
        }
        sip = SIPGrants(**sip_dict)

        return Claims(
            identity=claims.get("sub", ""),
            name=claims.get("name", ""),
            video=video,
            sip=sip,
            attributes=claims.get("attributes", {}),
            metadata=claims.get("metadata", ""),
            sha256=claims.get("sha256", ""),
        )

Methods

def verify(self, token: str) ‑> Claims
class VideoGrants (room_create: bool = False, room_list: bool = False, room_record: bool = False, room_admin: bool = False, room_join: bool = False, room: str = '', can_publish: bool = True, can_subscribe: bool = True, can_publish_data: bool = True, can_publish_sources: List[str] = <factory>, can_update_own_metadata: bool = False, ingress_admin: bool = False, hidden: bool = False, recorder: bool = False, agent: bool = False)

VideoGrants(room_create: bool = False, room_list: bool = False, room_record: bool = False, room_admin: bool = False, room_join: bool = False, room: str = '', can_publish: bool = True, can_subscribe: bool = True, can_publish_data: bool = True, can_publish_sources: List[str] = , can_update_own_metadata: bool = False, ingress_admin: bool = False, hidden: bool = False, recorder: bool = False, agent: bool = False)

Expand source code
@dataclasses.dataclass
class VideoGrants:
    # actions on rooms
    room_create: bool = False
    room_list: bool = False
    room_record: bool = False

    # actions on a particular room
    room_admin: bool = False
    room_join: bool = False
    room: str = ""

    # permissions within a room
    can_publish: bool = True
    can_subscribe: bool = True
    can_publish_data: bool = True

    # TrackSource types that a participant may publish.
    # When set, it supercedes CanPublish. Only sources explicitly set here can be
    # published
    can_publish_sources: List[str] = dataclasses.field(default_factory=list)

    # by default, a participant is not allowed to update its own metadata
    can_update_own_metadata: bool = False

    # actions on ingresses
    ingress_admin: bool = False  # applies to all ingress

    # participant is not visible to other participants (useful when making bots)
    hidden: bool = False

    # indicates to the room that current participant is a recorder
    recorder: bool = False

    # indicates that the holder can register as an Agent framework worker
    # it is also set on all participants that are joining as Agent
    agent: bool = False

Class variables

var agent : bool
var can_publish : bool
var can_publish_data : bool
var can_publish_sources : List[str]
var can_subscribe : bool
var can_update_own_metadata : bool
var hidden : bool
var ingress_admin : bool
var recorder : bool
var room : str
var room_admin : bool
var room_create : bool
var room_join : bool
var room_list : bool
var room_record : bool