Bläddra i källkod

rustfmt pass.

Kestrel 11 månader sedan
förälder
incheckning
8001c032e0
8 ändrade filer med 283 tillägg och 139 borttagningar
  1. 16 7
      src/cli.rs
  2. 4 1
      src/client_management.rs
  3. 29 21
      src/key.rs
  4. 69 39
      src/realm.rs
  5. 1 1
      src/server.rs
  6. 2 2
      src/server/oidc.rs
  7. 26 12
      src/server/oidc/authorize.rs
  8. 136 56
      src/server/oidc/token.rs

+ 16 - 7
src/cli.rs

@@ -253,8 +253,20 @@ impl TokenCommand {
         let config = config::Config::build_from(&args.db, None);
 
         let get_stored = |client_name: &str, user_name: &str| {
-            let stored_client = args.realm.clients.with(schema::Client::Shortname, client_name).first().get()?.ok_or(UIDCError::Abort("no such client"))?;
-            let stored_user = args.realm.users.with(schema::User::Username, user_name).first().get()?.ok_or(UIDCError::Abort("no such user"))?;
+            let stored_client = args
+                .realm
+                .clients
+                .with(schema::Client::Shortname, client_name)
+                .first()
+                .get()?
+                .ok_or(UIDCError::Abort("no such client"))?;
+            let stored_user = args
+                .realm
+                .users
+                .with(schema::User::Username, user_name)
+                .first()
+                .get()?
+                .ok_or(UIDCError::Abort("no such user"))?;
             Result::<_, UIDCError>::Ok((stored_client, stored_user))
         };
 
@@ -266,11 +278,8 @@ impl TokenCommand {
             } => {
                 let (stored_client, stored_user) = get_stored(client.as_str(), username.as_str())?;
                 let realm = RealmHelper::new(config, args.realm);
-                let token = realm.generate_access_token(
-                    &stored_client,
-                    &stored_user,
-                    scopes.split(" "),
-                )?;
+                let token =
+                    realm.generate_access_token(&stored_client, &stored_user, scopes.split(" "))?;
                 println!("{}", token);
                 Ok(())
             }

+ 4 - 1
src/client_management.rs

@@ -1,4 +1,7 @@
-use crate::{key::{HMacType, KeyType}, schema, UIDCError};
+use crate::{
+    key::{HMacType, KeyType},
+    schema, UIDCError,
+};
 use microrm::prelude::*;
 
 pub fn create(

+ 29 - 21
src/key.rs

@@ -79,7 +79,8 @@ impl schema::Key {
 
         match self.key_type.as_ref() {
             KeyType::RSA2048 | KeyType::RSA4096 => {
-                let rpubdata : RsaPublicData = bincode::deserialize(self.public_data.as_slice()).unwrap();
+                let rpubdata: RsaPublicData =
+                    bincode::deserialize(self.public_data.as_slice()).unwrap();
 
                 jwk::Jwk {
                     common: jwk::CommonParameters {
@@ -92,25 +93,23 @@ impl schema::Key {
                         key_type: jwk::RSAKeyType::RSA,
                         n: base64::encode(rpubdata.modulus),
                         e: base64::encode(rpubdata.exponent),
-                    })
-                }
-            },
-            KeyType::Ed25519 => {
-                jwk::Jwk {
-                    common: jwk::CommonParameters {
-                        public_key_use: Some(jwk::PublicKeyUse::Signature),
-                        key_algorithm: Some(jwk::KeyAlgorithm::EdDSA),
-                        key_id: Some(self.key_id),
-                        ..Default::default()
-                    },
-                    algorithm: jwk::AlgorithmParameters::OctetKeyPair(jwk::OctetKeyPairParameters {
-                        key_type: jwk::OctetKeyPairType::OctetKeyPair,
-                        curve: jwk::EllipticCurve::Ed25519,
-                        x: base64::encode(self.public_data),
-                    })
+                    }),
                 }
+            }
+            KeyType::Ed25519 => jwk::Jwk {
+                common: jwk::CommonParameters {
+                    public_key_use: Some(jwk::PublicKeyUse::Signature),
+                    key_algorithm: Some(jwk::KeyAlgorithm::EdDSA),
+                    key_id: Some(self.key_id),
+                    ..Default::default()
+                },
+                algorithm: jwk::AlgorithmParameters::OctetKeyPair(jwk::OctetKeyPairParameters {
+                    key_type: jwk::OctetKeyPairType::OctetKeyPair,
+                    curve: jwk::EllipticCurve::Ed25519,
+                    x: base64::encode(self.public_data),
+                }),
             },
-            _ => todo!()
+            _ => todo!(),
         }
     }
 }
@@ -146,9 +145,18 @@ fn generate_rsa(realm: &schema::Realm, kty: KeyType, bits: usize) -> Result<Stri
     let expiry = time::OffsetDateTime::now_utc() + time::Duration::days(730);
 
     let public_data = bincode::serialize(&RsaPublicData {
-            modulus: keypair.public_key().modulus().big_endian_without_leading_zero().into(),
-            exponent: keypair.public_key().exponent().big_endian_without_leading_zero().into(),
-    }).unwrap();
+        modulus: keypair
+            .public_key()
+            .modulus()
+            .big_endian_without_leading_zero()
+            .into(),
+        exponent: keypair
+            .public_key()
+            .exponent()
+            .big_endian_without_leading_zero()
+            .into(),
+    })
+    .unwrap();
 
     realm.keys.insert(schema::Key {
         key_id: key_id.clone(),

+ 69 - 39
src/realm.rs

@@ -51,12 +51,15 @@ impl RealmCache {
 
     pub fn get_helper(&self, id: schema::RealmID) -> Option<Arc<RealmHelper>> {
         if let Some(rh) = self.realms.read().unwrap().get(&id) {
-            return Some(rh.clone())
+            return Some(rh.clone());
         }
 
         let realm = self.db.realms.by_id(id).ok().flatten()?;
 
-        self.realms.write().unwrap().insert(id, RealmHelper::new(self.config.clone(), realm).into());
+        self.realms
+            .write()
+            .unwrap()
+            .insert(id, RealmHelper::new(self.config.clone(), realm).into());
         self.get_helper(id)
     }
 }
@@ -130,48 +133,58 @@ impl RealmHelper {
         Ok(roles)
     }
 
-    fn with_encoding_key<R>(&self, key: &microrm::Stored<schema::Key>, f: impl FnOnce(&jsonwebtoken::EncodingKey) -> R) -> R {
+    fn with_encoding_key<R>(
+        &self,
+        key: &microrm::Stored<schema::Key>,
+        f: impl FnOnce(&jsonwebtoken::EncodingKey) -> R,
+    ) -> R {
         // check to see if the cache will work
         if let Some(v) = self.encoding_key_cache.read().unwrap().get(&key.id()) {
-            return f(v)
+            return f(v);
         }
 
         // parse an EncodingKey out of the stored key
         let ekey = match key.key_type.as_ref() {
-            KeyType::HMac(_) => {
-                jsonwebtoken::EncodingKey::from_secret(&key.secret_data)
-            },
+            KeyType::HMac(_) => jsonwebtoken::EncodingKey::from_secret(&key.secret_data),
             KeyType::RSA2048 | KeyType::RSA4096 => {
                 jsonwebtoken::EncodingKey::from_rsa_der(&key.secret_data)
-            },
-            KeyType::Ed25519 => {
-                jsonwebtoken::EncodingKey::from_ed_der(&key.secret_data)
-            },
+            }
+            KeyType::Ed25519 => jsonwebtoken::EncodingKey::from_ed_der(&key.secret_data),
         };
 
-        f(self.encoding_key_cache.write().unwrap().entry(key.id()).or_insert(ekey))
+        f(self
+            .encoding_key_cache
+            .write()
+            .unwrap()
+            .entry(key.id())
+            .or_insert(ekey))
     }
 
-    fn with_decoding_key<R>(&self, key: &microrm::Stored<schema::Key>, f: impl FnOnce(&jsonwebtoken::DecodingKey) -> R) -> R {
+    fn with_decoding_key<R>(
+        &self,
+        key: &microrm::Stored<schema::Key>,
+        f: impl FnOnce(&jsonwebtoken::DecodingKey) -> R,
+    ) -> R {
         // check to see if the cache will work
         if let Some(v) = self.decoding_key_cache.read().unwrap().get(&key.id()) {
-            return f(v)
+            return f(v);
         }
 
         // parse an EncodingKey out of the stored key
         let ekey = match key.key_type.as_ref() {
-            KeyType::HMac(_) => {
-                jsonwebtoken::DecodingKey::from_secret(&key.secret_data)
-            },
+            KeyType::HMac(_) => jsonwebtoken::DecodingKey::from_secret(&key.secret_data),
             KeyType::RSA2048 | KeyType::RSA4096 => {
                 jsonwebtoken::DecodingKey::from_rsa_der(&key.secret_data)
-            },
-            KeyType::Ed25519 => {
-                jsonwebtoken::DecodingKey::from_ed_der(&key.secret_data)
-            },
+            }
+            KeyType::Ed25519 => jsonwebtoken::DecodingKey::from_ed_der(&key.secret_data),
         };
 
-        f(self.decoding_key_cache.write().unwrap().entry(key.id()).or_insert(ekey))
+        f(self
+            .decoding_key_cache
+            .write()
+            .unwrap()
+            .entry(key.id())
+            .or_insert(ekey))
     }
 
     pub fn generate_access_token<'a>(
@@ -213,13 +226,18 @@ impl RealmHelper {
         let mut hdr = jsonwebtoken::Header::new(match ekey.key_type.as_ref() {
             KeyType::Ed25519 => jsonwebtoken::Algorithm::EdDSA,
             KeyType::RSA2048 | KeyType::RSA4096 => jsonwebtoken::Algorithm::RS256,
-            KeyType::HMac(HMacType::Sha256) => Err(UIDCError::Abort("cannot sign access token with HS256"))?,
-            KeyType::HMac(HMacType::Sha512) => Err(UIDCError::Abort("cannot sign access token with HS512"))?,
+            KeyType::HMac(HMacType::Sha256) => {
+                Err(UIDCError::Abort("cannot sign access token with HS256"))?
+            }
+            KeyType::HMac(HMacType::Sha512) => {
+                Err(UIDCError::Abort("cannot sign access token with HS512"))?
+            }
         });
         hdr.kid = Some(ekey.key_id.clone());
 
         self.with_encoding_key(&ekey, |ekey| {
-            jsonwebtoken::encode(&hdr, &atclaims, ekey).map_err(|e| UIDCError::AbortString(format!("failed to sign token: {e}")))
+            jsonwebtoken::encode(&hdr, &atclaims, ekey)
+                .map_err(|e| UIDCError::AbortString(format!("failed to sign token: {e}")))
         })
     }
 
@@ -260,12 +278,8 @@ impl RealmHelper {
         let mut hdr = jsonwebtoken::Header::new(match ekey.key_type.as_ref() {
             KeyType::Ed25519 => jsonwebtoken::Algorithm::EdDSA,
             KeyType::RSA2048 | KeyType::RSA4096 => jsonwebtoken::Algorithm::RS256,
-            KeyType::HMac(HMacType::Sha256) => {
-                jsonwebtoken::Algorithm::HS256
-            }
-            KeyType::HMac(HMacType::Sha512) => {
-                jsonwebtoken::Algorithm::HS512
-            }
+            KeyType::HMac(HMacType::Sha256) => jsonwebtoken::Algorithm::HS256,
+            KeyType::HMac(HMacType::Sha512) => jsonwebtoken::Algorithm::HS512,
         });
 
         hdr.kid = Some(ekey.key_id.clone());
@@ -276,17 +290,28 @@ impl RealmHelper {
         })
     }
 
-    pub fn trade_refresh_token<'l>(&self, client: &microrm::Stored<schema::Client>, rtoken: &'l str) -> Result<(String, String), UIDCError> {
-        let header = jsonwebtoken::decode_header(rtoken).map_err(|e| UIDCError::AbortString(format!("invalid JWT header: {e}")))?;
+    pub fn trade_refresh_token<'l>(
+        &self,
+        client: &microrm::Stored<schema::Client>,
+        rtoken: &'l str,
+    ) -> Result<(String, String), UIDCError> {
+        let header = jsonwebtoken::decode_header(rtoken)
+            .map_err(|e| UIDCError::AbortString(format!("invalid JWT header: {e}")))?;
         let Some(kid) = header.kid else {
             return Err(UIDCError::Abort("no kid in header"));
         };
 
-        let Some(key) = self.realm.keys.with(schema::Key::KeyId, kid).first().get()? else {
-            return Err(UIDCError::Abort("no matching key"))
+        let Some(key) = self
+            .realm
+            .keys
+            .with(schema::Key::KeyId, kid)
+            .first()
+            .get()?
+        else {
+            return Err(UIDCError::Abort("no matching key"));
         };
         if *key.key_state.as_ref() == schema::KeyState::Retired {
-            return Err(UIDCError::Abort("signing key retired"))
+            return Err(UIDCError::Abort("signing key retired"));
         }
 
         let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256);
@@ -300,11 +325,16 @@ impl RealmHelper {
         };
 
         if rt.claims.use_ != "refresh" {
-            return Err(UIDCError::Abort("mismatching token use"))
+            return Err(UIDCError::Abort("mismatching token use"));
         }
 
-        let Some(user) = self.realm.users.keyed((self.realm.id(), rt.claims.sub.as_str())).get()? else {
-            return Err(UIDCError::Abort("user no longer exists or was renamed"))
+        let Some(user) = self
+            .realm
+            .users
+            .keyed((self.realm.id(), rt.claims.sub.as_str()))
+            .get()?
+        else {
+            return Err(UIDCError::Abort("user no longer exists or was renamed"));
         };
 
         let scopes = rt.claims.scopes.iter().map(String::as_str);

+ 1 - 1
src/server.rs

@@ -1,4 +1,4 @@
-use crate::{config, schema, UIDCError, realm};
+use crate::{config, realm, schema, UIDCError};
 
 mod oidc;
 mod session;

+ 2 - 2
src/server/oidc.rs

@@ -113,12 +113,12 @@ async fn jwks(request: Request) -> tide::Result<tide::Response> {
 
     for key in realm.keys.get()?.into_iter() {
         if *key.key_state.as_ref() == schema::KeyState::Retired {
-            continue
+            continue;
         }
 
         // skip HMAC keys
         if let key::KeyType::HMac(_) = *key.key_type.as_ref() {
-            continue
+            continue;
         }
 
         jwkset.keys.push(key.wrapped().into_jwk());

+ 26 - 12
src/server/oidc/authorize.rs

@@ -1,6 +1,6 @@
+use super::{api, OIDCError, OIDCErrorType, Request};
+use crate::{config, schema, server::session::SessionHelper};
 use microrm::prelude::*;
-use crate::{schema,config,server::session::SessionHelper};
-use super::{api,OIDCError,OIDCErrorType,Request};
 
 fn do_code_authorize<'l, 's>(
     config: &config::Config,
@@ -58,7 +58,10 @@ fn do_code_authorize<'l, 's>(
     .into())
 }
 
-pub(super) fn do_authorize(request: Request, state: Option<&str>) -> Result<tide::Response, OIDCError> {
+pub(super) fn do_authorize(
+    request: Request,
+    state: Option<&str>,
+) -> Result<tide::Response, OIDCError> {
     let shelper = SessionHelper::new(&request);
     let realm = shelper.get_realm().map_err(|_| {
         OIDCError(
@@ -144,17 +147,28 @@ pub(super) fn do_authorize(request: Request, state: Option<&str>) -> Result<tide
     } else if qp.response_type == "token" {
         let rhelper = request.state().core.realms.get_helper(realm.id()).unwrap();
 
-        let token = rhelper.generate_access_token(&client, &user, scopes).map_err(|e| OIDCError(OIDCErrorType::ServerError, format!("could not generate token: {e}").into(), state))?;
+        let token = rhelper
+            .generate_access_token(&client, &user, scopes)
+            .map_err(|e| {
+                OIDCError(
+                    OIDCErrorType::ServerError,
+                    format!("could not generate token: {e}").into(),
+                    state,
+                )
+            })?;
 
         Ok(tide::Response::builder(200)
-           .content_type(tide::http::mime::JSON)
-           .body(serde_json::to_vec(&api::TokenResponse {
-                token_type: "bearer",
-                access_token: token.as_str(),
-                refresh_token: None,
-                scope: None,
-           }).unwrap())
-           .build())
+            .content_type(tide::http::mime::JSON)
+            .body(
+                serde_json::to_vec(&api::TokenResponse {
+                    token_type: "bearer",
+                    access_token: token.as_str(),
+                    refresh_token: None,
+                    scope: None,
+                })
+                .unwrap(),
+            )
+            .build())
     } else {
         Err(OIDCError(
             OIDCErrorType::UnsupportedResponseType,

+ 136 - 56
src/server/oidc/token.rs

@@ -1,10 +1,25 @@
+use super::{api, OIDCError, OIDCErrorType, Request};
+use crate::{
+    realm::RealmHelper,
+    schema,
+    server::session::SessionHelper,
+    user::{UserError, UserExt},
+    UIDCError,
+};
 use microrm::prelude::*;
-use crate::{realm::RealmHelper, schema, server::session::SessionHelper, user::{UserError, UserExt}, UIDCError};
-use super::{api,OIDCError,OIDCErrorType,Request};
 
-fn do_authorization_code<'l>(realm: &microrm::Stored<schema::Realm>, rhelper: &RealmHelper, client: &microrm::Stored<schema::Client>, treq: &api::TokenRequestBody) -> Result<tide::Response, OIDCError<'l>> {
+fn do_authorization_code<'l>(
+    realm: &microrm::Stored<schema::Realm>,
+    rhelper: &RealmHelper,
+    client: &microrm::Stored<schema::Client>,
+    treq: &api::TokenRequestBody,
+) -> Result<tide::Response, OIDCError<'l>> {
     let Some(code) = treq.code.as_ref() else {
-        return Err(OIDCError(OIDCErrorType::InvalidRequest, "no authorization code provided".into(), None))
+        return Err(OIDCError(
+            OIDCErrorType::InvalidRequest,
+            "no authorization code provided".into(),
+            None,
+        ));
     };
     let code = realm
         .auth_codes
@@ -36,18 +51,19 @@ fn do_authorization_code<'l>(realm: &microrm::Stored<schema::Realm>, rhelper: &R
             None,
         ))?;
 
-    let access_token = rhelper.generate_access_token(
-        &client,
-        &user,
-        code.scopes.as_ref().iter().map(String::as_str),
-    )
-    .map_err(|e| {
-        OIDCError(
-            OIDCErrorType::ServerError,
-            format!("error signing key: {e}").into(),
-            None,
+    let access_token = rhelper
+        .generate_access_token(
+            &client,
+            &user,
+            code.scopes.as_ref().iter().map(String::as_str),
         )
-    })?;
+        .map_err(|e| {
+            OIDCError(
+                OIDCErrorType::ServerError,
+                format!("error signing key: {e}").into(),
+                None,
+            )
+        })?;
 
     Ok(tide::Response::builder(200)
         .content_type(tide::http::mime::JSON)
@@ -63,7 +79,12 @@ fn do_authorization_code<'l>(realm: &microrm::Stored<schema::Realm>, rhelper: &R
         .build())
 }
 
-fn do_refresh_token<'l>(_realm: &microrm::Stored<schema::Realm>, rhelper: &RealmHelper, client: &microrm::Stored<schema::Client>, treq: &api::TokenRequestBody) -> Result<tide::Response, OIDCError<'l>> {
+fn do_refresh_token<'l>(
+    _realm: &microrm::Stored<schema::Realm>,
+    rhelper: &RealmHelper,
+    client: &microrm::Stored<schema::Client>,
+    treq: &api::TokenRequestBody,
+) -> Result<tide::Response, OIDCError<'l>> {
     let Some(rtoken) = treq.refresh_token.as_ref() else {
         return Err(OIDCError(
             OIDCErrorType::InvalidRequest,
@@ -74,11 +95,13 @@ fn do_refresh_token<'l>(_realm: &microrm::Stored<schema::Realm>, rhelper: &Realm
 
     let (access, refresh) = match rhelper.trade_refresh_token(&client, rtoken.as_str()) {
         Ok((a, r)) => (a, r),
-        Err(e) => return Err(OIDCError(
-            OIDCErrorType::InvalidRequest,
-            format!("could not trade refresh token: {e}").into(),
-            None,
-        ))
+        Err(e) => {
+            return Err(OIDCError(
+                OIDCErrorType::InvalidRequest,
+                format!("could not trade refresh token: {e}").into(),
+                None,
+            ))
+        }
     };
     Ok(tide::Response::builder(200)
         .content_type(tide::http::mime::JSON)
@@ -94,43 +117,89 @@ fn do_refresh_token<'l>(_realm: &microrm::Stored<schema::Realm>, rhelper: &Realm
         .build())
 }
 
-fn do_direct_grant<'l>(realm: &microrm::Stored<schema::Realm>, rhelper: &RealmHelper, client: &microrm::Stored<schema::Client>, treq: &api::TokenRequestBody) -> Result<tide::Response, OIDCError<'l>> {
+fn do_direct_grant<'l>(
+    realm: &microrm::Stored<schema::Realm>,
+    rhelper: &RealmHelper,
+    client: &microrm::Stored<schema::Client>,
+    treq: &api::TokenRequestBody,
+) -> Result<tide::Response, OIDCError<'l>> {
     // first thing to check: does the client have the direct grant type enabled?
     if !client.direct_grant_enabled {
-        return Err(OIDCError(OIDCErrorType::UnsupportedResponseType, "client does not have direct grants enabled".into(), None))
+        return Err(OIDCError(
+            OIDCErrorType::UnsupportedResponseType,
+            "client does not have direct grants enabled".into(),
+            None,
+        ));
     }
 
     let Some(username) = treq.username.as_ref() else {
-        return Err(OIDCError(OIDCErrorType::InvalidRequest, "no username provided".into(), None))
+        return Err(OIDCError(
+            OIDCErrorType::InvalidRequest,
+            "no username provided".into(),
+            None,
+        ));
     };
     let Some(password) = treq.password.as_ref() else {
-        return Err(OIDCError(OIDCErrorType::InvalidRequest, "no password provided".into(), None))
+        return Err(OIDCError(
+            OIDCErrorType::InvalidRequest,
+            "no password provided".into(),
+            None,
+        ));
     };
 
-    let Some(user) = realm.users.with(schema::User::Username, username).first().get()? else {
-        return Err(OIDCError(OIDCErrorType::AccessDenied, "no such user".into(), None))
+    let Some(user) = realm
+        .users
+        .with(schema::User::Username, username)
+        .first()
+        .get()?
+    else {
+        return Err(OIDCError(
+            OIDCErrorType::AccessDenied,
+            "no such user".into(),
+            None,
+        ));
     };
 
     // verify that we don't accidentally log in someone with username/password who has MFA..
-    match user.auth.with(schema::AuthChallenge::Enabled, true).count()? {
-        0 => return Err(OIDCError(OIDCErrorType::AccessDenied, "user has no associated password".into(), None)),
+    match user
+        .auth
+        .with(schema::AuthChallenge::Enabled, true)
+        .count()?
+    {
+        0 => {
+            return Err(OIDCError(
+                OIDCErrorType::AccessDenied,
+                "user has no associated password".into(),
+                None,
+            ))
+        }
         1 => (),
-        _ => return Err(OIDCError(OIDCErrorType::AccessDenied, "user has MFA enabled".into(), None)),
+        _ => {
+            return Err(OIDCError(
+                OIDCErrorType::AccessDenied,
+                "user has MFA enabled".into(),
+                None,
+            ))
+        }
     }
 
     match user.verify_challenge_by_type(schema::AuthChallengeType::Password, password.as_bytes()) {
-        Err(UIDCError::UserError(UserError::NoSuchChallenge)) => {
-            Err(OIDCError(OIDCErrorType::AccessDenied, "user has no associated password".into(), None))
-        },
-        Err(UIDCError::DatabaseError(e)) => {
-            Err(e.into())
-        },
-        Err(e) => {
-            Err(OIDCError(OIDCErrorType::ServerError, format!("internal error: {e}").into(), None))
-        },
-        Ok(false) => {
-            Err(OIDCError(OIDCErrorType::AccessDenied, "password authentication failed".into(), None))
-        },
+        Err(UIDCError::UserError(UserError::NoSuchChallenge)) => Err(OIDCError(
+            OIDCErrorType::AccessDenied,
+            "user has no associated password".into(),
+            None,
+        )),
+        Err(UIDCError::DatabaseError(e)) => Err(e.into()),
+        Err(e) => Err(OIDCError(
+            OIDCErrorType::ServerError,
+            format!("internal error: {e}").into(),
+            None,
+        )),
+        Ok(false) => Err(OIDCError(
+            OIDCErrorType::AccessDenied,
+            "password authentication failed".into(),
+            None,
+        )),
         Ok(true) => {
             let scopes = treq
                 .scope
@@ -138,23 +207,35 @@ fn do_direct_grant<'l>(realm: &microrm::Stored<schema::Realm>, rhelper: &RealmHe
                 .map(String::as_str)
                 .unwrap_or("")
                 .split_whitespace();
-            let Ok(access_token) = rhelper.generate_access_token(client, &user, scopes.clone()) else {
-                return Err(OIDCError(OIDCErrorType::ServerError, "could not generate access token".into(), None))
+            let Ok(access_token) = rhelper.generate_access_token(client, &user, scopes.clone())
+            else {
+                return Err(OIDCError(
+                    OIDCErrorType::ServerError,
+                    "could not generate access token".into(),
+                    None,
+                ));
             };
             let Ok(refresh_token) = rhelper.generate_refresh_token(client, &user, scopes) else {
-                return Err(OIDCError(OIDCErrorType::ServerError, "could not generate access token".into(), None))
+                return Err(OIDCError(
+                    OIDCErrorType::ServerError,
+                    "could not generate access token".into(),
+                    None,
+                ));
             };
 
             Ok(tide::Response::builder(200)
-               .content_type(tide::http::mime::JSON)
-               .body(serde_json::to_vec(&api::TokenResponse {
-                    access_token: access_token.as_str(),
-                    token_type: "bearer",
-                    refresh_token: Some(refresh_token.as_str()),
-                    scope: None,
-               }).unwrap())
-               .build())
-        },
+                .content_type(tide::http::mime::JSON)
+                .body(
+                    serde_json::to_vec(&api::TokenResponse {
+                        access_token: access_token.as_str(),
+                        token_type: "bearer",
+                        refresh_token: Some(refresh_token.as_str()),
+                        scope: None,
+                    })
+                    .unwrap(),
+                )
+                .build())
+        }
     }
 }
 
@@ -199,9 +280,8 @@ pub(super) async fn do_token<'l>(mut request: Request) -> Result<tide::Response,
     } else {
         Err(OIDCError(
             OIDCErrorType::InvalidRequest,
-            format!("unknown grant type {}", treq.grant_type).into(),
+            format!("unknown grant_type {}", treq.grant_type).into(),
             None,
         ))
     }
 }
-