Skip to content

Commit

Permalink
Add sip grants and attributes support to access tokens (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidliu authored Jul 28, 2024
1 parent c893a05 commit 5d67c39
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
45 changes: 43 additions & 2 deletions src/main/kotlin/io/livekit/server/AccessToken.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.auth0.jwt.JWT
import com.auth0.jwt.JWTCreator
import com.auth0.jwt.algorithms.Algorithm
import java.time.Instant
import java.util.*
import java.util.Date
import java.util.concurrent.TimeUnit

/**
Expand All @@ -36,6 +36,7 @@ class AccessToken(
private val secret: String
) {
private val videoGrants = mutableSetOf<VideoGrant>()
private val sipGrants = mutableSetOf<SIPGrant>()

/**
* Amount of time in milliseconds the created token is valid for.
Expand Down Expand Up @@ -78,6 +79,11 @@ class AccessToken(
*/
var sha256: String? = null

/**
* Key/value attributes to attach to the participant
*/
val attributes = mutableMapOf<String, String>()

/**
* Add [VideoGrant] to this token.
*/
Expand All @@ -96,10 +102,38 @@ class AccessToken(
}
}

/**
* Clear all previously added [VideoGrant]s.
*/
fun clearGrants() {
videoGrants.clear()
}

/**
* Add [VideoGrant] to this token.
*/
fun addSIPGrants(vararg grants: SIPGrant) {
for (grant in grants) {
sipGrants.add(grant)
}
}

/**
* Add [VideoGrant] to this token.
*/
fun addSIPGrants(grants: Iterable<SIPGrant>) {
for (grant in grants) {
sipGrants.add(grant)
}
}

/**
* Clear all previously added [SIPGrant]s.
*/
fun clearSIPGrants() {
sipGrants.clear()
}

fun toJwt(): String {
return with(JWT.create()) {
withIssuer(apiKey)
Expand Down Expand Up @@ -127,13 +161,20 @@ class AccessToken(
}
val claimsMap = mutableMapOf<String, Any>()
val videoGrantsMap = videoGrants.associate { grant -> grant.toPair() }
val sipGrantsMap = sipGrants.associate { grant -> grant.toPair() }

name?.let { claimsMap["name"] = it }
metadata?.let { claimsMap["metadata"] = it }
sha256?.let { claimsMap["sha256"] = it }
attributes.toMap().let { attributesCopy ->
if (attributesCopy.isNotEmpty()) {
claimsMap["attributes"] = attributesCopy
}
}
claimsMap["video"] = videoGrantsMap
claimsMap["sip"] = sipGrantsMap

claimsMap.forEach { key, value ->
claimsMap.forEach { (key, value) ->
withClaimAny(key, value)
}

Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/io/livekit/server/SIPGrant.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.livekit.server

/**
* @link https://docs.livekit.io/guides/access-tokens/#video-grant
*/
sealed class SIPGrant(val key: String, val value: Any) {
fun toPair() = key to value
}


/** Can manage sip resources */
class SIPAdmin(value: Boolean) : SIPGrant("admin", value)

/** Can make outbound calls */
class SIPCall(value: Boolean) : SIPGrant("call", value)
5 changes: 5 additions & 0 deletions src/main/kotlin/io/livekit/server/VideoGrant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,8 @@ class Hidden(value: Boolean) : VideoGrant("hidden", value)
* indicates this participant is recording the room
*/
class Recorder(value: Boolean) : VideoGrant("recorder", value)

/**
* indicates this participant is allowed to connect to LiveKit as an Agent Framework worker
*/
class Agent(value: Boolean) : VideoGrant("agent", value)
9 changes: 8 additions & 1 deletion src/test/kotlin/io/livekit/server/AccessTokenTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package io.livekit.server
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import org.junit.jupiter.api.Test
import java.util.*
import java.util.Date
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

Expand All @@ -39,9 +39,11 @@ class AccessTokenTest {
token.identity = "identity"
token.metadata = "metadata"
token.sha256 = "gfedcba"
token.attributes["key"] = "value"

token.addGrants(RoomName("room_name"))
token.addGrants(CanPublishSources(listOf("camera", "microphone")))
token.addSIPGrants(SIPAdmin(true))

val jwt = token.toJwt()

Expand All @@ -59,10 +61,15 @@ class AccessTokenTest {
assertEquals(token.metadata, claims["metadata"]?.asString())
assertEquals(token.sha256, claims["sha256"]?.asString())
assertEquals(token.expiration, decodedJWT.expiresAt)
assertEquals(token.attributes["key"], claims["attributes"]?.asMap()?.get("key"))

val videoGrants = claims["video"]?.asMap()
assertNotNull(videoGrants)
assertEquals("room_name", videoGrants["room"])
assertEquals(listOf("camera", "microphone"), videoGrants["canPublishSources"])

val sipGrants = claims["sip"]?.asMap()
assertNotNull(sipGrants)
assertEquals(true, sipGrants["admin"])
}
}

0 comments on commit 5d67c39

Please sign in to comment.