support EIP1271 login

This commit is contained in:
Bryan Stitt 2023-06-24 09:48:28 -07:00
parent 91cbce6ce8
commit 8f76d9320d
6 changed files with 76 additions and 273 deletions

192
Cargo.lock generated
View File

@ -570,12 +570,6 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "base16ct"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
[[package]] [[package]]
name = "base16ct" name = "base16ct"
version = "0.2.0" version = "0.2.0"
@ -1027,7 +1021,7 @@ dependencies = [
"digest 0.10.7", "digest 0.10.7",
"getrandom", "getrandom",
"hmac", "hmac",
"k256 0.13.1", "k256",
"lazy_static", "lazy_static",
"serde", "serde",
"sha2 0.10.6", "sha2 0.10.6",
@ -1327,18 +1321,6 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "crypto-bigint"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
dependencies = [
"generic-array",
"rand_core",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
version = "0.5.2" version = "0.5.2"
@ -1465,16 +1447,6 @@ dependencies = [
"pem-rfc7468", "pem-rfc7468",
] ]
[[package]]
name = "der"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
dependencies = [
"const-oid 0.9.2",
"zeroize",
]
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.6" version = "0.7.6"
@ -1590,18 +1562,6 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]]
name = "ecdsa"
version = "0.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
dependencies = [
"der 0.6.1",
"elliptic-curve 0.12.3",
"rfc6979 0.3.1",
"signature 1.6.4",
]
[[package]] [[package]]
name = "ecdsa" name = "ecdsa"
version = "0.16.7" version = "0.16.7"
@ -1610,9 +1570,9 @@ checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428"
dependencies = [ dependencies = [
"der 0.7.6", "der 0.7.6",
"digest 0.10.7", "digest 0.10.7",
"elliptic-curve 0.13.5", "elliptic-curve",
"rfc6979 0.4.0", "rfc6979",
"signature 2.1.0", "signature",
"spki 0.7.2", "spki 0.7.2",
] ]
@ -1622,40 +1582,21 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "elliptic-curve"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
dependencies = [
"base16ct 0.1.1",
"crypto-bigint 0.4.9",
"der 0.6.1",
"digest 0.10.7",
"ff 0.12.1",
"generic-array",
"group 0.12.1",
"rand_core",
"sec1 0.3.0",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "elliptic-curve" name = "elliptic-curve"
version = "0.13.5" version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b"
dependencies = [ dependencies = [
"base16ct 0.2.0", "base16ct",
"crypto-bigint 0.5.2", "crypto-bigint 0.5.2",
"digest 0.10.7", "digest 0.10.7",
"ff 0.13.0", "ff",
"generic-array", "generic-array",
"group 0.13.0", "group",
"pkcs8 0.10.2", "pkcs8 0.10.2",
"rand_core", "rand_core",
"sec1 0.7.2", "sec1",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
@ -1693,7 +1634,7 @@ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"bytes", "bytes",
"hex", "hex",
"k256 0.13.1", "k256",
"log", "log",
"rand", "rand",
"rlp", "rlp",
@ -1928,11 +1869,11 @@ dependencies = [
"bytes", "bytes",
"cargo_metadata 0.15.4", "cargo_metadata 0.15.4",
"chrono", "chrono",
"elliptic-curve 0.13.5", "elliptic-curve",
"ethabi", "ethabi",
"generic-array", "generic-array",
"hex", "hex",
"k256 0.13.1", "k256",
"num_enum 0.6.1", "num_enum 0.6.1",
"once_cell", "once_cell",
"open-fastrlp", "open-fastrlp",
@ -2036,7 +1977,7 @@ dependencies = [
"async-trait", "async-trait",
"coins-bip32", "coins-bip32",
"coins-bip39", "coins-bip39",
"elliptic-curve 0.13.5", "elliptic-curve",
"eth-keystore", "eth-keystore",
"ethers-core", "ethers-core",
"hex", "hex",
@ -2145,16 +2086,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ff"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
dependencies = [
"rand_core",
"subtle",
]
[[package]] [[package]]
name = "ff" name = "ff"
version = "0.13.0" version = "0.13.0"
@ -2482,24 +2413,13 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558b88954871f5e5b2af0e62e2e176c8bde7a6c2c4ed41b13d138d96da2e2cbd" checksum = "558b88954871f5e5b2af0e62e2e176c8bde7a6c2c4ed41b13d138d96da2e2cbd"
[[package]]
name = "group"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
dependencies = [
"ff 0.12.1",
"rand_core",
"subtle",
]
[[package]] [[package]]
name = "group" name = "group"
version = "0.13.0" version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [ dependencies = [
"ff 0.13.0", "ff",
"rand_core", "rand_core",
"subtle", "subtle",
] ]
@ -3049,9 +2969,9 @@ dependencies = [
[[package]] [[package]]
name = "iri-string" name = "iri-string"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0586ad318a04c73acdbad33f67969519b5452c80770c4c72059a686da48a7e" checksum = "21859b667d66a4c1dacd9df0863b3efb65785474255face87f5bca39dd8407c0"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
@ -3111,19 +3031,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "k256"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
dependencies = [
"cfg-if",
"ecdsa 0.14.8",
"elliptic-curve 0.12.3",
"sha2 0.10.6",
"sha3",
]
[[package]] [[package]]
name = "k256" name = "k256"
version = "0.13.1" version = "0.13.1"
@ -3131,11 +3038,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"ecdsa 0.16.7", "ecdsa",
"elliptic-curve 0.13.5", "elliptic-curve",
"once_cell", "once_cell",
"sha2 0.10.6", "sha2 0.10.6",
"signature 2.1.0", "signature",
] ]
[[package]] [[package]]
@ -4169,16 +4076,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "pkcs8"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
dependencies = [
"der 0.6.1",
"spki 0.6.0",
]
[[package]] [[package]]
name = "pkcs8" name = "pkcs8"
version = "0.10.2" version = "0.10.2"
@ -4720,17 +4617,6 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
[[package]]
name = "rfc6979"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb"
dependencies = [
"crypto-bigint 0.4.9",
"hmac",
"zeroize",
]
[[package]] [[package]]
name = "rfc6979" name = "rfc6979"
version = "0.4.0" version = "0.4.0"
@ -5261,27 +5147,13 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "sec1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
dependencies = [
"base16ct 0.1.1",
"der 0.6.1",
"generic-array",
"pkcs8 0.9.0",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "sec1" name = "sec1"
version = "0.7.2" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e"
dependencies = [ dependencies = [
"base16ct 0.2.0", "base16ct",
"der 0.7.6", "der 0.7.6",
"generic-array", "generic-array",
"pkcs8 0.10.2", "pkcs8 0.10.2",
@ -5642,16 +5514,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "signature"
version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
dependencies = [
"digest 0.10.7",
"rand_core",
]
[[package]] [[package]]
name = "signature" name = "signature"
version = "2.1.0" version = "2.1.0"
@ -5677,13 +5539,13 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "siwe" name = "siwe"
version = "0.5.2" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/llamanodes/siwe-rs?rev=013be5204ff1c85778ce21619f4b677a003db8a1#013be5204ff1c85778ce21619f4b677a003db8a1"
checksum = "4e6d1f422a568af1e98db37c6d0427c7218459ccac39218fd15a51a34d3933af"
dependencies = [ dependencies = [
"ethers",
"hex", "hex",
"http", "http",
"iri-string", "iri-string",
"k256 0.11.6", "k256",
"rand", "rand",
"serde", "serde",
"sha3", "sha3",
@ -5802,16 +5664,6 @@ dependencies = [
"der 0.5.1", "der 0.5.1",
] ]
[[package]]
name = "spki"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
dependencies = [
"base64ct",
"der 0.6.1",
]
[[package]] [[package]]
name = "spki" name = "spki"
version = "0.7.2" version = "0.7.2"

View File

@ -25,7 +25,7 @@ redis-rate-limiter = { path = "../redis-rate-limiter" }
#ethers = { git = "https://github.com/llamanodes/ethers-rs/", rev = "eb68f5d60850008cd302762bd3a5a4bdcfecc713", default-features = false, features = ["rustls", "ws"] } #ethers = { git = "https://github.com/llamanodes/ethers-rs/", rev = "eb68f5d60850008cd302762bd3a5a4bdcfecc713", default-features = false, features = ["rustls", "ws"] }
influxdb2 = { git = "https://github.com/llamanodes/influxdb2", features = ["rustls"], rev = "2d125128696a29d7e0b9abc052c928937e7c0579" } influxdb2 = { git = "https://github.com/llamanodes/influxdb2", features = ["rustls"], rev = "2d125128696a29d7e0b9abc052c928937e7c0579" }
influxdb2-structmap = { git = "https://github.com/llamanodes/influxdb2/", rev = "2d125128696a29d7e0b9abc052c928937e7c0579"} influxdb2-structmap = { git = "https://github.com/llamanodes/influxdb2/", rev = "2d125128696a29d7e0b9abc052c928937e7c0579"}
#siwe = { git = "https://github.com/llamanodes/siwe-rs", rev = "bef5449b5dd8beb4e9fc697f09cd6dd11ba8f6e6", features = ["ethers", "serde"] } siwe = { git = "https://github.com/llamanodes/siwe-rs", rev = "013be5204ff1c85778ce21619f4b677a003db8a1", features = ["ethers", "serde"] }
# TODO: regex has several "perf" features that we might want to use # TODO: regex has several "perf" features that we might want to use
# TODO: make sure this uuid version matches sea-orm. PR to put this in their prelude # TODO: make sure this uuid version matches sea-orm. PR to put this in their prelude
@ -89,7 +89,6 @@ sentry-tracing = "0.31.5"
serde = { version = "1.0.164" } serde = { version = "1.0.164" }
serde_json = { version = "1.0.99", default-features = false, features = ["raw_value"] } serde_json = { version = "1.0.99", default-features = false, features = ["raw_value"] }
serde_prometheus = "0.2.3" serde_prometheus = "0.2.3"
siwe = { version = "0.5.2", features = ["serde"] }
strum = { version = "0.25.0", features = ["derive"] } strum = { version = "0.25.0", features = ["derive"] }
time = { version = "0.3.22", features = ["serde-well-known"] } time = { version = "0.3.22", features = ["serde-well-known"] }
tokio = { version = "1.28.2", features = ["full", "tracing"] } tokio = { version = "1.28.2", features = ["full", "tracing"] }

View File

@ -21,6 +21,7 @@ use reqwest::header::ToStrError;
use rust_decimal::Error as DecimalError; use rust_decimal::Error as DecimalError;
use serde::Serialize; use serde::Serialize;
use serde_json::value::RawValue; use serde_json::value::RawValue;
use siwe::VerificationError;
use std::sync::Arc; use std::sync::Arc;
use std::{borrow::Cow, net::IpAddr}; use std::{borrow::Cow, net::IpAddr};
use tokio::{sync::AcquireError, task::JoinError, time::Instant}; use tokio::{sync::AcquireError, task::JoinError, time::Instant};
@ -54,8 +55,6 @@ pub enum Web3ProxyError {
Contract(ContractError<EthersHttpProvider>), Contract(ContractError<EthersHttpProvider>),
Database(DbErr), Database(DbErr),
Decimal(DecimalError), Decimal(DecimalError),
#[display(fmt = "{:#?}, {:#?}", _0, _1)]
EipVerificationFailed(Box<Web3ProxyError>, Box<Web3ProxyError>),
EthersHttpClient(ethers::prelude::HttpClientError), EthersHttpClient(ethers::prelude::HttpClientError),
EthersProvider(ethers::prelude::ProviderError), EthersProvider(ethers::prelude::ProviderError),
EthersWsClient(ethers::prelude::WsClientError), EthersWsClient(ethers::prelude::WsClientError),
@ -133,6 +132,7 @@ pub enum Web3ProxyError {
SemaphoreAcquireError(AcquireError), SemaphoreAcquireError(AcquireError),
SendAppStatError(flume::SendError<crate::stats::AppStat>), SendAppStatError(flume::SendError<crate::stats::AppStat>),
SerdeJson(serde_json::Error), SerdeJson(serde_json::Error),
SiweVerification(VerificationError),
/// simple way to return an error message to the user and an anyhow to our logs /// simple way to return an error message to the user and an anyhow to our logs
#[display(fmt = "{}, {}, {:?}", _0, _1, _2)] #[display(fmt = "{}, {}, {:?}", _0, _1, _2)]
StatusCode(StatusCode, Cow<'static, str>, Option<anyhow::Error>), StatusCode(StatusCode, Cow<'static, str>, Option<anyhow::Error>),
@ -148,7 +148,6 @@ pub enum Web3ProxyError {
UserAgentNotAllowed(headers::UserAgent), UserAgentNotAllowed(headers::UserAgent),
UserIdZero, UserIdZero,
PaymentRequired, PaymentRequired,
VerificationError(siwe::VerificationError),
WatchRecvError(tokio::sync::watch::error::RecvError), WatchRecvError(tokio::sync::watch::error::RecvError),
WatchSendError, WatchSendError,
WebsocketOnly, WebsocketOnly,
@ -267,20 +266,12 @@ impl Web3ProxyError {
}, },
) )
} }
Self::EipVerificationFailed(err_1, err_191) => { Self::SiweVerification(err) => {
trace!( trace!("Siwe Verification err={:#?}", err,);
"EipVerificationFailed err_1={:#?} err2={:#?}",
err_1,
err_191
);
( (
StatusCode::UNAUTHORIZED, StatusCode::UNAUTHORIZED,
JsonRpcErrorData { JsonRpcErrorData {
message: format!( message: format!("siwe verification error: {:#?}", err).into(),
"both the primary and eip191 verification failed: {:#?}; {:#?}",
err_1, err_191
)
.into(),
code: StatusCode::UNAUTHORIZED.as_u16().into(), code: StatusCode::UNAUTHORIZED.as_u16().into(),
data: None, data: None,
}, },
@ -960,17 +951,6 @@ impl Web3ProxyError {
}, },
) )
} }
Self::VerificationError(err) => {
trace!("VerificationError err={:#?}", err);
(
StatusCode::BAD_REQUEST,
JsonRpcErrorData {
message: "verification error!".into(),
code: StatusCode::BAD_REQUEST.as_u16().into(),
data: None,
},
)
}
Self::WatchRecvError(err) => { Self::WatchRecvError(err) => {
error!("WatchRecvError err={:#?}", err); error!("WatchRecvError err={:#?}", err);
( (

View File

@ -252,6 +252,16 @@ pub async fn admin_login_get(
.db_replica() .db_replica()
.web3_context("login requires a replica database")?; .web3_context("login requires a replica database")?;
// delete ALL expired rows.
let now = Utc::now();
let delete_result = pending_login::Entity::delete_many()
.filter(pending_login::Column::ExpiresAt.lte(now))
.exec(&db_conn)
.await?;
// TODO: emit a stat? if this is high something weird might be happening
debug!("cleared expired pending_logins: {:?}", delete_result);
// Get the user that we want to imitate from the read-only database (their id ...) // Get the user that we want to imitate from the read-only database (their id ...)
// TODO: Only get the id, not the whole user object ... // TODO: Only get the id, not the whole user object ...
let user = user::Entity::find() let user = user::Entity::find()
@ -404,39 +414,16 @@ pub async fn admin_login_post(
.parse() .parse()
.web3_context("parsing siwe message")?; .web3_context("parsing siwe message")?;
// default options are fine. the message includes timestamp and domain and nonce // mostly default options are fine. the message includes timestamp and domain and nonce
let verify_config = VerificationOpts::default(); let verify_config = VerificationOpts {
rpc_provider: Some(app.internal_provider.clone()),
..Default::default()
};
let db_conn = app our_msg
.db_conn()
.web3_context("deleting expired pending logins requires a db")?;
if let Err(err_1) = our_msg
.verify(&their_sig, &verify_config) .verify(&their_sig, &verify_config)
.await .await
.web3_context("verifying signature against our local message") .web3_context("verifying signature against our local message")?;
{
// verification method 1 failed. try eip191
if let Err(err_191) = our_msg
.verify_eip191(&their_sig)
.web3_context("verifying eip191 signature against our local message")
{
// delete ALL expired rows.
let now = Utc::now();
let delete_result = pending_login::Entity::delete_many()
.filter(pending_login::Column::ExpiresAt.lte(now))
.exec(&db_conn)
.await?;
// TODO: emit a stat? if this is high something weird might be happening
debug!("cleared expired pending_logins: {:?}", delete_result);
return Err(Web3ProxyError::EipVerificationFailed(
Box::new(err_1),
Box::new(err_191),
));
}
}
let imitating_user_id = user_pending_login let imitating_user_id = user_pending_login
.imitating_user .imitating_user
@ -456,6 +443,10 @@ pub async fn admin_login_post(
.await? .await?
.web3_context("admin address was not found!")?; .web3_context("admin address was not found!")?;
let db_conn = app
.db_conn()
.web3_context("deleting expired pending logins requires a db")?;
// Add a message that the admin has logged in // Add a message that the admin has logged in
// Note that the admin is trying to log in as this user // Note that the admin is trying to log in as this user
let trail = admin_trail::ActiveModel { let trail = admin_trail::ActiveModel {
@ -500,9 +491,7 @@ pub async fn admin_login_post(
// add bearer to the database // add bearer to the database
// expire in 2 days, because this is more critical (and shouldn't need to be done so long!) // expire in 2 days, because this is more critical (and shouldn't need to be done so long!)
let expires_at = Utc::now() let expires_at = Utc::now() + chrono::Duration::days(2);
.checked_add_signed(chrono::Duration::days(2))
.unwrap();
// TODO: Here, the bearer token should include a message // TODO: Here, the bearer token should include a message
// TODO: Above, make sure that the calling address is an admin! // TODO: Above, make sure that the calling address is an admin!

View File

@ -29,7 +29,7 @@ use std::ops::Add;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use time::{Duration, OffsetDateTime}; use time::{Duration, OffsetDateTime};
use tracing::{trace, warn}; use tracing::{error, trace, warn};
use ulid::Ulid; use ulid::Ulid;
/// `GET /user/login/:user_address` or `GET /user/login/:user_address/:message_eip` -- Start the "Sign In with Ethereum" (siwe) login flow. /// `GET /user/login/:user_address` or `GET /user/login/:user_address/:message_eip` -- Start the "Sign In with Ethereum" (siwe) login flow.
@ -38,8 +38,7 @@ use ulid::Ulid;
/// - eip191_bytes /// - eip191_bytes
/// - eip191_hash /// - eip191_hash
/// - eip4361 (default) /// - eip4361 (default)
/// /// - eip1271
/// Coming soon: eip1271
/// ///
/// This is the initial entrypoint for logging in. Take the response from this endpoint and give it to your user's wallet for singing. POST the response to `/user/login`. /// This is the initial entrypoint for logging in. Take the response from this endpoint and give it to your user's wallet for singing. POST the response to `/user/login`.
/// ///
@ -103,6 +102,13 @@ pub async fn user_login_get(
let db_conn = app.db_conn().web3_context("login requires a database")?; let db_conn = app.db_conn().web3_context("login requires a database")?;
// delete ALL expired rows.
let now = Utc::now();
let _ = pending_login::Entity::delete_many()
.filter(pending_login::Column::ExpiresAt.lte(now))
.exec(&db_conn)
.await?;
// massage types to fit in the database. sea-orm does not make this very elegant // massage types to fit in the database. sea-orm does not make this very elegant
let uuid = Uuid::from_u128(nonce.into()); let uuid = Uuid::from_u128(nonce.into());
// we add 1 to expire_seconds just to be sure the database has the key for the full expiration_time // we add 1 to expire_seconds just to be sure the database has the key for the full expiration_time
@ -234,11 +240,8 @@ pub async fn user_login_post(
.db_replica() .db_replica()
.web3_context("Getting database connection")?; .web3_context("Getting database connection")?;
// massage type for the db
let login_nonce_uuid: Uuid = login_nonce.clone().into();
let user_pending_login = pending_login::Entity::find() let user_pending_login = pending_login::Entity::find()
.filter(pending_login::Column::Nonce.eq(login_nonce_uuid)) .filter(pending_login::Column::Nonce.eq(Uuid::from(login_nonce.clone())))
.one(db_replica.as_ref()) .one(db_replica.as_ref())
.await .await
.web3_context("database error while finding pending_login")? .web3_context("database error while finding pending_login")?
@ -249,40 +252,17 @@ pub async fn user_login_post(
.parse() .parse()
.web3_context("parsing siwe message")?; .web3_context("parsing siwe message")?;
// default options are fine. the message includes timestamp and domain and nonce // mostly default options are fine. the message includes timestamp and domain and nonce
let verify_config = VerificationOpts::default(); let verify_config = VerificationOpts {
rpc_provider: Some(app.internal_provider.clone()),
..Default::default()
};
// Check with both verify and verify_eip191 // Check with both verify and verify_eip191
if let Err(err_1) = our_msg our_msg
.verify(&their_sig, &verify_config) .verify(&their_sig, &verify_config)
.await .await
.web3_context("verifying signature against our local message") .web3_context("verifying signature against our local message")?;
{
// verification method 1 failed. try eip191
if let Err(err_191) = our_msg
.verify_eip191(&their_sig)
.web3_context("verifying eip191 signature against our local message")
{
let db_conn = app
.db_conn()
.web3_context("deleting expired pending logins requires a db")?;
// delete ALL expired rows.
let now = Utc::now();
let delete_result = pending_login::Entity::delete_many()
.filter(pending_login::Column::ExpiresAt.lte(now))
.exec(&db_conn)
.await?;
// TODO: emit a stat? if this is high something weird might be happening
trace!("cleared expired pending_logins: {:?}", delete_result);
return Err(Web3ProxyError::EipVerificationFailed(
Box::new(err_1),
Box::new(err_191),
));
}
}
// TODO: limit columns or load whole user? // TODO: limit columns or load whole user?
let caller = user::Entity::find() let caller = user::Entity::find()
@ -356,11 +336,7 @@ pub async fn user_login_post(
.one(&txn) .one(&txn)
.await? .await?
.ok_or(Web3ProxyError::BadRequest( .ok_or(Web3ProxyError::BadRequest(
format!( "The referral_link you provided does not exist".into(),
"The referral_link you provided does not exist {}",
referral_code
)
.into(),
))?; ))?;
// Create a new item in the database, // Create a new item in the database,
@ -430,7 +406,7 @@ pub async fn user_login_post(
.delete(&db_conn) .delete(&db_conn)
.await .await
{ {
warn!("Failed to delete nonce:{}: {}", login_nonce.0, err); error!("Failed to delete nonce:{}: {}", login_nonce, err);
} }
Ok(response) Ok(response)

View File

@ -1,6 +1,7 @@
use axum::headers::authorization::Bearer; use axum::headers::authorization::Bearer;
use migration::sea_orm::prelude::Uuid; use migration::sea_orm::prelude::Uuid;
use serde::Serialize; use serde::Serialize;
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use ulid::Ulid; use ulid::Ulid;
@ -47,6 +48,12 @@ impl From<UserBearerToken> for Uuid {
} }
} }
impl fmt::Display for UserBearerToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl TryFrom<Bearer> for UserBearerToken { impl TryFrom<Bearer> for UserBearerToken {
type Error = ulid::DecodeError; type Error = ulid::DecodeError;