Skip to content

Commit

Permalink
Allow client to be shared across threads (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
logan-stytch authored Jun 18, 2024
1 parent ab6546a commit 7feca69
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "stytch"
version = "3.0.0"
version = "3.0.1"
edition = "2021"
license = "MIT"
description = "Stytch Rust client"
Expand Down
37 changes: 25 additions & 12 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use base64::{engine::general_purpose, Engine as _};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::sync::{Arc, Mutex};

const LIVE_URL: &str = "https://api.stytch.com/";
const TEST_URL: &str = "https://test.stytch.com/";
Expand All @@ -13,7 +14,7 @@ pub struct Client {
// This would be very natural as a OnceCell, but get_or_try_init is unstable
// and would require marking this library as only usable with nightly rust.
// When that feature is stabilized, we should switch to using OnceCell.
jwks: std::cell::RefCell<Option<Jwks>>,
jwks: Arc<Mutex<Option<Jwks>>>,
}

impl std::fmt::Debug for Client {
Expand Down Expand Up @@ -82,22 +83,34 @@ impl Client {
project_id: project_id.to_string(),
base_url,
jwks_url,
jwks: std::cell::RefCell::new(None),
jwks: Arc::new(Mutex::new(None)),
})
}

async fn fetch_jwks(&self) -> crate::Result<Jwks> {
if self.jwks.borrow().is_none() {
self.jwks.replace(
self.send(crate::Request {
method: http::Method::GET,
path: self.jwks_url.clone(),
body: (),
})
.await?,
);
// First see if we already have the jwks cached
{
let guard = self.jwks.lock().map_err(|_| crate::Error::FetchJwks)?;
if let Some(jwks) = &*guard {
return Ok(jwks.clone());
}
}
Ok(self.jwks.borrow().clone().unwrap())

let fetched_jwks: Jwks = self
.send(crate::Request {
method: http::Method::GET,
path: self.jwks_url.clone(),
body: (),
})
.await
.map_err(|_| crate::Error::FetchJwks)?;

{
let mut guard = self.jwks.lock().map_err(|_| crate::Error::FetchJwks)?;
*guard = Some(fetched_jwks.clone());
}

Ok(fetched_jwks)
}

pub async fn fetch_jwk(&self, kid: &str) -> crate::Result<Jwk> {
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub enum Error {
#[error(transparent)]
InvalidUrl(#[from] url::ParseError),

#[error("Failed to fetch JWKS")]
FetchJwks,

#[error("{0:?}")]
JwkNotFound(String),

Expand Down

0 comments on commit 7feca69

Please sign in to comment.