use crate::{config, jwt, key, schema, UIDCError}; use microrm::prelude::*; #[derive(Debug)] pub enum TokenError { DBError(microrm::Error), InternalError(&'static str), RequestError(&'static str), } impl From for TokenError { fn from(value: microrm::Error) -> Self { Self::DBError(value) } } impl From for TokenError { fn from(_value: ring::error::KeyRejected) -> Self { Self::InternalError("could not load realm key") } } pub fn generate_access_token<'a>( config: &config::Config, realm: &schema::Realm, client: &schema::Client, user: &schema::User, scopes: impl Iterator, ) -> Result { let issuer = format!("{}/{}", config.base_url, realm.shortname,); let iat = std::time::SystemTime::now(); let exp = iat + std::time::Duration::from_secs(config.auth_token_expiry); // find all roles the user can possibly have access to let mut user_roles = user.groups.join(schema::Group::Roles).get()?; // find all roles requested by the scopes let mut requested_roles = vec![]; for scope_name in scopes { if let Some(scope) = realm .scopes .with(schema::Scope::Shortname, scope_name) .first() .get()? { requested_roles.extend(scope.roles.get()?.into_iter()); } } user_roles.sort_by_key(|k| k.id()); user_roles.dedup(); requested_roles.sort_by_key(|k| k.id()); requested_roles.dedup(); // find the intersection between requested roles and the ones the user actually has let resulting_roles = requested_roles .into_iter() .filter(|req| user_roles.contains(req)) .map(|role| Ok(serde_json::Value::String(role.wrapped().shortname))); let token = jwt::JWTData { sub: user.username.as_str(), iss: issuer.as_str(), aud: client.shortname.as_str(), iat: iat.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(), exp: exp.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(), extras: [( "roles", serde_json::Value::Array(resulting_roles.collect::, UIDCError>>()?), )] .into(), }; let mut realmkeys = key::RealmKeys::new(realm.clone()); let key = realmkeys .by_type(*client.key_type.as_ref())? .ok_or(UIDCError::Abort("no matching signing key for realm"))?; Ok(jwt::JWT::sign(&key, token).into_string()) } pub fn generate_refresh_token<'a>( config: &config::Config, realm: &schema::Realm, client: &schema::Client, user: &schema::User, scopes: impl Iterator, ) -> Result { todo!() }