2019-04-27 10:37:15 -07:00

114 lines
2.8 KiB
TypeScript

/**
* Auth class that connects to a native app for authentication.
*/
import { Auth } from "home-assistant-js-websocket";
import { ExternalMessaging, InternalMessage } from "./external_messaging";
const CALLBACK_SET_TOKEN = "externalAuthSetToken";
const CALLBACK_REVOKE_TOKEN = "externalAuthRevokeToken";
interface BasePayload {
callback: string;
}
interface RefreshTokenResponse {
access_token: string;
expires_in: number;
}
declare global {
interface Window {
externalApp?: {
getExternalAuth(payload: string);
revokeExternalAuth(payload: string);
externalBus(payload: string);
};
webkit?: {
messageHandlers: {
getExternalAuth: {
postMessage(payload: BasePayload);
};
revokeExternalAuth: {
postMessage(payload: BasePayload);
};
externalBus: {
postMessage(payload: InternalMessage);
};
};
};
}
}
if (!window.externalApp && !window.webkit) {
throw new Error(
"External auth requires either externalApp or webkit defined on Window object."
);
}
class ExternalAuth extends Auth {
public external?: ExternalMessaging;
constructor(hassUrl: string) {
super({
hassUrl,
clientId: "",
refresh_token: "",
access_token: "",
expires_in: 0,
// This will trigger connection to do a refresh right away
expires: 0,
});
}
public async refreshAccessToken() {
const callbackPayload = { callback: CALLBACK_SET_TOKEN };
if (window.externalApp) {
window.externalApp.getExternalAuth(JSON.stringify(callbackPayload));
} else {
window.webkit!.messageHandlers.getExternalAuth.postMessage(
callbackPayload
);
}
const tokens = await new Promise<RefreshTokenResponse>(
(resolve, reject) => {
window[CALLBACK_SET_TOKEN] = (success, data) =>
success ? resolve(data) : reject(data);
}
);
this.data.access_token = tokens.access_token;
this.data.expires = tokens.expires_in * 1000 + Date.now();
}
public async revoke() {
const callbackPayload = { callback: CALLBACK_REVOKE_TOKEN };
if (window.externalApp) {
window.externalApp.revokeExternalAuth(JSON.stringify(callbackPayload));
} else {
window.webkit!.messageHandlers.revokeExternalAuth.postMessage(
callbackPayload
);
}
await new Promise((resolve, reject) => {
window[CALLBACK_REVOKE_TOKEN] = (success, data) =>
success ? resolve(data) : reject(data);
});
}
}
export const createExternalAuth = (hassUrl: string) => {
const auth = new ExternalAuth(hassUrl);
if (
(window.externalApp && window.externalApp.externalBus) ||
(window.webkit && window.webkit.messageHandlers.externalBus)
) {
auth.external = new ExternalMessaging();
auth.external.attach();
}
return auth;
};