package auth
import (
"time"
"github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/v3/jwt"
"github.com/livekit/protocol/livekit"
)
const (
defaultValidDuration = 6 * time.Hour
)
type AccessToken struct {
apiKey string
secret string
grant ClaimGrants
validFor time.Duration
allowSensitiveCredentials bool
}
func NewAccessToken(key string, secret string) *AccessToken {
return &AccessToken{
apiKey: key,
secret: secret,
}
}
func (t *AccessToken) SetIdentity(identity string) *AccessToken {
t.grant.Identity = identity
return t
}
func (t *AccessToken) SetValidFor(duration time.Duration) *AccessToken {
t.validFor = duration
return t
}
func (t *AccessToken) SetName(name string) *AccessToken {
t.grant.Name = name
return t
}
func (t *AccessToken) SetKind(kind livekit.ParticipantInfo_Kind) *AccessToken {
t.grant.SetParticipantKind(kind)
return t
}
func (t *AccessToken) SetKindDetail(details ...livekit.ParticipantInfo_KindDetail) *AccessToken {
t.grant.SetKindDetail(details...)
return t
}
func (t *AccessToken) AddGrant(grant *VideoGrant) *AccessToken {
return t.SetVideoGrant(grant)
}
func (t *AccessToken) SetVideoGrant(grant *VideoGrant) *AccessToken {
t.grant.Video = grant
return t
}
func (t *AccessToken) AddSIPGrant(grant *SIPGrant) *AccessToken {
return t.SetSIPGrant(grant)
}
func (t *AccessToken) SetSIPGrant(grant *SIPGrant) *AccessToken {
t.grant.SIP = grant
return t
}
func (t *AccessToken) SetAgentGrant(grant *AgentGrant) *AccessToken {
t.grant.Agent = grant
return t
}
func (t *AccessToken) SetInferenceGrant(grant *InferenceGrant) *AccessToken {
t.grant.Inference = grant
return t
}
func (t *AccessToken) SetObservabilityGrant(grant *ObservabilityGrant) *AccessToken {
t.grant.Observability = grant
return t
}
func (t *AccessToken) SetMetadata(md string) *AccessToken {
t.grant.Metadata = md
return t
}
func (t *AccessToken) SetAttributes(attrs map[string]string) *AccessToken {
if len(attrs) == 0 {
return t
}
if t.grant.Attributes == nil {
t.grant.Attributes = make(map[string]string)
}
for k, v := range attrs {
t.grant.Attributes[k] = v
}
return t
}
func (t *AccessToken) SetSha256(sha string) *AccessToken {
t.grant.Sha256 = sha
return t
}
func (t *AccessToken) SetRoomPreset(preset string) *AccessToken {
t.grant.RoomPreset = preset
return t
}
func (t *AccessToken) SetRoomConfig(config *livekit.RoomConfiguration) *AccessToken {
if config == nil {
t.grant.RoomConfig = nil
} else {
t.grant.RoomConfig = (*RoomConfiguration)(config)
}
return t
}
func (t *AccessToken) SetAgents(agents ...*livekit.RoomAgentDispatch) *AccessToken {
if t.grant.RoomConfig == nil {
t.grant.RoomConfig = &RoomConfiguration{}
}
t.grant.RoomConfig.Agents = agents
return t
}
func (t *AccessToken) SetAllowSensitiveCredentials(allow bool) *AccessToken {
t.allowSensitiveCredentials = allow
return t
}
func (t *AccessToken) GetGrants() *ClaimGrants {
return &t.grant
}
func (t *AccessToken) ToJWT() (string, error) {
if t.apiKey == "" || t.secret == "" {
return "", ErrKeysMissing
}
if t.grant.RoomConfig != nil && !t.allowSensitiveCredentials {
if err := t.grant.RoomConfig.CheckCredentials(); err != nil {
return "", err
}
}
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: []byte(t.secret)},
(&jose.SignerOptions{}).WithType("JWT"))
if err != nil {
return "", err
}
validFor := defaultValidDuration
if t.validFor > 0 {
validFor = t.validFor
}
cl := jwt.Claims{
Issuer: t.apiKey,
NotBefore: jwt.NewNumericDate(time.Now()),
Expiry: jwt.NewNumericDate(time.Now().Add(validFor)),
Subject: t.grant.Identity,
}
return jwt.Signed(sig).Claims(cl).Claims(&t.grant).CompactSerialize()
}