ha-frontend/src/auth/ha-authorize.ts

181 lines
5.1 KiB
TypeScript

import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators";
import punycode from "punycode";
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
import { extractSearchParamsObject } from "../common/url/search-params";
import {
AuthProvider,
AuthUrlSearchParams,
fetchAuthProviders,
} from "../data/auth";
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { registerServiceWorker } from "../util/register-service-worker";
import "./ha-auth-flow";
import("./ha-pick-auth-provider");
class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
@property() public clientId?: string;
@property() public redirectUri?: string;
@property() public oauth2State?: string;
@state() private _authProvider?: AuthProvider;
@state() private _authProviders?: AuthProvider[];
constructor() {
super();
this.translationFragment = "page-authorize";
const query = extractSearchParamsObject() as AuthUrlSearchParams;
if (query.client_id) {
this.clientId = query.client_id;
}
if (query.redirect_uri) {
this.redirectUri = query.redirect_uri;
}
if (query.state) {
this.oauth2State = query.state;
}
}
protected render() {
if (!this._authProviders) {
return html`
<p>${this.localize("ui.panel.page-authorize.initializing")}</p>
`;
}
// We don't have a good approach yet to map text markup in localization.
// So we sanitize the translation with innerText and then inject
// the name with a bold tag.
const loggingInWith = document.createElement("div");
loggingInWith.innerText = this.localize(
"ui.panel.page-authorize.logging_in_with",
"authProviderName",
"NAME"
);
loggingInWith.innerHTML = loggingInWith.innerHTML.replace(
"**NAME**",
`<b>${this._authProvider!.name}</b>`
);
const inactiveProviders = this._authProviders.filter(
(prv) => prv !== this._authProvider
);
return html`
<p>
${this.localize(
"ui.panel.page-authorize.authorizing_client",
"clientId",
this.clientId ? punycode.toASCII(this.clientId) : this.clientId
)}
</p>
${loggingInWith}
<ha-auth-flow
.resources=${this.resources}
.clientId=${this.clientId}
.redirectUri=${this.redirectUri}
.oauth2State=${this.oauth2State}
.authProvider=${this._authProvider}
></ha-auth-flow>
${inactiveProviders.length > 0
? html`
<ha-pick-auth-provider
.resources=${this.resources}
.clientId=${this.clientId}
.authProviders=${inactiveProviders}
@pick-auth-provider=${this._handleAuthProviderPick}
></ha-pick-auth-provider>
`
: ""}
`;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this._fetchAuthProviders();
if (matchMedia("(prefers-color-scheme: dark)").matches) {
applyThemesOnElement(
document.documentElement,
{
default_theme: "default",
default_dark_theme: null,
themes: {},
darkMode: false,
},
"default",
{ dark: true }
);
}
if (!this.redirectUri) {
return;
}
// If we are logging into the instance that is hosting this auth form
// we will register the service worker to start preloading.
const tempA = document.createElement("a");
tempA.href = this.redirectUri!;
if (tempA.host === location.host) {
registerServiceWorker(this, false);
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("language")) {
document.querySelector("html")!.setAttribute("lang", this.language!);
}
}
private async _fetchAuthProviders() {
// Fetch auth providers
try {
// We prefetch this data on page load in authorize.html.template for modern builds
const response = await ((window as any).providersPromise ||
fetchAuthProviders());
const authProviders = await response.json();
// Forward to main screen which will redirect to right onboarding page.
if (
response.status === 400 &&
authProviders.code === "onboarding_required"
) {
location.href = `/onboarding.html${location.search}`;
return;
}
if (authProviders.length === 0) {
alert("No auth providers returned. Unable to finish login.");
return;
}
this._authProviders = authProviders;
this._authProvider = authProviders[0];
} catch (err: any) {
// eslint-disable-next-line
console.error("Error loading auth providers", err);
}
}
private async _handleAuthProviderPick(ev) {
this._authProvider = ev.detail;
}
static get styles(): CSSResultGroup {
return css`
ha-pick-auth-provider {
display: block;
margin-top: 48px;
}
`;
}
}
customElements.define("ha-authorize", HaAuthorize);