Check access token expires before we use it. (#1444)

* Check access token expires before we use it.If we found access token expires, we proactive call refresh token.So that we can avoid uncessary invalid auth error in backend.

* Move expires  calc to fetchToken() and refresToken()

* Address code review comment
This commit is contained in:
Jason Hu 2018-07-16 14:11:05 -07:00 committed by Paulus Schoutsen
parent b5ff52febf
commit 348b284ba6
4 changed files with 37 additions and 9 deletions

View File

@ -8,6 +8,8 @@ export default function fetchToken(clientId, code) {
body: data, body: data,
}).then((resp) => { }).then((resp) => {
if (!resp.ok) throw new Error('Unable to fetch tokens'); if (!resp.ok) throw new Error('Unable to fetch tokens');
return resp.json(); const tokens = resp.json();
tokens.expires = (tokens.expires_in * 1000) + Date.now();
return tokens;
}); });
} }

View File

@ -8,6 +8,8 @@ export default function refreshAccessToken(clientId, refreshToken) {
body: data, body: data,
}).then((resp) => { }).then((resp) => {
if (!resp.ok) throw new Error('Unable to fetch tokens'); if (!resp.ok) throw new Error('Unable to fetch tokens');
return resp.json(); const tokens = resp.json();
tokens.expires = (tokens.expires_in * 1000) + Date.now();
return tokens;
}); });
} }

View File

@ -235,13 +235,20 @@ class HomeAssistant extends LocalizeMixin(PolymerElement) {
const host = window.location.protocol + '//' + window.location.host; const host = window.location.protocol + '//' + window.location.host;
const auth = conn.options; const auth = conn.options;
try { try {
// Refresh token if it will expire in 30 seconds
if (auth.accessToken && Date.now() + 30000 > auth.expires) {
const accessToken = await window.refreshToken();
conn.options.accessToken = accessToken.access_token;
conn.options.expires = accessToken.expires;
}
return await hassCallApi(host, auth, method, path, parameters); return await hassCallApi(host, auth, method, path, parameters);
} catch (err) { } catch (err) {
if (!err || err.status_code !== 401 || !auth.accessToken) throw err; if (!err || err.status_code !== 401 || !auth.accessToken) throw err;
// If we connect with access token and get 401, refresh token and try again // If we connect with access token and get 401, refresh token and try again
const accessToken = await window.refreshToken(); const accessToken = await window.refreshToken();
conn.options.accessToken = accessToken; conn.options.accessToken = accessToken.access_token;
conn.options.expires = accessToken.expires;
return await hassCallApi(host, auth, method, path, parameters); return await hassCallApi(host, auth, method, path, parameters);
} }
}, },

View File

@ -18,7 +18,8 @@ const init = window.createHassConnection = function (password, accessToken) {
if (password) { if (password) {
options.authToken = password; options.authToken = password;
} else if (accessToken) { } else if (accessToken) {
options.accessToken = accessToken; options.accessToken = accessToken.access_token;
options.expires = accessToken.expires;
} }
return createConnection(url, options) return createConnection(url, options)
.then(function (conn) { .then(function (conn) {
@ -40,7 +41,10 @@ window.refreshToken = () =>
refreshToken_(clientId(), window.tokens.refresh_token).then((accessTokenResp) => { refreshToken_(clientId(), window.tokens.refresh_token).then((accessTokenResp) => {
window.tokens.access_token = accessTokenResp.access_token; window.tokens.access_token = accessTokenResp.access_token;
localStorage.tokens = JSON.stringify(window.tokens); localStorage.tokens = JSON.stringify(window.tokens);
return accessTokenResp.access_token; return {
access_token: accessTokenResp.access_token,
expires: window.tokens.expires
};
}, () => redirectLogin()); }, () => redirectLogin());
function resolveCode(code) { function resolveCode(code) {
@ -66,11 +70,24 @@ function main() {
} }
if (localStorage.tokens) { if (localStorage.tokens) {
window.tokens = JSON.parse(localStorage.tokens); window.tokens = JSON.parse(localStorage.tokens);
window.hassConnection = init(null, window.tokens.access_token).catch((err) => { if (window.tokens.expires === undefined) {
// for those tokens got from previous version
window.tokens.expires = Date.now() - 1;
}
if (Date.now() + 30000 > window.tokens.expires) {
// refresh access token if it will expire in 30 seconds to avoid invalid auth event
window.hassConnection = window.refreshToken().then(accessToken => init(null, accessToken));
} else {
const accessTokenObject = {
access_token: window.tokens.access_token,
expires: window.tokens.expires
};
window.hassConnection = init(null, accessTokenObject).catch((err) => {
if (err !== ERR_INVALID_AUTH) throw err; if (err !== ERR_INVALID_AUTH) throw err;
return window.refreshToken().then(accessToken => init(null, accessToken)); return window.refreshToken().then(accessToken => init(null, accessToken));
}); });
}
return; return;
} }
redirectLogin(); redirectLogin();