瀏覽代碼

rustfmt pass and revivifying oidc API.

Kestrel 11 月之前
父節點
當前提交
69735311f5
共有 14 個文件被更改,包括 236 次插入231 次删除
  1. 5 55
      src/cli.rs
  2. 11 10
      src/cli/client.rs
  3. 10 8
      src/cli/group.rs
  4. 10 8
      src/cli/role.rs
  5. 10 8
      src/cli/scope.rs
  6. 24 12
      src/cli/user.rs
  7. 13 22
      src/client_management.rs
  8. 21 26
      src/key.rs
  9. 4 1
      src/scope_management.rs
  10. 2 2
      src/server.rs
  11. 108 74
      src/server/oidc.rs
  12. 5 1
      src/server/session.rs
  13. 6 1
      src/token.rs
  14. 7 3
      src/user.rs

+ 5 - 55
src/cli.rs

@@ -1,18 +1,18 @@
 use crate::{
-    client_management, config,
+    config,
     key::{self, KeyType},
     schema::{self, UIDCDatabase},
-    scope_management, server, token_management, UIDCError,
+    server, token_management, UIDCError,
 };
 use clap::{Parser, Subcommand};
-use microrm::{prelude::*, schema::Stored};
 use microrm::cli::Autogenerate;
+use microrm::{prelude::*, schema::Stored};
 
 mod client;
 mod group;
 mod role;
-mod user;
 mod scope;
+mod user;
 
 impl microrm::cli::CLIError for UIDCError {
     fn no_such_entity(ename: &'static str, keys: String) -> Self {
@@ -97,10 +97,7 @@ impl RootArgs {
             .get()?
             .ok_or(UIDCError::Abort("no such realm"))?;
 
-        let ra = RunArgs {
-            db,
-            realm,
-        };
+        let ra = RunArgs { db, realm };
 
         match self.command {
             Command::Init => unreachable!(),
@@ -250,53 +247,6 @@ impl ConfigArgs {
     }
 }
 
-#[derive(Debug, Subcommand)]
-enum ScopeCommand {
-    AttachRole {
-        scope_name: String,
-        role_name: String,
-    },
-    Create {
-        scope_name: String,
-    },
-    DetachRole {
-        scope_name: String,
-        role_name: String,
-    },
-    Inspect {
-        scope_name: String,
-    },
-    List,
-}
-
-#[derive(Debug, Parser)]
-struct ScopeArgs {
-    #[clap(subcommand)]
-    command: ScopeCommand,
-}
-
-impl ScopeArgs {
-    async fn run(self, args: RunArgs) -> Result<(), UIDCError> {
-        match &self.command {
-            ScopeCommand::AttachRole {
-                scope_name,
-                role_name,
-            } => scope_management::attach_role(&args.realm, scope_name, role_name),
-            ScopeCommand::Create { scope_name } => {
-                scope_management::create_scope(&args.realm, scope_name)
-            }
-            ScopeCommand::DetachRole {
-                scope_name,
-                role_name,
-            } => scope_management::detach_role(&args.realm, scope_name, role_name),
-            ScopeCommand::Inspect { scope_name } => {
-                scope_management::inspect_scope(&args.realm, scope_name)
-            }
-            ScopeCommand::List => scope_management::list_scopes(&args.realm),
-        }
-    }
-}
-
 #[derive(Debug, Parser)]
 struct ServerArgs {
     #[clap(short, long)]

+ 11 - 10
src/cli/client.rs

@@ -1,7 +1,7 @@
 use std::str::FromStr;
 
+use crate::{client_management, key, schema, UIDCError};
 use microrm::{prelude::*, schema::entity::EntityID};
-use crate::{schema, UIDCError, key, client_management};
 
 #[derive(Debug)]
 pub struct ClientInterface;
@@ -27,20 +27,23 @@ impl microrm::cli::EntityInterface for ClientInterface {
             ClientCommands::Create { name, key_type } => {
                 let kt = key::KeyType::from_str(key_type.as_str())?;
                 client_management::create(ctx, &name, kt)?;
-            },
-            ClientCommands::RotateSecret { name } =>  {
+            }
+            ClientCommands::RotateSecret { name } => {
                 client_management::rotate_secret(ctx, &name)?;
-            },
+            }
         }
 
         Ok(())
     }
 
-    fn should_override(_entity: &'static str, field: &'static str, _role: microrm::cli::ValueRole) -> bool {
+    fn should_override(
+        _entity: &'static str,
+        field: &'static str,
+        _role: microrm::cli::ValueRole,
+    ) -> bool {
         if field == "realm" {
             true
-        }
-        else {
+        } else {
             false
         }
     }
@@ -53,10 +56,8 @@ impl microrm::cli::EntityInterface for ClientInterface {
     ) -> String {
         if field == "realm" {
             format!("{}", ctx.id().into_raw())
-        }
-        else {
+        } else {
             unreachable!()
         }
     }
 }
-

+ 10 - 8
src/cli/group.rs

@@ -1,12 +1,12 @@
-use microrm::{prelude::*, schema::entity::EntityID};
 use crate::{schema, UIDCError};
+use microrm::{prelude::*, schema::entity::EntityID};
 
 #[derive(Debug)]
 pub struct GroupInterface;
 
 #[derive(Debug, clap::Subcommand)]
 pub enum GroupCommands {
-    Create { name: String }
+    Create { name: String },
 }
 
 impl microrm::cli::EntityInterface for GroupInterface {
@@ -28,17 +28,20 @@ impl microrm::cli::EntityInterface for GroupInterface {
                     users: Default::default(),
                     roles: Default::default(),
                 })?;
-            },
+            }
         }
 
         Ok(())
     }
 
-    fn should_override(_entity: &'static str, field: &'static str, _role: microrm::cli::ValueRole) -> bool {
+    fn should_override(
+        _entity: &'static str,
+        field: &'static str,
+        _role: microrm::cli::ValueRole,
+    ) -> bool {
         if field == "realm" {
             true
-        }
-        else {
+        } else {
             false
         }
     }
@@ -51,8 +54,7 @@ impl microrm::cli::EntityInterface for GroupInterface {
     ) -> String {
         if field == "realm" {
             format!("{}", ctx.id().into_raw())
-        }
-        else {
+        } else {
             unreachable!()
         }
     }

+ 10 - 8
src/cli/role.rs

@@ -1,12 +1,12 @@
-use microrm::{prelude::*, schema::entity::EntityID};
 use crate::{schema, UIDCError};
+use microrm::{prelude::*, schema::entity::EntityID};
 
 #[derive(Debug)]
 pub struct RoleInterface;
 
 #[derive(Debug, clap::Subcommand)]
 pub enum RoleCommands {
-    Create { name: String }
+    Create { name: String },
 }
 
 impl microrm::cli::EntityInterface for RoleInterface {
@@ -27,17 +27,20 @@ impl microrm::cli::EntityInterface for RoleInterface {
                     shortname: name,
                     groups: Default::default(),
                 })?;
-            },
+            }
         }
 
         Ok(())
     }
 
-    fn should_override(_entity: &'static str, field: &'static str, _role: microrm::cli::ValueRole) -> bool {
+    fn should_override(
+        _entity: &'static str,
+        field: &'static str,
+        _role: microrm::cli::ValueRole,
+    ) -> bool {
         if field == "realm" {
             true
-        }
-        else {
+        } else {
             false
         }
     }
@@ -50,8 +53,7 @@ impl microrm::cli::EntityInterface for RoleInterface {
     ) -> String {
         if field == "realm" {
             format!("{}", ctx.id().into_raw())
-        }
-        else {
+        } else {
             unreachable!()
         }
     }

+ 10 - 8
src/cli/scope.rs

@@ -1,12 +1,12 @@
-use microrm::{prelude::*, schema::entity::EntityID};
 use crate::{schema, UIDCError};
+use microrm::{prelude::*, schema::entity::EntityID};
 
 #[derive(Debug)]
 pub struct ScopeInterface;
 
 #[derive(Debug, clap::Subcommand)]
 pub enum ScopeCommands {
-    Create { name: String }
+    Create { name: String },
 }
 
 impl microrm::cli::EntityInterface for ScopeInterface {
@@ -27,17 +27,20 @@ impl microrm::cli::EntityInterface for ScopeInterface {
                     shortname: name,
                     roles: Default::default(),
                 })?;
-            },
+            }
         }
 
         Ok(())
     }
 
-    fn should_override(_entity: &'static str, field: &'static str, _role: microrm::cli::ValueRole) -> bool {
+    fn should_override(
+        _entity: &'static str,
+        field: &'static str,
+        _role: microrm::cli::ValueRole,
+    ) -> bool {
         if field == "realm" {
             true
-        }
-        else {
+        } else {
             false
         }
     }
@@ -50,8 +53,7 @@ impl microrm::cli::EntityInterface for ScopeInterface {
     ) -> String {
         if field == "realm" {
             format!("{}", ctx.id().into_raw())
-        }
-        else {
+        } else {
             unreachable!()
         }
     }

+ 24 - 12
src/cli/user.rs

@@ -1,12 +1,14 @@
-use microrm::{prelude::*, schema::entity::EntityID, cli::CLIError};
 use crate::{schema, user::UserExt, UIDCError};
+use microrm::{cli::CLIError, prelude::*, schema::entity::EntityID};
 
 #[derive(Debug)]
 pub struct UserInterface;
 
 #[derive(Debug, clap::Subcommand)]
 pub enum UserCommands {
-    Create { username: String },
+    Create {
+        username: String,
+    },
     UpdateAuth {
         username: String,
         #[clap(short = 'p', long, action = clap::ArgAction::Count)]
@@ -35,13 +37,21 @@ impl microrm::cli::EntityInterface for UserInterface {
                     auth: Default::default(),
                     groups: Default::default(),
                 })?;
-            },
-            UserCommands::UpdateAuth { username, password, totp } => {
-                let user = query_ctx.with(schema::User::Username, &username).first().get()?.ok_or(Self::Error::no_such_entity("user", username))?;
+            }
+            UserCommands::UpdateAuth {
+                username,
+                password,
+                totp,
+            } => {
+                let user = query_ctx
+                    .with(schema::User::Username, &username)
+                    .first()
+                    .get()?
+                    .ok_or(Self::Error::no_such_entity("user", username))?;
 
                 if password == 0 && totp == 0 {
                     println!("No changes requested!");
-                    return Ok(())
+                    return Ok(());
                 }
                 if password > 0 {
                     let raw_pass = rpassword::prompt_password("Enter new user password: ").unwrap();
@@ -68,17 +78,20 @@ impl microrm::cli::EntityInterface for UserInterface {
                     }
                     user.set_new_totp(new_secret.as_slice())?;
                 }
-            },
+            }
         }
 
         Ok(())
     }
 
-    fn should_override(_entity: &'static str, field: &'static str, _role: microrm::cli::ValueRole) -> bool {
+    fn should_override(
+        _entity: &'static str,
+        field: &'static str,
+        _role: microrm::cli::ValueRole,
+    ) -> bool {
         if field == "realm" {
             true
-        }
-        else {
+        } else {
             false
         }
     }
@@ -91,8 +104,7 @@ impl microrm::cli::EntityInterface for UserInterface {
     ) -> String {
         if field == "realm" {
             format!("{}", ctx.id().into_raw())
-        }
-        else {
+        } else {
             unreachable!()
         }
     }

+ 13 - 22
src/client_management.rs

@@ -1,7 +1,11 @@
 use crate::{key::KeyType, schema, UIDCError};
 use microrm::prelude::*;
 
-pub fn create(realm: &microrm::Stored<schema::Realm>, name: &String, key_type: KeyType) -> Result<(), UIDCError> {
+pub fn create(
+    realm: &microrm::Stored<schema::Realm>,
+    name: &String,
+    key_type: KeyType,
+) -> Result<(), UIDCError> {
     let rng = ring::rand::SystemRandom::new();
     let client_secret: [u8; 32] = ring::rand::generate(&rng).unwrap().expose();
 
@@ -20,28 +24,15 @@ pub fn rotate_secret(realm: &microrm::Stored<schema::Realm>, name: &str) -> Resu
     let rng = ring::rand::SystemRandom::new();
     let client_secret: [u8; 32] = ring::rand::generate(&rng).unwrap().expose();
 
-    todo!();
+    let mut client = realm
+        .clients
+        .with(schema::Client::Shortname, name)
+        .first()
+        .get()?
+        .ok_or(UIDCError::Abort("no such client"))?;
+    client.secret = base64::encode(&client_secret);
 
-    Ok(())
-}
-
-pub fn inspect(realm: &microrm::Stored<schema::Realm>, name: &str) -> Result<(), UIDCError> {
-    if let Some(client) = realm.clients.with(schema::Client::Shortname, name).first().get()? {
-        println!("Found client {name}");
-        println!("Client secret: {}", client.secret);
-        println!("Signature type: {:?}", client.key_type);
-        println!("Valid redirect URIs:");
-        for redirect in client.redirects.get()? {
-            println!(" - {}", redirect.redirect);
-        }
-
-        println!("Valid scopes:");
-        for scope in client.scopes.get()? {
-            println!(" - {}", scope.shortname);
-        }
-    } else {
-        println!("No such client {name}");
-    }
+    client.sync()?;
 
     Ok(())
 }

+ 21 - 26
src/key.rs

@@ -40,7 +40,10 @@ impl std::str::FromStr for KeyType {
                 return Ok(*kty);
             }
         }
-        Err(UIDCError::AbortString(format!("invalid keytype: must be one of {}", KEY_TYPE_NAMES.iter().map(|v| v.0).join(","))))
+        Err(UIDCError::AbortString(format!(
+            "invalid keytype: must be one of {}",
+            KEY_TYPE_NAMES.iter().map(|v| v.0).join(",")
+        )))
     }
 }
 
@@ -102,6 +105,21 @@ impl ParsedKey {
                 .is_ok()),
         }
     }
+
+    pub fn parse_from(key: &schema::Key) -> Result<Self, UIDCError> {
+        match key.key_type.as_ref() {
+            KeyType::RSA2048 | KeyType::RSA4096 => Ok(ParsedKey::RSA {
+                key_id: key.key_id.clone(),
+                keypair: ring::signature::RsaKeyPair::from_pkcs8(&key.secret_data)
+                    .map_err(|_| UIDCError::Abort("could not load RSA key from database"))?,
+            }),
+            KeyType::Ed25519 => Ok(ParsedKey::Ed25519 {
+                key_id: key.key_id.clone(),
+                keypair: Ed25519KeyPair::from_pkcs8(&key.secret_data)
+                    .map_err(|_| UIDCError::Abort("could not load ed25519 key from database"))?,
+            }),
+        }
+    }
 }
 
 pub struct RealmKeys {
@@ -130,17 +148,7 @@ impl RealmKeys {
         let key = self.realm.keys.with(schema::Key::KeyId, id).first().get()?;
 
         if let Some(key) = key {
-            let parsed = Arc::new(match key.key_type.as_ref() {
-                KeyType::RSA2048 | KeyType::RSA4096 => {
-                    todo!()
-                }
-                KeyType::Ed25519 => ParsedKey::Ed25519 {
-                    key_id: key.key_id.clone(),
-                    keypair: Ed25519KeyPair::from_pkcs8(&key.secret_data).map_err(|_| {
-                        UIDCError::Abort("could not load ed25519 key from database")
-                    })?,
-                },
-            });
+            let parsed = Arc::new(ParsedKey::parse_from(key.as_ref())?);
 
             self.keys
                 .borrow_mut()
@@ -169,20 +177,7 @@ impl RealmKeys {
             .get()?;
 
         if let Some(key) = key {
-            let parsed = Arc::new(match kty {
-                KeyType::RSA2048 | KeyType::RSA4096 => ParsedKey::RSA {
-                    key_id: key.key_id.clone(),
-                    keypair: ring::signature::RsaKeyPair::from_pkcs8(&key.secret_data)
-                        .map_err(|_| UIDCError::Abort("could not load RSA key from database"))?,
-                },
-                KeyType::Ed25519 => ParsedKey::Ed25519 {
-                    key_id: key.key_id.clone(),
-                    keypair: Ed25519KeyPair::from_pkcs8(&key.secret_data).map_err(|_| {
-                        UIDCError::Abort("could not load ed25519 key from database")
-                    })?,
-                },
-            });
-
+            let parsed = Arc::new(ParsedKey::parse_from(key.as_ref())?);
             self.keys.borrow_mut().push((kty, parsed.clone()));
 
             Ok(Some(parsed))

+ 4 - 1
src/scope_management.rs

@@ -17,7 +17,10 @@ pub fn list_scopes(realm: &schema::Realm) -> Result<(), UIDCError> {
     Ok(())
 }
 
-pub fn inspect_scope(realm: &microrm::Stored<schema::Realm>, scope_name: &str) -> Result<(), UIDCError> {
+pub fn inspect_scope(
+    realm: &microrm::Stored<schema::Realm>,
+    scope_name: &str,
+) -> Result<(), UIDCError> {
     let scope = realm
         .scopes
         .keyed((realm.id(), scope_name))

+ 2 - 2
src/server.rs

@@ -1,6 +1,6 @@
 use crate::{config, schema, UIDCError};
 
-// mod oidc;
+mod oidc;
 mod session;
 // mod um;
 
@@ -72,7 +72,7 @@ pub async fn run_server(
         .expect("Can't serve static files");
 
     session::session_v1_server(app.at("/:realm/v1/session/"));
-    // oidc::oidc_server(app.at("/:realm/"));
+    oidc::oidc_server(app.at("/:realm/"));
     // um::um_server(app.at("/:realm/um/"));
 
     app.listen(("127.0.0.1", port))

+ 108 - 74
src/server/oidc.rs

@@ -1,8 +1,10 @@
-use crate::{schema, token};
+use crate::{key, schema, token, UIDCError};
 use microrm::prelude::*;
 use ring::signature::KeyPair;
 use serde::{Deserialize, Serialize};
 
+use super::session::SessionHelper;
+
 type Request = tide::Request<super::ServerStateWrapper>;
 
 #[derive(serde::Serialize)]
@@ -52,7 +54,7 @@ fn do_code_authorize(
     request: Request,
     qp: AuthorizeQueryParams,
     state: Option<&str>,
-    client: microrm::WithID<schema::Client>,
+    client: microrm::Stored<schema::Client>,
 ) -> Result<tide::Response, OIDCError> {
     todo!()
 }
@@ -61,26 +63,47 @@ fn do_token_authorize(
     request: Request,
     qp: AuthorizeQueryParams,
     state: Option<&str>,
-    client: microrm::WithID<schema::Client>,
+    client: microrm::Stored<schema::Client>,
 ) -> Result<tide::Response, OIDCError> {
-    let qi = request.state().core.pool.query_interface();
-
-    let shelper = super::session::SessionHelper::new(&request);
-
-    let sauth = shelper
-        .get_session(&request)
-        .and_then(|sid| shelper.get_auth_for_session(shelper.get_realm().unwrap(), sid));
+    let shelper = SessionHelper::new(&request);
+    let realm = shelper.get_realm().map_err(|_| {
+        OIDCError(
+            OIDCErrorType::InvalidRequest,
+            "No such realm!".to_string(),
+            state,
+        )
+    })?;
 
-    if sauth.is_none() {
-        // if we don't have any relevant auth info, redirect to login
+    let make_redirect = || {
         let mut login_url = request.url().join("../session/login").unwrap();
         login_url
             .query_pairs_mut()
             .clear()
             .append_pair("redirect", request.url().as_str());
         return Ok(tide::Redirect::new(login_url).into());
-    }
-    let sauth = sauth.unwrap();
+    };
+
+    let potential_sauth = shelper
+        .get_session(&request)
+        .and_then(|session| shelper.get_auth_for_session(realm.id(), &session));
+
+    let Some(user_id) = potential_sauth.and_then(|v| v.user) else {
+        // if we don't have any relevant auth info, redirect to login
+        return make_redirect();
+    };
+
+    let Ok(Some(user)) = realm
+        .users
+        .with(schema::UserID::default(), user_id)
+        .first()
+        .get()
+    else {
+        return Err(OIDCError(
+            OIDCErrorType::ServerError,
+            "Internal state error!".to_string(),
+            state,
+        ));
+    };
 
     let scopes = qp
         .scope
@@ -89,12 +112,13 @@ fn do_token_authorize(
         .unwrap_or("")
         .split_whitespace();
 
+    // TODO: check that redirect URI matches
+
     let token = token::generate_auth_token(
         &request.state().core.config,
-        &qi,
-        shelper.get_realm().unwrap(),
-        client.id(),
-        sauth.user,
+        &realm,
+        client.as_ref(),
+        user.as_ref(),
         scopes,
     );
 
@@ -108,50 +132,46 @@ fn do_token_authorize(
 }
 
 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(
+            OIDCErrorType::InvalidRequest,
+            "No such realm!".to_string(),
+            state,
+        )
+    })?;
+
     let qp: AuthorizeQueryParams = request
         .query()
         .map_err(|x| OIDCError(OIDCErrorType::InvalidRequest, x.to_string(), state))?;
 
     // verify the realm and client_id and redirect_uri
-    let qi = request.state().core.pool.query_interface();
-    let realm = qi
-        .get()
-        .by(schema::Realm::Shortname, &request.param("realm").unwrap())
-        .one()
-        .expect("couldn't query db");
-    if realm.is_none() {
-        return Err(OIDCError(
-            OIDCErrorType::InvalidRequest,
-            "No such realm!".to_string(),
-            state,
-        ));
-    }
-    let realm = realm.unwrap();
 
-    let client = qi
+    let client = realm
+        .clients
+        .with(schema::Client::Shortname, &qp.client_id)
+        .first()
         .get()
-        .by(schema::Client::Realm, &realm.id())
-        .by(schema::Client::Shortname, &qp.client_id)
-        .one()
-        .expect("couldn't query db");
-    if client.is_none() {
-        return Err(OIDCError(
-            OIDCErrorType::UnauthorizedClient,
-            "Client does not exist".to_string(),
-            state,
-        ));
-    }
+        .ok()
+        .flatten()
+        .ok_or_else(|| {
+            OIDCError(
+                OIDCErrorType::UnauthorizedClient,
+                "Client does not exist".to_string(),
+                state,
+            )
+        })?;
 
     if qp.response_type == "code" {
-        do_code_authorize(request, qp, state, client.unwrap())
+        do_code_authorize(request, qp, state, client)
     } else if qp.response_type == "token" {
-        do_token_authorize(request, qp, state, client.unwrap())
+        do_token_authorize(request, qp, state, client)
     } else {
-        return Err(OIDCError(
+        Err(OIDCError(
             OIDCErrorType::UnsupportedResponseType,
             "Only code and token are understood.".to_string(),
             state,
-        ));
+        ))
     }
 }
 
@@ -193,35 +213,46 @@ const TOKEN_PATH: &'static str = "oidc/token";
 const JWKS_PATH: &'static str = "oidc/jwks";
 
 async fn jwks(request: Request) -> tide::Result<tide::Response> {
-    let qi = request.state().core.pool.query_interface();
-
     let shelper = super::session::SessionHelper::new(&request);
     let realm = shelper.get_realm()?;
-
-    let keyinfo = qi
-        .get()
-        .by(schema::Key::Realm, &realm)
-        .all()?
-        .into_iter()
-        .map(|key| {
-            let kpair = ring::signature::Ed25519KeyPair::from_pkcs8(&key.keydata)
-                .expect("couldn't parse keypair in db");
-            let pubkey_bytes = kpair.public_key().as_ref();
-            assert_eq!(pubkey_bytes.len(), 32);
-
-            serde_json::json!({
-                "kty": "OKP",
-                "crv": "Ed25519",
-                "x": base64::encode(pubkey_bytes),
-                "kid": key.key_id,
-            })
-        });
+    // let rkeys = key::RealmKeys::new(realm.wrapped());
+
+    let keyinfo =
+        realm
+            .keys
+            .get()?
+            .into_iter()
+            .map(|key| match key::ParsedKey::parse_from(&key)? {
+                key::ParsedKey::Ed25519 { key_id: _, keypair } => Ok(serde_json::json!({
+                    "crv": "Ed25519",
+                    "kid": key.key_id,
+                    "kty": "OKP",
+                    "use": "sig",
+
+                    "x": base64::encode(keypair.public_key().as_ref()),
+                })),
+                key::ParsedKey::RSA { key_id: _, keypair } => {
+                    let pubkey = keypair.public_key();
+                    Ok(serde_json::json!({
+                        "alg": "RS256",
+                        "kid": key.key_id,
+                        "kty": "RSA",
+                        "use": "sig",
+
+                        "e": base64::encode(pubkey.exponent().big_endian_without_leading_zero()),
+                        "n": base64::encode(pubkey.modulus().big_endian_without_leading_zero()),
+                    }))
+                }
+            });
 
     let jwks_response = serde_json::json!({
-        "keys": keyinfo.collect::<Vec<_>>(),
+        "keys": keyinfo.collect::<Result<Vec<_>, UIDCError>>()?,
     });
 
-    Ok(tide::Response::builder(200).body(jwks_response).build())
+    Ok(tide::Response::builder(200)
+        .header(tide::http::headers::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
+        .body(jwks_response)
+        .build())
 }
 
 async fn discovery_config(request: Request) -> tide::Result<tide::Response> {
@@ -237,13 +268,16 @@ async fn discovery_config(request: Request) -> tide::Result<tide::Response> {
         "authorization_endpoint": format!("{}/{}", base_url, AUTHORIZE_PATH),
         "token_endpoint": format!("{}/{}", base_url, TOKEN_PATH),
         "jwks_uri": format!("{}/{}", base_url, JWKS_PATH),
-        "token_endpoint_auth_signing_alg_values_supported": ["EdDSA"],
+        "token_endpoint_auth_signing_alg_values_supported": ["EdDSA", "RS256"],
         "response_types_supported": ["code", "id_token", "token id_token"],
         "subject_types_supported": ["public"],
-        "id_token_signing_alg_values_supported": ["EdDSA"],
+        "id_token_signing_alg_values_supported": ["EdDSA", "RS256"],
     });
 
-    Ok(tide::Response::builder(200).body(config_response).build())
+    Ok(tide::Response::builder(200)
+        .header(tide::http::headers::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
+        .body(config_response)
+        .build())
 }
 
 pub(super) fn oidc_server(mut route: tide::Route<super::ServerStateWrapper>) {

+ 5 - 1
src/server/session.rs

@@ -274,7 +274,11 @@ async fn v1_login_post(mut req: Request) -> tide::Result<tide::Response> {
         ChallengeType::Username => {
             shelper.destroy_auth(realm.id(), &session)?;
 
-            let user = realm.users.with(schema::User::Username, &body.challenge).first().get()?;
+            let user = realm
+                .users
+                .with(schema::User::Username, &body.challenge)
+                .first()
+                .get()?;
             if user.is_none() {
                 error = Some(format!("No such user {}", body.challenge));
             } else {

+ 6 - 1
src/token.rs

@@ -38,7 +38,12 @@ pub fn generate_auth_token<'a>(
     // 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()? {
+        if let Some(scope) = realm
+            .scopes
+            .with(schema::Scope::Shortname, scope_name)
+            .first()
+            .get()?
+        {
             requested_roles.extend(scope.roles.get()?.into_iter());
         }
     }

+ 7 - 3
src/user.rs

@@ -73,7 +73,8 @@ pub trait UserExt {
     }
 
     fn set_new_password(&self, password: &[u8]) -> Result<(), UIDCError> {
-        self.stored_user().auth
+        self.stored_user()
+            .auth
             .with(
                 schema::AuthChallenge::ChallengeType,
                 &schema::AuthChallengeType::Password.into(),
@@ -139,7 +140,8 @@ pub trait UserExt {
     }
 
     fn clear_totp(&self) -> Result<(), UIDCError> {
-        self.stored_user().auth
+        self.stored_user()
+            .auth
             .with(
                 schema::AuthChallenge::ChallengeType,
                 &schema::AuthChallengeType::TOTP.into(),
@@ -150,7 +152,9 @@ pub trait UserExt {
 }
 
 impl UserExt for Stored<schema::User> {
-    fn stored_user(&self) -> &Stored<schema::User> { self }
+    fn stored_user(&self) -> &Stored<schema::User> {
+        self
+    }
 }
 
 impl schema::AuthChallenge {