Add automatic refresh of authentication tokens
This commit is contained in:
parent
bea99ea701
commit
b08bd39fef
5 changed files with 74 additions and 5 deletions
|
@ -17,6 +17,10 @@ export function to_iso8601(date) {
|
||||||
return date.toISOString();
|
return date.toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function to_unix(date) {
|
||||||
|
return date.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
export function unix_now() {
|
export function unix_now() {
|
||||||
return Date.now();
|
return Date.now();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub type Config {
|
||||||
debug: Bool,
|
debug: Bool,
|
||||||
api_timeout: Int,
|
api_timeout: Int,
|
||||||
api_auth_url: Uri,
|
api_auth_url: Uri,
|
||||||
|
api_auth_token_refresh_diff: Int,
|
||||||
api_installations_url: Uri,
|
api_installations_url: Uri,
|
||||||
api_device_url: Uri,
|
api_device_url: Uri,
|
||||||
api_device_status_url: Uri,
|
api_device_status_url: Uri,
|
||||||
|
@ -38,6 +39,10 @@ pub fn load_config() -> Config {
|
||||||
config_helpers.api_auth_url,
|
config_helpers.api_auth_url,
|
||||||
"https://thermia-auth-api.azurewebsites.net/api/v1/Jwt/login",
|
"https://thermia-auth-api.azurewebsites.net/api/v1/Jwt/login",
|
||||||
),
|
),
|
||||||
|
api_auth_token_refresh_diff: get_env(
|
||||||
|
config_helpers.api_auth_token_refresh_diff,
|
||||||
|
300_000,
|
||||||
|
),
|
||||||
api_installations_url: get_uri(
|
api_installations_url: get_uri(
|
||||||
config_helpers.api_installations_url,
|
config_helpers.api_installations_url,
|
||||||
"https://online-genesis-serviceapi.azurewebsites.net/api/v1/installationsInfo",
|
"https://online-genesis-serviceapi.azurewebsites.net/api/v1/installationsInfo",
|
||||||
|
|
|
@ -16,6 +16,10 @@ pub fn api_auth_url() {
|
||||||
config_url("api_auth_url")
|
config_url("api_auth_url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn api_auth_token_refresh_diff() {
|
||||||
|
config("api_auth_token_refresh_diff", dynamic.int)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn api_installations_url() {
|
pub fn api_installations_url() {
|
||||||
config_url("api_installations_url")
|
config_url("api_installations_url")
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ pub fn to_iso8601(d: Date) -> String
|
||||||
@external(javascript, "../../date_ffi.mjs", "from_unix")
|
@external(javascript, "../../date_ffi.mjs", "from_unix")
|
||||||
pub fn from_unix(a: Int) -> Result(Date, Nil)
|
pub fn from_unix(a: Int) -> Result(Date, Nil)
|
||||||
|
|
||||||
|
@external(javascript, "../../date_ffi.mjs", "to_unix")
|
||||||
|
pub fn to_unix(a: Date) -> Int
|
||||||
|
|
||||||
@external(javascript, "../../date_ffi.mjs", "now")
|
@external(javascript, "../../date_ffi.mjs", "now")
|
||||||
pub fn now() -> Date
|
pub fn now() -> Date
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import gleam/option.{None, Option, Some}
|
import gleam/option.{None, Option, Some}
|
||||||
|
import gleam/order
|
||||||
import gleam/string
|
import gleam/string
|
||||||
|
import gleam/io
|
||||||
import gleam/javascript/promise
|
import gleam/javascript/promise
|
||||||
import lustre
|
import lustre
|
||||||
import lustre/element/html
|
import lustre/element/html
|
||||||
import lustre/effect.{Effect}
|
import lustre/effect.{Effect}
|
||||||
import lustre/element
|
import lustre/element
|
||||||
import plinth/javascript/storage.{Storage}
|
import plinth/javascript/storage.{Storage}
|
||||||
|
import geo_t/helpers/timers
|
||||||
|
import geo_t/helpers/date
|
||||||
import geo_t/config.{Config}
|
import geo_t/config.{Config}
|
||||||
import geo_t/web/login_view
|
import geo_t/web/login_view
|
||||||
import geo_t/web/installations_view
|
import geo_t/web/installations_view
|
||||||
|
@ -15,6 +19,8 @@ import geo_t/pump_api/api.{ApiError}
|
||||||
import geo_t/pump_api/auth/api as auth_api
|
import geo_t/pump_api/auth/api as auth_api
|
||||||
import geo_t/web/auth.{AuthInfo, AuthInfoStorage}
|
import geo_t/web/auth.{AuthInfo, AuthInfoStorage}
|
||||||
|
|
||||||
|
const auth_update_check_interval = 10_000
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let app = lustre.application(init, update, view)
|
let app = lustre.application(init, update, view)
|
||||||
let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil)
|
let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil)
|
||||||
|
@ -41,6 +47,8 @@ pub type Msg {
|
||||||
LoginResult(Result(User, ApiError))
|
LoginResult(Result(User, ApiError))
|
||||||
InstallationsView(installations_view.Msg)
|
InstallationsView(installations_view.Msg)
|
||||||
PumpView(pump_view.Msg)
|
PumpView(pump_view.Msg)
|
||||||
|
AuthRefreshCheck
|
||||||
|
RefreshLogin
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(_) {
|
fn init(_) {
|
||||||
|
@ -52,6 +60,9 @@ fn init(_) {
|
||||||
Error(_) -> None
|
Error(_) -> None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let auth_refresh_effect =
|
||||||
|
effect.from(fn(dispatch) { auth_refresh_check_timer(dispatch) })
|
||||||
|
|
||||||
let #(inst_model, inst_effect) = case auth_info {
|
let #(inst_model, inst_effect) = case auth_info {
|
||||||
Some(info) ->
|
Some(info) ->
|
||||||
update_installations(
|
update_installations(
|
||||||
|
@ -73,13 +84,15 @@ fn init(_) {
|
||||||
logging_in: False,
|
logging_in: False,
|
||||||
logged_in: option.is_some(auth_info),
|
logged_in: option.is_some(auth_info),
|
||||||
),
|
),
|
||||||
inst_effect,
|
effect.batch([auth_refresh_effect, inst_effect]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(model: Model, msg: Msg) {
|
fn update(model: Model, msg: Msg) {
|
||||||
case msg {
|
case msg {
|
||||||
LoginView(login_view.AttemptLogin) -> {
|
AuthRefreshCheck -> #(model, auth_refresh_check(model))
|
||||||
|
|
||||||
|
RefreshLogin | LoginView(login_view.AttemptLogin) -> {
|
||||||
#(
|
#(
|
||||||
Model(
|
Model(
|
||||||
..model,
|
..model,
|
||||||
|
@ -164,11 +177,11 @@ fn view(model: Model) {
|
||||||
html.div(
|
html.div(
|
||||||
[],
|
[],
|
||||||
[
|
[
|
||||||
case model.logged_in {
|
case model.auth {
|
||||||
False ->
|
None ->
|
||||||
login_view.view(model.login)
|
login_view.view(model.login)
|
||||||
|> element.map(LoginView)
|
|> element.map(LoginView)
|
||||||
True -> {
|
Some(_) -> {
|
||||||
case model.pump {
|
case model.pump {
|
||||||
None ->
|
None ->
|
||||||
installations_view.view(model.installations)
|
installations_view.view(model.installations)
|
||||||
|
@ -183,6 +196,46 @@ fn view(model: Model) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn auth_refresh_check_timer(dispatch) {
|
||||||
|
timers.set_timeout(
|
||||||
|
fn() { dispatch(AuthRefreshCheck) },
|
||||||
|
auth_update_check_interval,
|
||||||
|
)
|
||||||
|
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth_refresh_check(model: Model) {
|
||||||
|
use dispatch <- effect.from()
|
||||||
|
|
||||||
|
auth_refresh_check_timer(dispatch)
|
||||||
|
|
||||||
|
case model.auth {
|
||||||
|
Some(auth) -> {
|
||||||
|
let now = date.now()
|
||||||
|
let assert Ok(last_valid_time) =
|
||||||
|
date.from_unix(
|
||||||
|
date.to_unix(auth.user.tokens.access_token_expiry) - model.config.api_auth_token_refresh_diff,
|
||||||
|
)
|
||||||
|
|
||||||
|
case date.compare(now, last_valid_time) {
|
||||||
|
order.Gt -> {
|
||||||
|
io.println("[AuthCheck] Auth expiring soon, refreshing...")
|
||||||
|
dispatch(RefreshLogin)
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
order.Lt | order.Eq -> {
|
||||||
|
io.println("[AuthCheck] Auth not expiring yet, no need to refresh.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
None -> Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn login(model: Model) -> Effect(Msg) {
|
fn login(model: Model) -> Effect(Msg) {
|
||||||
use dispatch <- effect.from()
|
use dispatch <- effect.from()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue