dry redis connections and use bearer tokens

This commit is contained in:
Bryan Stitt 2022-08-23 18:48:27 +00:00
parent 94bc6fef8c
commit 2989b7e91c
4 changed files with 43 additions and 27 deletions

21
Cargo.lock generated

@ -278,9 +278,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.53" version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -386,6 +386,18 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "axum-auth"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9770f9a9147b2324066609acb5495538cb25f973129663fba2658ba7ed69407"
dependencies = [
"async-trait",
"axum-core",
"base64 0.13.0",
"http",
]
[[package]] [[package]]
name = "axum-client-ip" name = "axum-client-ip"
version = "0.2.0" version = "0.2.0"
@ -4344,9 +4356,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.83" version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [ dependencies = [
"itoa 1.0.2", "itoa 1.0.2",
"ryu", "ryu",
@ -5521,6 +5533,7 @@ dependencies = [
"arc-swap", "arc-swap",
"argh", "argh",
"axum", "axum",
"axum-auth",
"axum-client-ip", "axum-client-ip",
"axum-macros", "axum-macros",
"counter", "counter",

@ -20,6 +20,7 @@ anyhow = { version = "1.0.62", features = ["backtrace"] }
arc-swap = "1.5.1" arc-swap = "1.5.1"
argh = "0.1.8" argh = "0.1.8"
axum = { version = "0.5.15", features = ["headers", "serde_json", "tokio-tungstenite", "ws"] } axum = { version = "0.5.15", features = ["headers", "serde_json", "tokio-tungstenite", "ws"] }
axum-auth = "0.3.0"
axum-client-ip = "0.2.0" axum-client-ip = "0.2.0"
axum-macros = "0.2.3" axum-macros = "0.2.3"
counter = "0.5.6" counter = "0.5.6"
@ -49,7 +50,7 @@ rustc-hash = "1.1.0"
siwe = "0.4.1" siwe = "0.4.1"
sea-orm = { version = "0.9.2", features = ["macros"] } sea-orm = { version = "0.9.2", features = ["macros"] }
serde = { version = "1.0.144", features = [] } serde = { version = "1.0.144", features = [] }
serde_json = { version = "1.0.83", default-features = false, features = ["alloc", "raw_value"] } serde_json = { version = "1.0.85", default-features = false, features = ["alloc", "raw_value"] }
# TODO: make sure this time version matches siwe. PR to put this in their prelude # TODO: make sure this time version matches siwe. PR to put this in their prelude
time = "0.3.13" time = "0.3.13"
tokio = { version = "1.20.1", features = ["full", "tracing"] } tokio = { version = "1.20.1", features = ["full", "tracing"] }

@ -15,6 +15,7 @@ use futures::stream::StreamExt;
use futures::Future; use futures::Future;
use migration::{Migrator, MigratorTrait}; use migration::{Migrator, MigratorTrait};
use parking_lot::RwLock; use parking_lot::RwLock;
use redis_rate_limit::bb8::PooledConnection;
use redis_rate_limit::{ use redis_rate_limit::{
bb8::{self, ErrorSink}, bb8::{self, ErrorSink},
RedisConnectionManager, RedisErrorSink, RedisPool, RedisRateLimit, RedisConnectionManager, RedisErrorSink, RedisPool, RedisRateLimit,
@ -158,6 +159,17 @@ impl fmt::Debug for Web3ProxyApp {
} }
impl Web3ProxyApp { impl Web3ProxyApp {
pub async fn redis_conn(&self) -> anyhow::Result<PooledConnection<RedisConnectionManager>> {
match self.redis_pool.as_ref() {
None => Err(anyhow::anyhow!("no redis server configured")),
Some(redis_pool) => {
let redis_conn = redis_pool.get().await?;
Ok(redis_conn)
}
}
}
// TODO: should we just take the rpc config as the only arg instead? // TODO: should we just take the rpc config as the only arg instead?
pub async fn spawn( pub async fn spawn(
app_stats: AppStats, app_stats: AppStats,

@ -15,6 +15,7 @@ use axum::{
response::IntoResponse, response::IntoResponse,
Extension, Json, Extension, Json,
}; };
use axum_auth::AuthBearer;
use axum_client_ip::ClientIp; use axum_client_ip::ClientIp;
use axum_macros::debug_handler; use axum_macros::debug_handler;
use entities::{user, user_keys}; use entities::{user, user_keys};
@ -48,7 +49,8 @@ pub async fn get_login(
// create a message and save it in redis // create a message and save it in redis
// TODO: how many seconds? get from config? // TODO: how many seconds? get from config?
let expire_seconds: usize = 300; // TODO: while developing, we put a giant number here
let expire_seconds: usize = 28800;
let nonce = Ulid::new(); let nonce = Ulid::new();
@ -78,21 +80,14 @@ pub async fn get_login(
let session_key = format!("pending:{}", nonce); let session_key = format!("pending:{}", nonce);
// TODO: if no redis server, store in local cache? // TODO: if no redis server, store in local cache?
let mut redis_conn = app
.redis_pool
.as_ref()
.expect("login requires a redis server")
.get()
.await?;
// the address isn't enough. we need to save the actual message so we can read the nonce // the address isn't enough. we need to save the actual message so we can read the nonce
// TODO: what message format is the most efficient to store in redis? probably eip191_string // TODO: what message format is the most efficient to store in redis? probably eip191_string
redis_conn // we add 1 to expire_seconds just to be sure redis has the key for the full expiration_time
.set_ex(session_key, message.to_string(), expire_seconds) app.redis_conn()
.await?
.set_ex(session_key, message.to_string(), expire_seconds + 1)
.await?; .await?;
drop(redis_conn);
// there are multiple ways to sign messages and not all wallets support them // there are multiple ways to sign messages and not all wallets support them
let message_eip = params let message_eip = params
.remove("message_eip") .remove("message_eip")
@ -152,16 +147,8 @@ pub async fn post_login(
let their_sig: [u8; 65] = payload.sig.as_ref().try_into().unwrap(); let their_sig: [u8; 65] = payload.sig.as_ref().try_into().unwrap();
// fetch the message we gave them from our redis // fetch the message we gave them from our redis
let redis_pool = app
.redis_pool
.as_ref()
.expect("login requires a redis server");
let mut redis_conn = redis_pool.get().await.unwrap();
// TODO: use getdel // TODO: use getdel
// TODO: do not unwrap. make this function return a FrontendResult let our_msg: String = app.redis_conn().await?.get(&their_msg.nonce).await?;
let our_msg: String = redis_conn.get(&their_msg.nonce).await.unwrap();
let our_msg: siwe::Message = our_msg.parse().unwrap(); let our_msg: siwe::Message = our_msg.parse().unwrap();
@ -203,6 +190,7 @@ pub async fn post_login(
// TODO: set a cookie? // TODO: set a cookie?
// TODO: do not expose user ids // TODO: do not expose user ids
// TODO: return an api key and a bearer token
(StatusCode::CREATED, Json(user)).into_response() (StatusCode::CREATED, Json(user)).into_response()
*/ */
} else { } else {
@ -224,10 +212,12 @@ pub struct PostUser {
pub async fn post_user( pub async fn post_user(
Json(payload): Json<PostUser>, Json(payload): Json<PostUser>,
Extension(app): Extension<Arc<Web3ProxyApp>>, Extension(app): Extension<Arc<Web3ProxyApp>>,
ClientIp(ip): ClientIp, AuthBearer(auth_token): AuthBearer,
) -> FrontendResult { ) -> FrontendResult {
todo!("finish post_user"); todo!("finish post_user");
// TODO: check the auth_token is valid for the user in PostUser
// let user = user::ActiveModel { // let user = user::ActiveModel {
// address: sea_orm::Set(payload.address.to_fixed_bytes().into()), // address: sea_orm::Set(payload.address.to_fixed_bytes().into()),
// email: sea_orm::Set(payload.email), // email: sea_orm::Set(payload.email),