Skip to content

Webrtc 170 #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .env

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only qualm with this is that we encourage customers to fetch auth tokens from the frontend, and storing the BW Username and pass there is not safe

it is a sample app at the end of the day, but the tokens shouldn't be generated client side, a backend should handle that so that the user/pass is nowhere in the client

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
REACT_APP_ACCOUNT_USERNAME=+191xxxxxxx
REACT_APP_ACCOUNT_DISPLAY_NAME=+1919xxxxxx
REACT_APP_ACCOUNT_PASSWORD=+1919xxxxxxx
REACT_APP_AUTH_TOKEN=xxxxxxxx
REACT_APP_ACCOUNT_USERNAME=xxxxxxxxxx
REACT_APP_ACCOUNT_DISPLAY_NAME=xxxxxxxxx
REACT_APP_ACCOUNT_PASSWORD=xxxxxxxxx
REACT_APP_AUTH_URL=https://authtoken.url

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will always be the id.bandwidth URL correct? Should we just set it for the user since it is not a secret value?

REACT_APP_AUTH_CREDENTIALS='username:pass'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"axios": "^1.5.0",
"cors": "^2.8.5",
"firebase": "^10.12.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down Expand Up @@ -59,4 +60,4 @@
"last 1 safari version"
]
}
}
}
85 changes: 85 additions & 0 deletions src/auth_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Store token and expiry in localStorage
const TOKEN_STORAGE_KEY = 'auth_token';
const EXPIRY_STORAGE_KEY = 'token_expiry';

// Your API details
const AUTH_URL = process.env.REACT_APP_AUTH_URL;
const header = process.env.REACT_APP_AUTH_CREDENTIALS;
const BASIC_AUTH_CREDENTIALS = btoa(header); // Base64 encoding for Basic Auth

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of having repeated variables (username, password, and authCredentials which is just username:password) - can we simplify the env vars and construct the header from the provided user/pass env vars?

const REFRESH_INTERVAL = 60 * 1000; // Check every 60 seconds

// Function to fetch new token
const fetchAuthToken = async () => {
try {
var headers = {
'Authorization': 'Basic ' + BASIC_AUTH_CREDENTIALS,
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Origin': '*',
};
const response = await fetch(`${AUTH_URL}`, {
method: 'post',
headers: headers,
body: 'grant_type=client_credentials'
},);

const data = await response.json();

console.log("Data: " + data);

const { access_token, expires_in } = data;

// Store token and expiry time
localStorage.setItem(TOKEN_STORAGE_KEY, access_token);
localStorage.setItem(EXPIRY_STORAGE_KEY, Date.now() + expires_in * 1000);
return access_token;
} catch (error) {
console.error("Error fetching auth token", error);
return null;
}
};

// Function to check and refresh token periodically
const startTokenRefreshLoop = () => {
setInterval(async () => {
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
const expiryTime = localStorage.getItem(EXPIRY_STORAGE_KEY);

if (!token || !expiryTime) {
console.log("[oAuth Service] No token found, fetching a new one...");
await fetchAuthToken();
return;
}

const timeLeft = expiryTime - Date.now();
console.log(`[oAuth Service] Token expires in ${Math.floor(timeLeft / 1000)} seconds`);

// Refresh token if it will expire in less than 2 minutes
if (timeLeft < 2 * 60 * 1000) {
console.log("[oAuth Service] Refreshing token before expiry...");
await fetchAuthToken();
}
}, REFRESH_INTERVAL);
};


// Function to check token validity and refresh if needed
const getAuthToken = async () => {
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
const expiryTime = parseInt(localStorage.getItem(EXPIRY_STORAGE_KEY), 10);

if (token && expiryTime && Date.now() < expiryTime - 60000) { // Refresh 1 min before expiry
return token;
}

return await fetchAuthToken();
};

// Initialize authentication flow
const initAuth = async () => {
await fetchAuthToken(); // Get initial token
startTokenRefreshLoop(); // Start background refresh
};

export { initAuth, TOKEN_STORAGE_KEY };
38 changes: 22 additions & 16 deletions src/components/DialPad.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { db, setupNotifications } from '../fireabase_helper';
import '../css/DialPad.css';
import StatusBar from './StatusBar';
import DigitGrid from './DigitGrid';
import { initAuth, TOKEN_STORAGE_KEY } from '../auth_service'
import NumberInput from './NumberInput';
import CallControlButton from './CallControlButton';
import CallIcon from '@mui/icons-material/Call';
Expand All @@ -16,7 +17,6 @@ import { Button } from '@mui/material';
export default function DialPad() {
console.log("Dialpad rendering...");
const userId = process.env.REACT_APP_ACCOUNT_USERNAME;
const authToken = process.env.REACT_APP_AUTH_TOKEN;
const sourceNumber = userId;
console.log('User ID:', userId);

Expand Down Expand Up @@ -56,6 +56,7 @@ export default function DialPad() {
setIncomingCall(true);
updateFBStatus('Ringing');
});
initAuth();
}, []);

useEffect(() => {
Expand Down Expand Up @@ -93,7 +94,6 @@ export default function DialPad() {
);
newPhone.checkAvailableDevices();
newPhone.setAccount(`${sourceNumber}`, 'In-App Calling Sample', '');
newPhone.setOAuthToken(authToken);
newPhone.init();
setPhone(newPhone);
}, []);
Expand Down Expand Up @@ -270,21 +270,27 @@ export default function DialPad() {
}

const handleDialClick = () => {
if (phone.isInitialized()) {
updateFBStatus("Calling");
setCallStatus('Calling');
setWebRtcStatus('Ringing');
let extraHeaders = [`User-to-User:eyJhbGciOiJIUzI1NiJ9.WyJoaSJd.-znkjYyCkgz4djmHUPSXl9YrJ6Nix_XvmlwKGFh5ERM;encoding=jwt;aGVsbG8gd29ybGQ;encoding=base64`];
console.log("Dialed number: ", destNumber);
phone.makeCall(`${destNumber}`, extraHeaders).then((value) => {
setActiveCall(value);
});
setDialedNumber(`+${destNumber}`);
setAllowHangup(true);
setAllowBackspace(false);
reset();
var authToken = localStorage.getItem(TOKEN_STORAGE_KEY);
if (authToken) {
if (phone.isInitialized()) {
updateFBStatus("Calling");
setCallStatus('Calling');
setWebRtcStatus('Ringing');
let extraHeaders = [`User-to-User:eyJhbGciOiJIUzI1NiJ9.WyJoaSJd.-znkjYyCkgz4djmHUPSXl9YrJ6Nix_XvmlwKGFh5ERM;encoding=jwt;aGVsbG8gd29ybGQ;encoding=base64`];
console.log("Dialed number: ", destNumber);
phone.setOAuthToken(authToken);
phone.makeCall(`${destNumber}`, extraHeaders).then((value) => {
setActiveCall(value);
});
setDialedNumber(`+${destNumber}`);
setAllowHangup(true);
setAllowBackspace(false);
reset();
} else {
console.error("BandwithUA not initialized!");
}
} else {
console.error("BandwithUA not initialized!");
console.error("Invalid/Empty oAuth Token");
}
};

Expand Down