import { AuthData , ERR_INVALID_AUTH , genExpires , ERR_INVALID_HTTPS_TO_HTTP , } from "home-assistant-js-websocket" ; import { saveTokens } from "../HomeAssistant" ; import queryString from "query-string" ; type OAuthState = { hassUrl : string ; clientId : string ; } ; type AuthorizationCodeRequest = { grant_type : "authorization_code" ; code : string ; } ; type RefreshTokenRequest = { grant_type : "refresh_token" ; refresh_token : string ; } ; function decodeOAuthState ( encoded ) { return JSON . parse ( atob ( encoded ) ) ; } async function tokenRequest ( hassUrl , clientId , data ) { const l = typeof window . location !== "undefined" && window . location ; if ( l && l . protocol === "https:" ) { const a = document . createElement ( "a" ) ; a . href = hassUrl ; if ( a . protocol === "http:" && a . hostname !== "localhost" ) { throw ERR_INVALID_HTTPS_TO_HTTP ; } } const formData = new FormData ( ) ; formData . append ( "client_id" , clientId ) ; Object . keys ( data ) . forEach ( ( key ) => { formData . append ( key , data [ key ] ) ; } ) ; const resp = await fetch ( `${ hassUrl }/auth/token` , { method : "POST" , credentials : "same-origin" , body : formData , } ) ; if ( ! resp . ok ) { throw resp . status === 400 || resp . status === 403 ? ERR_INVALID_AUTH : new Error ( "Unable to fetch tokens" ) ; } const tokens = await resp . json ( ) ; tokens . hassUrl = hassUrl ; tokens . clientId = clientId ; tokens . expires = genExpires ( tokens . expires_in ) ; return tokens ; } async function fetchToken ( hassUrl , clientId , code ) { return tokenRequest ( hassUrl , clientId , { code , grant_type : "authorization_code" , } ) ; } export async function parseTokens ( ) { let data ; const query = queryString . parse ( window . location . search ) ; if ( "auth_callback" in query ) { if ( typeof query . state === "string" && typeof query . code === "string" ) { const state = decodeOAuthState ( query . state ) ; data = await fetchToken ( state . hassUrl , state . clientId , query . code ) ; if ( data ) { localStorage . setItem ( "hass_url" , state . hassUrl ) ; saveTokens ( data ) ; window . location . replace ( window . location . href . replace ( window . location . search , "" ) ) ; } } } }