diff --git a/src/common/auth/external_auth.js b/src/common/auth/external_auth.js new file mode 100644 index 0000000000..85e8d2bbb1 --- /dev/null +++ b/src/common/auth/external_auth.js @@ -0,0 +1,46 @@ +/** + * Auth class that connects to a native app for authentication. + */ +import { Auth } from 'home-assistant-js-websocket'; + +const CALLBACK_METHOD = 'externalAuthSetToken'; + +if (!window.externalApp && !window.webkit) { + throw new Error('External auth requires either externalApp or webkit defined on Window object.'); +} + +export default class ExternalAuth extends Auth { + constructor(hassUrl) { + super(); + + this.data = { + hassUrl, + access_token: '', + // This will trigger connection to do a refresh right away + expires: 0, + }; + } + + async refreshAccessToken() { + const meth = window.externalApp ? + window.externalApp.getExternalAuth : + window.webkit.messageHandlers.getExternalAuth.postMessage; + + const responseProm = new Promise((resolve) => { window[CALLBACK_METHOD] = resolve; }); + + // Allow promise to set resolve on window object. + await 0; + + meth({ callback: CALLBACK_METHOD }); + + // Response we expect back: + // { + // "access_token": "qwere", + // "expires_in": 1800 + // } + const tokens = await responseProm; + + this.data.access_token = tokens.access_token; + this.data.expires = (tokens.expires_in * 1000) + Date.now(); + } +} diff --git a/src/entrypoints/core.js b/src/entrypoints/core.js index 71a87bd5c3..adb0113d8d 100644 --- a/src/entrypoints/core.js +++ b/src/entrypoints/core.js @@ -11,11 +11,18 @@ import { subscribePanels } from '../data/ws-panels.js'; import { subscribeThemes } from '../data/ws-themes.js'; import { subscribeUser } from '../data/ws-user.js'; -window.hassAuth = getAuth({ - hassUrl: `${location.protocol}//${location.host}`, - saveTokens, - loadTokens: () => Promise.resolve(loadTokens()), -}); +const hassUrl = `${location.protocol}//${location.host}`; + +if (location.search.includes('external_auth=1')) { + window.hassAuth = import('../common/auth/external_auth.js') + .then(mod => new mod.default(hassUrl)); +} else { + window.hassAuth = getAuth({ + hassUrl, + saveTokens, + loadTokens: () => Promise.resolve(loadTokens()), + }); +} window.hassConnection = window.hassAuth.then((auth) => { if (location.search.includes('auth_callback=1')) {