1#![forbid(unsafe_code)]
2
3use jwt::Jwt;
4use keyset::KeyStore;
5use std::sync::Arc;
6use thiserror::Error as ThisError;
7use tokio::sync::RwLock;
8use tracing::error;
9
10pub mod error;
11pub mod jwt;
12pub mod keyset;
13
14#[cfg(feature = "actix-web")]
15pub mod actix_web;
16
17#[cfg(feature = "axum")]
18pub mod axum;
19
20#[derive(ThisError, Debug)]
21pub enum Error {
22 #[error("jwks_client: {0}")]
23 Jwks(error::Error),
24
25 #[error("{0}")]
26 Unknown(String),
27}
28
29impl From<error::Error> for Error {
30 fn from(e: error::Error) -> Self {
31 Error::Jwks(e)
32 }
33}
34
35#[derive(Clone)]
41pub struct JwksClient {
42 inner: Arc<RwLock<KeyStore>>,
43 insecure: bool,
44}
45
46impl JwksClient {
47 pub async fn new<U: Into<String>>(url: U) -> Result<Self, Error> {
48 Self::build(Some(url)).await
49 }
50
51 pub async fn insecure() -> Result<Self, Error> {
52 Self::build(None::<String>).await
53 }
54
55 pub async fn build<U: Into<String>>(url: Option<U>) -> Result<Self, Error> {
56 match url {
57 Some(url) => {
58 let store = KeyStore::new_from(url.into()).await?;
59
60 Ok(Self {
61 inner: Arc::new(RwLock::new(store)),
62 insecure: false,
63 })
64 }
65 _ => {
66 let store = KeyStore::new();
67
68 Ok(Self {
69 inner: Arc::new(RwLock::new(store)),
70 insecure: true,
71 })
72 }
73 }
74 }
75
76 pub async fn verify(&self, token: &str) -> Result<Jwt, error::Error> {
77 let read = self.inner.read().await;
78
79 if self.insecure {
80 return read.decode(token);
81 }
82
83 if read.should_refresh().unwrap_or(false) {
84 drop(read);
85
86 let mut guard = self.inner.write().await;
87 guard.load_keys().await?;
88
89 drop(guard);
90 }
91
92 let read = self.inner.read().await;
93
94 read.verify(token)
95 }
96}