diff --git a/index.js b/index.js index 233618e..ad440b1 100644 --- a/index.js +++ b/index.js @@ -30,6 +30,18 @@ const FIREBASE_CONFIG = JSON.stringify({ vapidKey: process.env.FIREBASE_VAPID_KEY, }) +function getOAuthConfig(req) { return JSON.stringify({ + authority: "cf-reference", // dummy authority + metadata: { + issuer: process.env.SIGNALWIRE_FABRIC_API_URL, + authorization_endpoint: process.env.OAUTH_AUTH_URI, + token_endpoint: process.env.OAUTH_TOKEN_URI, + }, + client_id: process.env.OAUTH_CLIENT_ID, + redirect_uri: getCallbackUrl(req), + response_type: "code", +})} + const host = process.env.RELAY_HOST const fabricApiUrl = process.env.SIGNALWIRE_FABRIC_API_URL @@ -99,7 +111,7 @@ async function getSubscriberToken(reference, password) { } app.get('/', async (req, res) => { - let token +let token let user if (req.session && req.session.token) { token = req.session.token @@ -108,11 +120,12 @@ app.get('/', async (req, res) => { res.render('index', { host, - token: token, +token: token, user: user, fabricApiUrl: fabricApiUrl, destination: process.env.DEFAULT_DESTINATION, firebaseConfig: FIREBASE_CONFIG, + oauthConfig: getOAuthConfig(req) }) }) @@ -131,46 +144,32 @@ app.get('/minimal', async (req, res) => { }) }) -app.get('/oauth', (req, res) => { - console.log('oauth: begin initiation') - - const authEndpoint = process.env.OAUTH_AUTH_URI - const verifier = base64url(crypto.pseudoRandomBytes(32)) - req.session.verifier = verifier - const challenge = base64url( - crypto.createHash('sha256').update(verifier).digest() - ) - const currentHost = `${req.protocol}://${req.get('host')}` - - const queryParams = new URLSearchParams({ - response_type: 'code', - client_id: process.env.OAUTH_CLIENT_ID, - redirect_uri: getCallbackUrl(req), - code_challenge: challenge, - code_challenge_method: 'S256', - }) - - const authorizationUri = `${authEndpoint}?${queryParams}` - - res.redirect(authorizationUri) -}) - app.get('/callback', async (req, res) => { console.log('oauth: process callback') - const callbackUrl = getCallbackUrl(req) - try { - const tokenData = await getAccessToken(req.query.code, req.session.verifier, callbackUrl) - const token = tokenData.access_token - req.session.token = token + res.render('index', { + host, + token: null, + user: null, + fabricApiUrl: fabricApiUrl, + destination: process.env.DEFAULT_DESTINATION, + firebaseConfig: FIREBASE_CONFIG, + oauthConfig: getOAuthConfig(req), + }) +}) - const userInfo = await getUserInfo(token) - req.session.user = userInfo +app.get('/userinfo', async (req, res) => { + const accessToken = req.headers['authorization'].split(' ')[1]; + - res.redirect('/') - } catch (error) { - console.error(error) + if(!accessToken || !accessToken.length) { + res.sendStatus(401) } + req.session.token = accessToken + const userInfo = await getUserInfo(accessToken) + req.session.user = userInfo + + res.json(userInfo) }) app.get('/subscriber', (req, res) => { diff --git a/public/full.js b/public/full.js index 7bae521..eba51cc 100644 --- a/public/full.js +++ b/public/full.js @@ -13,6 +13,19 @@ const { createMicrophoneAnalyzer, } = SignalWire +const { + UserManager, + WebStorageStateStore +} = oidc + +window.UserManager = new UserManager({..._oauth_config, + userStore: new WebStorageStateStore({ store: window.localStorage }), +}) + +window.signin = async () => { + await window.UserManager.signinRedirect() +} + const searchInput = document.getElementById('searchInput') const searchType = document.getElementById('searchType') @@ -362,6 +375,7 @@ function restoreUI() { } async function getClient() { + const _token = (await window.UserManager.getUser())?.access_token if (!client && _token) { client = await SWire({ host: !!_host && _host.trim().length ? _host : undefined, @@ -370,6 +384,12 @@ async function getClient() { logWsTraffic: true, }, logLevel: 'debug', + maxConnectionStateTimeout: 9000, + onRefreshToken: async () => { + await window.UserManager.signinSilent() + const user = await window.UserManager.getUser() + return user?.access_token + } }) } @@ -399,6 +419,7 @@ window.connect = async () => { } try { + window._beforeDial = performance.now(); const call = await client.dial({ to: document.getElementById('destination').value, logLevel: 'debug', @@ -411,7 +432,9 @@ window.connect = async () => { roomObj = call roomObj.on('media.connected', () => { + window._afterMediaConnected = performance.now(); console.debug('>> media.connected') + console.debug(`⏱️⏱️⏱️ From dial() to media.connect: ${window._afterMediaConnected - window._beforeDial}ms ⏱️⏱️⏱️`) }) roomObj.on('media.reconnecting', () => { console.debug('>> media.reconnecting') @@ -420,9 +443,17 @@ window.connect = async () => { console.debug('>> media.disconnected') }) - roomObj.on('room.started', (params) => + roomObj.on('room.started', (params) => { console.debug('>> room.started', params) - ) + window._afterRoomStared = performance.now() + console.debug(`⏱️⏱️⏱️ From dial() to room.started: ${window._afterRoomStared - window._beforeDial}ms ⏱️⏱️⏱️`) + }) + + roomObj.on('room.joined', (params) => { + console.debug('>> room.joined', params) + window._afterRoomJoined = performance.now() + console.debug(`⏱️⏱️⏱️ From dial() to room.joined: ${window._afterRoomJoined - window._beforeDial}ms ⏱️⏱️⏱️`) + }) roomObj.on('destroy', () => { console.debug('>> destroy') @@ -964,13 +995,52 @@ window.seekForwardPlayback = () => { }) } +const updateUserInfoUI = async (accessToken) => { + if(!accessToken) return + + const headers = {Authorization: `Bearer ${accessToken}`} + const user = await (await fetch('/userinfo', {headers})).json(); + userInfo.innerHTML = `
User Info
+
+ +
` +} + window.ready(async function () { console.log('Ready!') + const searchParams = new URLSearchParams(location.search) + if (searchParams.has('code')) { + console.log('signinCallback!') + await window.UserManager.signinCallback() + } else { + try { + await window.UserManager.signinSilent() + } catch {} + } + + const accessToken = (await window.UserManager.getUser())?.access_token + + if(accessToken) { + await updateUserInfoUI(accessToken) + + } + const client = await getClient() if (client) { await client.connect() } - const searchParams = new URLSearchParams(location.search) + console.log('Handle inbound?', searchParams.has('inbound')) if (searchParams.has('inbound')) { await enablePushNotifications() diff --git a/views/index.ejs b/views/index.ejs index 53c5de2..96e8eeb 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -10,6 +10,9 @@ + + + @@ -29,7 +32,7 @@ Minimal