Skip to content

Commit

Permalink
feat(service-worker): check if token is expired
Browse files Browse the repository at this point in the history
The time on the token is compared to the current time. If the expiration
occurs before the current time, then the token will attempt to refresh.
  • Loading branch information
justinrubek committed Sep 30, 2023
1 parent 99376dd commit 1f87496
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 27 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ version = "0.2.0"

[workspace.dependencies]
anyhow = "1.0.68"
base64 = "0.21.4"
axum = "0.6.12"
hyper = "0.14"
js-sys = "0.3.61"
Expand Down
2 changes: 2 additions & 0 deletions crates/service-worker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ crate-type = ["cdylib"]

[dependencies]
anyhow = { workspace = true }
base64 = { workspace = true }
gloo-utils = "0.2.0"
js-sys = { workspace = true }
log = { workspace = true }
rexie = "0.4.0"
serde = { workspace = true }
serde_json = { workspace = true }
serde-wasm-bindgen = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
Expand Down
9 changes: 1 addition & 8 deletions crates/service-worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,10 @@ pub async fn on_fetch(event: web_sys::FetchEvent) -> std::result::Result<Promise

let request = modify_request(&global, &event).await?;

let response = fetch_with_request(&request);
let response = global.fetch_with_request(&request);
Ok(response)
}

/// Bindings for the `fetch` function which is available globally in the service worker context
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_name = fetch)]
fn fetch_with_request(request: &web_sys::Request) -> Promise;
}

/// Performs the logic for determining whether to intercept a request and what to do with it.
pub async fn modify_request(
scope: &ServiceWorkerGlobalScope,
Expand Down
19 changes: 0 additions & 19 deletions crates/service-worker/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{constants, error::Result};
use rexie::*;
use serde::{Deserialize, Serialize};
use wasm_bindgen::JsValue;

pub(crate) async fn build_database() -> Result<Rexie> {
Expand All @@ -15,24 +14,6 @@ pub(crate) async fn build_database() -> Result<Rexie> {
Ok(rexie)
}

#[derive(Debug, Serialize, Deserialize)]
pub struct KeyValue {
pub key: String,
pub value: String,
}

impl From<KeyValue> for JsValue {
fn from(kv: KeyValue) -> Self {
serde_wasm_bindgen::to_value(&kv).unwrap()
}
}

impl From<JsValue> for KeyValue {
fn from(value: JsValue) -> Self {
serde_wasm_bindgen::from_value(value).unwrap()
}
}

/// Assigns a value in the key-value store.
pub(crate) async fn set_key(rexie: &Rexie, key: &str, value: &str) -> Result<()> {
let transaction =
Expand Down
36 changes: 36 additions & 0 deletions crates/service-worker/src/token.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::{constants, state};
use base64::Engine as _;
use log::{debug, info};
use serde::{Deserialize, Serialize};
use wasm_bindgen::JsValue;

/// Creates a new request that has the token attached to it on the `Authorization` header.
Expand All @@ -26,6 +28,11 @@ pub(crate) async fn fetch_with_token(
}
}

#[derive(Debug, Serialize, Deserialize)]
struct TokenPayload {
exp: u64,
}

/// Attempts to retrieve the token from the database.
/// If the token is not found then an attempt is made to refresh the token.
/// If the token is successfully refreshed then the new token is returned.
Expand All @@ -42,6 +49,35 @@ async fn retrieve_token(event: &web_sys::FetchEvent) -> Result<Option<String>, J
}
}

// Ensure that the token isn't expired.
if let Some(token) = &token {
// Take the second parts of the token, decode it, and look at the `exp` field.
let token_part: &str = token.split('.').nth(1).unwrap();
let engine = base64::engine::general_purpose::URL_SAFE_NO_PAD;
let token_part = engine.decode(token_part).unwrap();
let token_part: TokenPayload = serde_json::from_slice(&token_part).unwrap();
info!("token_part: {:?}", token_part);

// Get the current time in seconds
let now = js_sys::Math::floor(js_sys::Date::now() / 1000.0) as u64;

if token_part.exp < now {
// Attempt to refresh the token
let token = refresh_token(event).await?;

if token.is_none() {
return Ok(None);
}
}
}

// Ensure that the token is not the unauthorized token
if let Some(token) = &token {
if token == constants::UNAUTHORIZED_TOKEN {
return Ok(None);
}
}

Ok(token)
}

Expand Down

0 comments on commit 1f87496

Please sign in to comment.