generate access from within server to avoid distributing api token

This commit is contained in:
Roland Osborne 2024-05-31 14:57:56 -07:00
parent 3e2b70c5ed
commit 3318048ce3
7 changed files with 92 additions and 48 deletions

View File

@ -40,6 +40,17 @@ func AddCall(w http.ResponseWriter, r *http.Request) {
return
}
iceService := getBoolConfigValue(CNFIceService, false);
iceURL := getStrConfigValue(CNFIceUrl, "")
iceUsername := getStrConfigValue(CNFIceUsername, "")
icePassword := getStrConfigValue(CNFIcePassword, "")
ice, err := getIce(iceService, iceURL, iceUsername, icePassword);
if err != nil || len(ice) == 0 {
ErrResponse(w, http.StatusServiceUnavailable, err)
return
}
// generate call params
callerBin, callerErr := securerandom.Bytes(APPTokenSize)
if callerErr != nil {
@ -51,20 +62,11 @@ func AddCall(w http.ResponseWriter, r *http.Request) {
ErrResponse(w, http.StatusInternalServerError, calleeErr)
return
}
//turnBin, turnErr := securerandom.Bytes(APPTokenSize)
//if turnErr != nil {
// ErrResponse(w, http.StatusInternalServerError, turnErr)
// return
//}
callId := uuid.New().String()
// allocate bridge
callerToken := hex.EncodeToString(callerBin);
calleeToken := hex.EncodeToString(calleeBin);
iceService := getBoolConfigValue(CNFIceService, false);
iceUrl := getStrConfigValue(CNFIceUrl, "")
iceUsername := getStrConfigValue(CNFIceUsername, "")
icePassword := getStrConfigValue(CNFIcePassword, "")
bridgeRelay.AddBridge(account.ID, callId, cardId, callerToken, calleeToken);
// create response
@ -73,10 +75,11 @@ func AddCall(w http.ResponseWriter, r *http.Request) {
CardId: cardId,
CallerToken: callerToken,
CalleeToken: calleeToken,
Ice: ice,
IceService: iceService,
IceUrl: iceUrl,
IceUsername: iceUsername,
IcePassword: icePassword,
IceURL: ice[len(ice)-1].URLs,
IceUsername: ice[len(ice)-1].Username,
IcePassword: ice[len(ice)-1].Credential,
KeepAlive: BridgeKeepAlive,
}
WriteResponse(w, call);

View File

@ -26,7 +26,7 @@ func GetNodeConfig(w http.ResponseWriter, r *http.Request) {
config.PushSupported = getBoolConfigValue(CNFPushSupported, true)
config.EnableIce = getBoolConfigValue(CNFEnableIce, false)
config.IceService = getBoolConfigValue(CNFIceService, false)
config.IceUrl = getStrConfigValue(CNFIceUrl, "")
config.IceURL = getStrConfigValue(CNFIceUrl, "")
config.IceUsername = getStrConfigValue(CNFIceUsername, "")
config.IcePassword = getStrConfigValue(CNFIcePassword, "")
config.EnableOpenAccess = getBoolConfigValue(CNFEnableOpenAccess, false);

View File

@ -121,7 +121,7 @@ func SetNodeConfig(w http.ResponseWriter, r *http.Request) {
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"str_value"}),
}).Create(&store.Config{ConfigID: CNFIceUrl, StrValue: config.IceUrl}).Error; res != nil {
}).Create(&store.Config{ConfigID: CNFIceUrl, StrValue: config.IceURL}).Error; res != nil {
return res
}

View File

@ -156,7 +156,7 @@ func SetRing(card *store.Card, ring Ring) {
phone.CallID = ring.CallID
phone.CalleeToken = ring.CalleeToken
phone.Ice = ring.Ice
phone.IceUrl = ring.IceUrl
phone.IceURL = ring.IceURL
phone.IceUsername = ring.IceUsername
phone.IcePassword = ring.IcePassword
phone.CardID = card.CardSlot.CardSlotID

View File

@ -0,0 +1,47 @@
package databag
import (
"encoding/json"
"net/http"
"errors"
"bytes"
)
func getIce(service bool, urls string, username string, credential string) ([]IceURL, error) {
if service {
gen := "https://rtc.live.cloudflare.com/v1/turn/keys/" + username + "/credentials/generate"
req, err := http.NewRequest(http.MethodPost, gen, bytes.NewBuffer([]byte("{\"ttl\": 86400}")))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("Authorization", "Bearer " + credential)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil || resp == nil || resp.StatusCode != 201 {
return nil, errors.New("invalid ice service response")
}
var r IceService
err = json.NewDecoder(resp.Body).Decode(&r)
if err != nil {
return nil, errors.New("invalid ice service response")
}
ice := []IceURL{}
for _, url := range r.Servers.URLs {
ice = append(ice, IceURL{ URLs: url, Username: r.Servers.Username, Credential: r.Servers.Credential });
}
return ice, nil
}
return []IceURL {
IceURL {
URLs: urls,
Username: username,
Credential: credential,
},
}, nil
}

View File

@ -377,7 +377,7 @@ type NodeConfig struct {
IceService bool `json:"iceService"`
IceUrl string `json:"iceUrl"`
IceURL string `json:"iceUrl"`
IceUsername string `json:"iceUsername"`
@ -460,9 +460,9 @@ type Phone struct {
CalleeToken string `json:"calleeToken"`
Ice []IceUrl `json:"ice,omitEmpty"`
Ice []IceURL `json:"ice,omitEmpty"`
IceUrl string `json:"iceUrl"`
IceURL string `json:"iceUrl"`
IceUsername string `json:"iceUsername"`
@ -569,14 +569,30 @@ type Call struct {
IceService bool `json:"iceService"`
IceUrl string `json:"iceUrl"`
Ice []IceURL `json:"ice,omitEmpty"`
IceURL string `json:"iceUrl"`
IceUsername string `json:"iceUsername"`
IcePassword string `json:"icePassword"`
}
type IceUrl struct {
type IceServers struct {
URLs []string `json:"urls"`
Username string `json:"username"`
Credential string `json:"credential"`
}
type IceService struct {
Servers IceServers `json:"iceServers"`
}
type IceURL struct {
URLs string `json:"urls"`
@ -593,9 +609,9 @@ type Ring struct {
Index int32 `json:"index"`
Ice []IceUrl `json:"ice,omitEmpty"`
Ice []IceURL `json:"ice,omitEmpty"`
IceUrl string `json:"iceUrl"`
IceURL string `json:"iceUrl"`
IceUsername string `json:"iceUsername"`

View File

@ -134,28 +134,6 @@ export function useRingContext() {
processing.current = false;
}
const getIce = async (service, urls, username, credential) => {
if (!service) {
return [{ urls, username, credential }];
}
const params = await fetch(urls.replace('%%TURN_KEY_ID%%', username), {
method: 'POST',
body: '{"ttl": 86400}',
headers: new Headers({
'Authorization': `Bearer ${credential}`,
'Content-Type': 'application/json'
})
});
const server = await params.json();
const ice = [];
server.iceServers.urls.forEach(urls => {
const { username, credential } = server.iceServers;
ice.push({ urls, username, credential });
});
return ice;
}
const getAudioStream = async (audioId) => {
try {
if (audioId) {
@ -357,6 +335,8 @@ export function useRingContext() {
}
},
accept: async (cardId, callId, contactNode, contactToken, calleeToken, ice, audioId) => {
console.log("ACCEPT", ice);
if (calling.current) {
throw new Error("active session");
}
@ -409,7 +389,6 @@ export function useRingContext() {
// create call
let call;
let ice;
try {
call = await addCall(access.current, cardId);
}
@ -419,10 +398,9 @@ export function useRingContext() {
}
let index = 0;
const { id, keepAlive, callerToken, calleeToken, iceService, iceUrl, iceUsername, icePassword } = call;
const { id, keepAlive, callerToken, calleeToken, ice } = call;
try {
ice = await getIce(iceService, iceUrl, iceUsername, icePassword);
const turn = ice[ice.length - 1];
const turn = ice[ice.length - 1]; //backwards compatibility
await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken, ice, iceUrl: turn.urls, iceUsername: turn.username, icePassword: turn.credential });
}
catch (err) {