pub mod json_file;
use std::fmt;
use std::future::Future;
use serde::{Deserialize, Serialize};
use webauthn_rs::prelude::{AuthenticationResult, Passkey};
#[derive(Debug, Serialize, Deserialize, Clone)]
#[non_exhaustive]
pub struct AuthCode {
pub client_id: String,
pub redirect_uri: String,
pub code_challenge: String,
pub created_at: u64,
}
impl AuthCode {
#[must_use]
pub const fn new(
client_id: String,
redirect_uri: String,
code_challenge: String,
created_at: u64,
) -> Self {
Self {
client_id,
redirect_uri,
code_challenge,
created_at,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[non_exhaustive]
pub struct AccessTokenEntry {
pub client_id: String,
pub created_at: u64,
pub expires_in_secs: u64,
pub refresh_token: String,
}
impl AccessTokenEntry {
#[must_use]
pub const fn new(
client_id: String,
created_at: u64,
expires_in_secs: u64,
refresh_token: String,
) -> Self {
Self {
client_id,
created_at,
expires_in_secs,
refresh_token,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[non_exhaustive]
pub struct RefreshTokenEntry {
pub client_id: String,
}
impl RefreshTokenEntry {
#[must_use]
pub const fn new(client_id: String) -> Self {
Self { client_id }
}
}
#[derive(Serialize, Deserialize, Clone)]
#[non_exhaustive]
pub struct RegisteredClient {
pub client_secret: String,
pub redirect_uris: Vec<String>,
}
impl RegisteredClient {
#[must_use]
pub const fn new(client_secret: String, redirect_uris: Vec<String>) -> Self {
Self {
client_secret,
redirect_uris,
}
}
}
impl fmt::Debug for RegisteredClient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RegisteredClient")
.field("client_secret", &"[REDACTED]")
.field("redirect_uris", &self.redirect_uris)
.finish()
}
}
pub const TRANSIENT_STATE_TTL_SECS: u64 = 300;
#[derive(Debug)]
pub enum StoreError {
CapacityExceeded,
Backend(Box<dyn std::error::Error + Send + Sync>),
}
impl fmt::Display for StoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CapacityExceeded => write!(f, "store capacity exceeded"),
Self::Backend(e) => write!(f, "store backend error: {e}"),
}
}
}
impl std::error::Error for StoreError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Backend(e) => Some(&**e),
Self::CapacityExceeded => None,
}
}
}
pub trait TokenStore: Send + Sync + 'static {
fn store_auth_code(
&self,
code: String,
entry: AuthCode,
) -> impl Future<Output = Result<(), StoreError>> + Send;
fn consume_auth_code(
&self,
code: &str,
) -> impl Future<Output = Result<Option<AuthCode>, StoreError>> + Send;
fn store_access_token(
&self,
token: String,
entry: AccessTokenEntry,
) -> impl Future<Output = Result<(), StoreError>> + Send;
fn get_access_token(
&self,
token: &str,
) -> impl Future<Output = Result<Option<AccessTokenEntry>, StoreError>> + Send;
fn revoke_access_tokens_by_refresh(
&self,
refresh_token: &str,
) -> impl Future<Output = Result<(), StoreError>> + Send;
fn store_refresh_token(
&self,
token: String,
entry: RefreshTokenEntry,
) -> impl Future<Output = Result<(), StoreError>> + Send;
fn get_refresh_token(
&self,
token: &str,
) -> impl Future<Output = Result<Option<RefreshTokenEntry>, StoreError>> + Send;
fn consume_refresh_token(
&self,
token: &str,
) -> impl Future<Output = Result<Option<RefreshTokenEntry>, StoreError>> + Send;
fn cleanup_expired_tokens(
&self,
now: u64,
) -> impl Future<Output = Result<(), StoreError>> + Send;
}
pub trait ClientStore: Send + Sync + 'static {
fn register_client(
&self,
id: String,
client: RegisteredClient,
) -> impl Future<Output = Result<(), StoreError>> + Send;
fn try_register_client(
&self,
id: String,
client: RegisteredClient,
) -> impl Future<Output = Result<bool, StoreError>> + Send;
fn get_client(
&self,
id: &str,
) -> impl Future<Output = Result<Option<RegisteredClient>, StoreError>> + Send;
fn client_count(&self) -> impl Future<Output = Result<usize, StoreError>> + Send;
}
pub trait PasskeyStore: Send + Sync + 'static {
fn list_passkeys(&self) -> impl Future<Output = Result<Vec<Passkey>, StoreError>> + Send;
fn add_passkey_if_none(
&self,
passkey: Passkey,
) -> impl Future<Output = Result<bool, StoreError>> + Send;
fn add_passkey(&self, passkey: Passkey) -> impl Future<Output = Result<(), StoreError>> + Send;
fn update_passkey(
&self,
auth_result: &AuthenticationResult,
) -> impl Future<Output = Result<(), StoreError>> + Send;
fn has_passkeys(&self) -> impl Future<Output = Result<bool, StoreError>> + Send;
}