From ca33fcad30af7dd1ffdebdea7da61f7d1c8bfe37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:48:24 +0200 Subject: [PATCH 1/6] Add local packages --- Package.swift | 3 +++ Package@swift-6.0.swift | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Package.swift b/Package.swift index 4ba4edc21..89d354c2f 100644 --- a/Package.swift +++ b/Package.swift @@ -25,6 +25,8 @@ let package = Package( .package(url: "https://github.com/vapor/jwt-kit.git", from: "4.13.5"), // Only used for DocC generation .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.3.0"), + // Local uniffi bindings + .package(path: "../rust-sdks/livekit-uniffi"), ], targets: [ .target( @@ -55,6 +57,7 @@ let package = Package( name: "LiveKitTestSupport", dependencies: [ "LiveKit", + .product(name: "LiveKitFFI", package: "livekit-uniffi"), ], path: "Tests/LiveKitTestSupport" ), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index d6cc50024..962c5716f 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -26,6 +26,8 @@ let package = Package( .package(url: "https://github.com/vapor/jwt-kit.git", from: "4.13.5"), // Only used for DocC generation .package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.3.0"), + // Local uniffi bindings + .package(path: "../rust-sdks/livekit-uniffi"), ], targets: [ .target( @@ -56,6 +58,7 @@ let package = Package( name: "LiveKitTestSupport", dependencies: [ "LiveKit", + .product(name: "LiveKitFFI", package: "livekit-uniffi"), ], path: "Tests/LiveKitTestSupport" ), From a1bf4d051fed511b54dccd9f65d50fd5be597db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:50:04 +0200 Subject: [PATCH 2/6] Use in tests --- Tests/LiveKitTestSupport/TokenGenerator.swift | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/Tests/LiveKitTestSupport/TokenGenerator.swift b/Tests/LiveKitTestSupport/TokenGenerator.swift index bfeac2f80..f8f187b89 100644 --- a/Tests/LiveKitTestSupport/TokenGenerator.swift +++ b/Tests/LiveKitTestSupport/TokenGenerator.swift @@ -14,8 +14,8 @@ * limitations under the License. */ -import JWTKit @testable import LiveKit +import LiveKitFFI public class TokenGenerator { // 30 mins @@ -31,10 +31,6 @@ public class TokenGenerator { public var metadata: String? public var videoGrant: LiveKitJWTPayload.VideoGrant? - // MARK: - Private - - private let signers = JWTSigners() - public init(apiKey: String, apiSecret: String, identity: String, @@ -47,19 +43,40 @@ public class TokenGenerator { } public func sign() throws -> String { - // Add HMAC with SHA-256 signer. - signers.use(.hs256(key: apiSecret)) - - let n = Date().timeIntervalSince1970 + var ffiVideoGrants: VideoGrants? + if let grant = videoGrant { + ffiVideoGrants = VideoGrants( + roomCreate: grant.roomCreate ?? false, + roomList: grant.roomList ?? false, + roomRecord: grant.roomRecord ?? false, + roomAdmin: grant.roomAdmin ?? false, + roomJoin: grant.roomJoin ?? false, + room: grant.room ?? "", + destinationRoom: "", + canPublish: grant.canPublish ?? false, + canSubscribe: grant.canSubscribe ?? false, + canPublishData: grant.canPublishData ?? false, + canPublishSources: grant.canPublishSources ?? [], + canUpdateOwnMetadata: false, + ingressAdmin: false, + hidden: grant.hidden ?? false, + recorder: grant.recorder ?? false + ) + } - let p = LiveKitJWTPayload(exp: .init(value: Date(timeIntervalSince1970: floor(n + ttl))), - iss: .init(stringLiteral: apiKey), - nbf: .init(value: Date(timeIntervalSince1970: floor(n))), - sub: .init(stringLiteral: identity), - name: name, - metadata: metadata, - video: videoGrant) + let credentials = ApiCredentials(key: apiKey, secret: apiSecret) + let options = TokenOptions( + ttl: ttl, + videoGrants: ffiVideoGrants, + sipGrants: nil, + identity: identity, + name: name, + metadata: metadata, + attributes: nil, + sha256: nil, + roomName: videoGrant?.room + ) - return try signers.sign(p) + return try generateToken(options: options, credentials: credentials) } } From c2cf1c996a4d5d1981face423b5717c13e1f3e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:18:15 +0200 Subject: [PATCH 3/6] Move dependency --- Package.swift | 2 +- Package@swift-6.0.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 89d354c2f..2a35a3d10 100644 --- a/Package.swift +++ b/Package.swift @@ -41,6 +41,7 @@ let package = Package( .product(name: "DequeModule", package: "swift-collections"), .product(name: "OrderedCollections", package: "swift-collections"), .product(name: "JWTKit", package: "jwt-kit"), + .product(name: "LiveKitFFI", package: "livekit-uniffi"), "LKObjCHelpers", ], exclude: [ @@ -57,7 +58,6 @@ let package = Package( name: "LiveKitTestSupport", dependencies: [ "LiveKit", - .product(name: "LiveKitFFI", package: "livekit-uniffi"), ], path: "Tests/LiveKitTestSupport" ), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 962c5716f..3f2eacba6 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -42,6 +42,7 @@ let package = Package( .product(name: "DequeModule", package: "swift-collections"), .product(name: "OrderedCollections", package: "swift-collections"), .product(name: "JWTKit", package: "jwt-kit"), + .product(name: "LiveKitFFI", package: "livekit-uniffi"), "LKObjCHelpers", ], exclude: [ @@ -58,7 +59,6 @@ let package = Package( name: "LiveKitTestSupport", dependencies: [ "LiveKit", - .product(name: "LiveKitFFI", package: "livekit-uniffi"), ], path: "Tests/LiveKitTestSupport" ), From 544130587f3ce9c30423efde3e8d082b09232c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:49:39 +0200 Subject: [PATCH 4/6] FFI log forwarding --- Sources/LiveKit/Support/Logger.swift | 73 +++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/Sources/LiveKit/Support/Logger.swift b/Sources/LiveKit/Support/Logger.swift index 4a263c692..da78fb31a 100644 --- a/Sources/LiveKit/Support/Logger.swift +++ b/Sources/LiveKit/Support/Logger.swift @@ -16,6 +16,7 @@ import OSLog internal import LiveKitWebRTC +internal import LiveKitFFI // MARK: - Logger @@ -108,30 +109,36 @@ public struct PrintLogger: Logger { /// A logger that logs to OSLog /// - Parameter minLevel: The minimum level to log /// - Parameter rtc: Whether to log WebRTC output +/// - Parameter ffi: Whether to log Rust FFI output open class OSLogger: Logger, @unchecked Sendable { private static let subsystem = "io.livekit.sdk" - private let queue = DispatchQueue(label: "io.livekit.oslogger", qos: .utility) + private static let queue = DispatchQueue(label: "io.livekit.oslogger", qos: .utility) + + private nonisolated(unsafe) static var ffiBootstrapped = false + private var logs: [String: OSLog] = [:] private lazy var rtcLogger = LKRTCCallbackLogger() + private var ffiLogForwardTask: Task? private let minLevel: LogLevel - public init(minLevel: LogLevel = .info, rtc: Bool = false) { + public init(minLevel: LogLevel = .info, rtc: Bool = false, ffi: Bool = true) { self.minLevel = minLevel - guard rtc else { return } + if rtc { + startRTCLogForwarding(minLevel: minLevel) + } - let rtcLog = OSLog(subsystem: Self.subsystem, category: "WebRTC") - rtcLogger.severity = minLevel.rtcSeverity - rtcLogger.start { message, severity in - os_log("%{public}@", log: rtcLog, type: severity.osLogType, message) + if ffi { + startFFILogForwarding(minLevel: minLevel) } } deinit { rtcLogger.stop() + ffiLogForwardTask?.cancel() } public func log( @@ -155,7 +162,7 @@ open class OSLogger: Logger, @unchecked Sendable { let metadata = buildScopedMetadataString() - queue.async { + Self.queue.async { func getOSLog(for type: Any.Type) -> OSLog { let typeName = String(describing: type) @@ -171,6 +178,36 @@ open class OSLogger: Logger, @unchecked Sendable { os_log("%{public}@", log: getOSLog(for: type), type: level.osLogType, "\(type).\(function) \(message)\(metadata)") } } + + private func startRTCLogForwarding(minLevel: LogLevel) { + let rtcLog = OSLog(subsystem: Self.subsystem, category: "WebRTC") + + rtcLogger.severity = minLevel.rtcSeverity + rtcLogger.start { message, severity in + os_log("%{public}@", log: rtcLog, type: severity.osLogType, message) + } + } + + private func startFFILogForwarding(minLevel: LogLevel) { + Self.queue.sync { + if !Self.ffiBootstrapped { + logForwardBootstrap(level: minLevel.logForwardFilter) + Self.ffiBootstrapped = true + } + } + + let ffiLog = OSLog(subsystem: Self.subsystem, category: "FFI") + + ffiLogForwardTask = Task(priority: .utility) { + for await entry in AsyncStream(unfolding: logForwardReceive) { + guard !Task.isCancelled else { break } + + let message = "\(entry.target) \(entry.message)" + + os_log("%{public}@", log: ffiLog, type: entry.level.osLogType, message) + } + } + } } // MARK: - Loggable @@ -236,6 +273,15 @@ public enum LogLevel: Int, Sendable, Comparable, CustomStringConvertible { } } + var logForwardFilter: LogForwardFilter { + switch self { + case .debug: .debug + case .info: .info + case .warning: .warn + case .error: .error + } + } + @inlinable public static func < (lhs: LogLevel, rhs: LogLevel) -> Bool { lhs.rawValue < rhs.rawValue @@ -263,3 +309,14 @@ extension LKRTCLoggingSeverity { } } } + +extension LogForwardLevel { + var osLogType: OSLogType { + switch self { + case .error: .error + case .warn: .default + case .info: .info + case .debug, .trace: .debug + } + } +} From 68c05d890cdacef51ecb67b712a7726fdb0ef34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Fri, 24 Oct 2025 10:48:44 +0200 Subject: [PATCH 5/6] Fix log level --- Sources/LiveKit/Support/Logger.swift | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/Sources/LiveKit/Support/Logger.swift b/Sources/LiveKit/Support/Logger.swift index da78fb31a..a16d8ec95 100644 --- a/Sources/LiveKit/Support/Logger.swift +++ b/Sources/LiveKit/Support/Logger.swift @@ -113,14 +113,10 @@ public struct PrintLogger: Logger { open class OSLogger: Logger, @unchecked Sendable { private static let subsystem = "io.livekit.sdk" - private static let queue = DispatchQueue(label: "io.livekit.oslogger", qos: .utility) - - private nonisolated(unsafe) static var ffiBootstrapped = false - + private let queue = DispatchQueue(label: "io.livekit.oslogger", qos: .utility) private var logs: [String: OSLog] = [:] private lazy var rtcLogger = LKRTCCallbackLogger() - private var ffiLogForwardTask: Task? private let minLevel: LogLevel @@ -138,7 +134,6 @@ open class OSLogger: Logger, @unchecked Sendable { deinit { rtcLogger.stop() - ffiLogForwardTask?.cancel() } public func log( @@ -162,7 +157,7 @@ open class OSLogger: Logger, @unchecked Sendable { let metadata = buildScopedMetadataString() - Self.queue.async { + queue.async { func getOSLog(for type: Any.Type) -> OSLog { let typeName = String(describing: type) @@ -189,18 +184,15 @@ open class OSLogger: Logger, @unchecked Sendable { } private func startFFILogForwarding(minLevel: LogLevel) { - Self.queue.sync { - if !Self.ffiBootstrapped { - logForwardBootstrap(level: minLevel.logForwardFilter) - Self.ffiBootstrapped = true - } - } + Task(priority: .utility) { [weak self] in + guard self != nil else { return } // don't initialize global level when releasing + logForwardBootstrap(level: minLevel.logForwardFilter) - let ffiLog = OSLog(subsystem: Self.subsystem, category: "FFI") + let ffiLog = OSLog(subsystem: Self.subsystem, category: "FFI") + let ffiStream = AsyncStream(unfolding: logForwardReceive) - ffiLogForwardTask = Task(priority: .utility) { - for await entry in AsyncStream(unfolding: logForwardReceive) { - guard !Task.isCancelled else { break } + for await entry in ffiStream { + guard self != nil else { return } let message = "\(entry.target) \(entry.message)" From cf38ca2a998349fc00f1da36bc9f41f055fd981c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:43:09 +0100 Subject: [PATCH 6/6] JWT logic --- .../LiveKit/Token/CachingTokenSource.swift | 28 +++-- Sources/LiveKit/Token/JWT.swift | 107 ------------------ .../Token/TokenSourceTests.swift | 39 ++++++- Tests/LiveKitTestSupport/Room.swift | 25 +++- Tests/LiveKitTestSupport/TokenGenerator.swift | 29 +---- 5 files changed, 77 insertions(+), 151 deletions(-) delete mode 100644 Sources/LiveKit/Token/JWT.swift diff --git a/Sources/LiveKit/Token/CachingTokenSource.swift b/Sources/LiveKit/Token/CachingTokenSource.swift index 18f23499d..73d37e74a 100644 --- a/Sources/LiveKit/Token/CachingTokenSource.swift +++ b/Sources/LiveKit/Token/CachingTokenSource.swift @@ -14,6 +14,7 @@ * limitations under the License. */ +internal import LiveKitFFI import Foundation /// A token source that caches credentials from any other ``TokenSourceConfigurable`` using a configurable store. @@ -148,20 +149,27 @@ public extension TokenSourceResponse { return false } - do { - try jwt.nbf.verifyNotBefore() - try jwt.exp.verifyNotExpired(currentDate: Date().addingTimeInterval(tolerance)) - } catch { - return false - } - - return true + return jwt.nbf.verifyNotBefore() && jwt.exp.verifyNotExpired(currentDate: Date().addingTimeInterval(tolerance)) } /// Extracts the JWT payload from the participant token. /// /// - Returns: The JWT payload if successfully parsed, nil otherwise - internal func jwt() -> LiveKitJWTPayload? { - LiveKitJWTPayload.fromUnverified(token: participantToken) + internal func jwt() -> Claims? { + try? tokenClaimsFromUnverified(token: participantToken) + } +} + +private extension UInt64 { + var asDate: Date { + Date(timeIntervalSince1970: TimeInterval(self)) + } + + func verifyNotBefore(currentDate: Date = Date()) -> Bool { + currentDate >= asDate + } + + func verifyNotExpired(currentDate: Date = Date()) -> Bool { + currentDate < asDate } } diff --git a/Sources/LiveKit/Token/JWT.swift b/Sources/LiveKit/Token/JWT.swift deleted file mode 100644 index c7a51df3b..000000000 --- a/Sources/LiveKit/Token/JWT.swift +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2025 LiveKit - * - * 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. - */ - -// To be swapped with ffi -internal import JWTKit - -/// JWT payload structure for LiveKit authentication tokens. -struct LiveKitJWTPayload: JWTPayload, Codable, Equatable { - /// Video-specific permissions and room access grants for the participant. - struct VideoGrant: Codable, Equatable { - /// Name of the room. Required for admin or join permissions. - let room: String? - /// Permission to create new rooms. - let roomCreate: Bool? - /// Permission to join a room as a participant. Requires `room` to be set. - let roomJoin: Bool? - /// Permission to list available rooms. - let roomList: Bool? - /// Permission to start recording sessions. - let roomRecord: Bool? - /// Permission to control a specific room. Requires `room` to be set. - let roomAdmin: Bool? - - /// Allow participant to publish tracks. If neither `canPublish` or `canSubscribe` is set, both are enabled. - let canPublish: Bool? - /// Allow participant to subscribe to other participants' tracks. - let canSubscribe: Bool? - /// Allow participant to publish data messages. Defaults to `true` if not set. - let canPublishData: Bool? - /// Allowed track sources for publishing (e.g., "camera", "microphone", "screen_share"). - let canPublishSources: [String]? - /// Hide participant from other participants in the room. - let hidden: Bool? - /// Mark participant as a recorder. When set, allows room to indicate it's being recorded. - let recorder: Bool? - - init(room: String? = nil, - roomCreate: Bool? = nil, - roomJoin: Bool? = nil, - roomList: Bool? = nil, - roomRecord: Bool? = nil, - roomAdmin: Bool? = nil, - canPublish: Bool? = nil, - canSubscribe: Bool? = nil, - canPublishData: Bool? = nil, - canPublishSources: [String]? = nil, - hidden: Bool? = nil, - recorder: Bool? = nil) - { - self.room = room - self.roomCreate = roomCreate - self.roomJoin = roomJoin - self.roomList = roomList - self.roomRecord = roomRecord - self.roomAdmin = roomAdmin - self.canPublish = canPublish - self.canSubscribe = canSubscribe - self.canPublishData = canPublishData - self.canPublishSources = canPublishSources - self.hidden = hidden - self.recorder = recorder - } - } - - /// JWT expiration time claim (when the token expires). - let exp: ExpirationClaim - /// JWT issuer claim (who issued the token). - let iss: IssuerClaim - /// JWT not-before claim (when the token becomes valid). - let nbf: NotBeforeClaim - /// JWT subject claim (the participant identity). - let sub: SubjectClaim - - /// Display name for the participant in the room. - let name: String? - /// Custom metadata associated with the participant. - let metadata: String? - /// Video-specific permissions and room access grants. - let video: VideoGrant? - - /// Verifies the JWT token's validity by checking expiration and not-before claims. - func verify(using _: JWTSigner) throws { - try nbf.verifyNotBefore() - try exp.verifyNotExpired() - } - - /// Creates a JWT payload from an unverified token string. - /// - /// - Parameter token: The JWT token string to parse - /// - Returns: The parsed JWT payload if successful, nil otherwise - static func fromUnverified(token: String) -> Self? { - try? JWTSigners().unverified(token, as: Self.self) - } -} diff --git a/Tests/LiveKitCoreTests/Token/TokenSourceTests.swift b/Tests/LiveKitCoreTests/Token/TokenSourceTests.swift index bcf08c0d3..1c6c20c0c 100644 --- a/Tests/LiveKitCoreTests/Token/TokenSourceTests.swift +++ b/Tests/LiveKitCoreTests/Token/TokenSourceTests.swift @@ -18,6 +18,7 @@ #if canImport(LiveKitTestSupport) import LiveKitTestSupport #endif +import LiveKitFFI class TokenSourceTests: LKTestCase { actor MockValidJWTSource: TokenSourceConfigurable { @@ -38,7 +39,23 @@ class TokenSourceTests: LKTestCase { identity: options.participantIdentity ?? "test-identity" ) tokenGenerator.name = options.participantName ?? participantName - tokenGenerator.videoGrant = LiveKitJWTPayload.VideoGrant(room: options.roomName ?? "test-room", roomJoin: true) + tokenGenerator.videoGrants = VideoGrants( + roomCreate: false, + roomList: false, + roomRecord: false, + roomAdmin: false, + roomJoin: true, + room: options.roomName ?? "test-room", + destinationRoom: "", + canPublish: false, + canSubscribe: false, + canPublishData: false, + canPublishSources: [], + canUpdateOwnMetadata: false, + ingressAdmin: false, + hidden: false, + recorder: false + ) let token = try tokenGenerator.sign() @@ -74,10 +91,26 @@ class TokenSourceTests: LKTestCase { apiKey: "test-api-key", apiSecret: "test-api-secret", identity: options.participantIdentity ?? "test-identity", - ttl: -60 + ttl: 0 ) tokenGenerator.name = options.participantName ?? "test-participant" - tokenGenerator.videoGrant = LiveKitJWTPayload.VideoGrant(room: options.roomName ?? "test-room", roomJoin: true) + tokenGenerator.videoGrants = VideoGrants( + roomCreate: false, + roomList: false, + roomRecord: false, + roomAdmin: false, + roomJoin: true, + room: options.roomName ?? "test-room", + destinationRoom: "", + canPublish: false, + canSubscribe: false, + canPublishData: false, + canPublishSources: [], + canUpdateOwnMetadata: false, + ingressAdmin: false, + hidden: false, + recorder: false + ) let token = try tokenGenerator.sign() diff --git a/Tests/LiveKitTestSupport/Room.swift b/Tests/LiveKitTestSupport/Room.swift index 92e8e516b..152d79b04 100644 --- a/Tests/LiveKitTestSupport/Room.swift +++ b/Tests/LiveKitTestSupport/Room.swift @@ -15,6 +15,7 @@ */ @testable import LiveKit +import LiveKitFFI public struct RoomTestingOptions { public let delegate: RoomDelegate? @@ -78,12 +79,24 @@ public extension LKTestCase { apiSecret: apiSecret, identity: identity) - tokenGenerator.videoGrant = LiveKitJWTPayload.VideoGrant(room: room, - roomJoin: true, - canPublish: canPublish, - canSubscribe: canSubscribe, - canPublishData: canPublishData, - canPublishSources: canPublishSources.map(String.init)) + tokenGenerator.videoGrants = VideoGrants( + roomCreate: false, + roomList: false, + roomRecord: false, + roomAdmin: false, + roomJoin: true, + room: room, + destinationRoom: "", + canPublish: canPublish, + canSubscribe: canSubscribe, + canPublishData: canPublishData, + canPublishSources: canPublishSources.map(String.init), + canUpdateOwnMetadata: false, + ingressAdmin: false, + hidden: false, + recorder: false + ) + return try tokenGenerator.sign() } diff --git a/Tests/LiveKitTestSupport/TokenGenerator.swift b/Tests/LiveKitTestSupport/TokenGenerator.swift index f8f187b89..5dd462836 100644 --- a/Tests/LiveKitTestSupport/TokenGenerator.swift +++ b/Tests/LiveKitTestSupport/TokenGenerator.swift @@ -29,7 +29,7 @@ public class TokenGenerator { public var ttl: TimeInterval public var name: String? public var metadata: String? - public var videoGrant: LiveKitJWTPayload.VideoGrant? + public var videoGrants: VideoGrants? public init(apiKey: String, apiSecret: String, @@ -43,40 +43,19 @@ public class TokenGenerator { } public func sign() throws -> String { - var ffiVideoGrants: VideoGrants? - if let grant = videoGrant { - ffiVideoGrants = VideoGrants( - roomCreate: grant.roomCreate ?? false, - roomList: grant.roomList ?? false, - roomRecord: grant.roomRecord ?? false, - roomAdmin: grant.roomAdmin ?? false, - roomJoin: grant.roomJoin ?? false, - room: grant.room ?? "", - destinationRoom: "", - canPublish: grant.canPublish ?? false, - canSubscribe: grant.canSubscribe ?? false, - canPublishData: grant.canPublishData ?? false, - canPublishSources: grant.canPublishSources ?? [], - canUpdateOwnMetadata: false, - ingressAdmin: false, - hidden: grant.hidden ?? false, - recorder: grant.recorder ?? false - ) - } - let credentials = ApiCredentials(key: apiKey, secret: apiSecret) let options = TokenOptions( ttl: ttl, - videoGrants: ffiVideoGrants, + videoGrants: videoGrants, sipGrants: nil, identity: identity, name: name, metadata: metadata, attributes: nil, sha256: nil, - roomName: videoGrant?.room + roomName: videoGrants?.room ) - return try generateToken(options: options, credentials: credentials) + return try tokenGenerate(options: options, credentials: credentials) } }