完成 Passkey 登录
This commit is contained in:
parent
75e616201b
commit
2b7932dd49
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -886,6 +886,12 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-range-header"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@ -1080,6 +1086,16 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minijinja"
|
name = "minijinja"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
@ -2214,10 +2230,18 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.1",
|
"bitflags 2.4.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
|
"http-range-header",
|
||||||
|
"httpdate",
|
||||||
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -2354,6 +2378,15 @@ version = "1.17.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.13"
|
version = "0.3.13"
|
||||||
|
|||||||
@ -14,7 +14,7 @@ serde = "1.0.190"
|
|||||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
||||||
totp-rs = "5.4.0"
|
totp-rs = "5.4.0"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||||
tower-http = { version = "0.5", features = ["trace"] }
|
tower-http = { version = "0.5", features = ["trace", "fs"] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
askama = "0.10"
|
askama = "0.10"
|
||||||
minijinja = "1.0.9"
|
minijinja = "1.0.9"
|
||||||
|
|||||||
@ -5,13 +5,14 @@ upstream protected {
|
|||||||
server {
|
server {
|
||||||
listen 8080;
|
listen 8080;
|
||||||
location / {
|
location / {
|
||||||
auth_request /auth;
|
auth_request /aaron/auth;
|
||||||
set $original_full_url $scheme://$host$request_uri;
|
set $original_full_url $scheme://$host$request_uri;
|
||||||
error_page 401 =200 /login;
|
error_page 401 =200 /login;
|
||||||
proxy_set_header X-Original-URI $scheme://$host$request_uri;
|
proxy_set_header X-Original-URI $scheme://$host$request_uri;
|
||||||
proxy_pass http://protected/;
|
proxy_pass http://protected/;
|
||||||
}
|
}
|
||||||
location = /auth {
|
location
|
||||||
|
location = /aaron/auth {
|
||||||
internal;
|
internal;
|
||||||
proxy_pass http://localhost:3000/auth;
|
proxy_pass http://localhost:3000/auth;
|
||||||
proxy_pass_request_body off;
|
proxy_pass_request_body off;
|
||||||
@ -20,7 +21,7 @@ server {
|
|||||||
proxy_set_header X-Original-Remote-Addr $remote_addr;
|
proxy_set_header X-Original-Remote-Addr $remote_addr;
|
||||||
proxy_set_header X-Original-Host $host;
|
proxy_set_header X-Original-Host $host;
|
||||||
}
|
}
|
||||||
location /login {
|
location /aaron/login {
|
||||||
proxy_pass http://localhost:3000/login;
|
proxy_pass http://localhost:3000/login;
|
||||||
proxy_set_header X-Original-Remote-Addr $remote_addr;
|
proxy_set_header X-Original-Remote-Addr $remote_addr;
|
||||||
proxy_set_header X-Original-Host $host;
|
proxy_set_header X-Original-Host $host;
|
||||||
|
|||||||
@ -63,13 +63,15 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Login</h1>
|
<h1>Login</h1>
|
||||||
<form action="/login{{ url }}" method="POST">
|
<form action="{{ home_url|safe }}/login{{ url }}" method="POST">
|
||||||
<label for="username">Username:</label>
|
<label for="username">Username:</label>
|
||||||
<input type="text" id="username" name="username" required>
|
<input type="text" id="username" name="username" required>
|
||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
<input type="password" id="password" name="otp" required>
|
<input type="password" id="password" name="otp" required>
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
|
<p>Or:</p>
|
||||||
|
<a href="{{ home_url|safe }}/register">Continue with Passkey</a>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
137
register.html
Normal file
137
register.html
Normal file
File diff suppressed because one or more lines are too long
@ -11,7 +11,11 @@ pub const SESSION_ACTIVE_TIME: Lazy<u64> = Lazy::new(|| {
|
|||||||
pub const COOKIE_DOMAIN: Lazy<String> =
|
pub const COOKIE_DOMAIN: Lazy<String> =
|
||||||
Lazy::new(|| env::var("DOMAIN").ok().unwrap_or(".aaronhu.cn".to_owned()));
|
Lazy::new(|| env::var("DOMAIN").ok().unwrap_or(".aaronhu.cn".to_owned()));
|
||||||
pub const LOGIN_PAGE_HTML: &str = include_str!("../loginpage.html");
|
pub const LOGIN_PAGE_HTML: &str = include_str!("../loginpage.html");
|
||||||
|
pub const REGISTER_PAGE_HTML: &str = include_str!("../register.html");
|
||||||
pub const PORT: Lazy<String> = Lazy::new(|| env::var("PORT").unwrap_or("3000".to_string()));
|
pub const PORT: Lazy<String> = Lazy::new(|| env::var("PORT").unwrap_or("3000".to_string()));
|
||||||
|
// 部署此应用的URL,默认为/aaron
|
||||||
|
pub const HOME_URL: Lazy<String> =
|
||||||
|
Lazy::new(|| env::var("HOME_URL").unwrap_or("/aaron".to_owned()));
|
||||||
pub const DATABASE_URL: Lazy<String> =
|
pub const DATABASE_URL: Lazy<String> =
|
||||||
Lazy::new(|| env::var("DATABASE_URL").unwrap_or("".to_owned()));
|
Lazy::new(|| env::var("DATABASE_URL").unwrap_or("".to_owned()));
|
||||||
pub const RP_ID: Lazy<String> =
|
pub const RP_ID: Lazy<String> =
|
||||||
|
|||||||
@ -29,8 +29,8 @@ impl ServerState {
|
|||||||
// Now, with the builder you can define other options.
|
// Now, with the builder you can define other options.
|
||||||
// Set a "nice" relying party name. Has no security properties and
|
// Set a "nice" relying party name. Has no security properties and
|
||||||
// may be changed in the future.
|
// may be changed in the future.
|
||||||
let _RP_NAME = RP_NAME.to_string();
|
let _rp_name = RP_NAME.to_string();
|
||||||
let builder = builder.rp_name(&_RP_NAME);
|
let builder = builder.rp_name(&_rp_name);
|
||||||
|
|
||||||
// Consume the builder and create our webauthn instance.
|
// Consume the builder and create our webauthn instance.
|
||||||
let webauthn = Arc::new(builder.build().expect("Invalid configuration"));
|
let webauthn = Arc::new(builder.build().expect("Invalid configuration"));
|
||||||
|
|||||||
18
src/main.rs
18
src/main.rs
@ -1,22 +1,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
use axum::{routing::{get, post, Route}, Router};
|
use axum::{routing::{get, get_service, post, Route}, Router};
|
||||||
use entities::*;
|
use entities::*;
|
||||||
|
|
||||||
use services::{auth::{self, finish_authentication, start_authentication}, gc_task, login, login_page};
|
use services::{auth::{self, finish_authentication, start_authentication}, gc_task, login, login_page, register_page};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower_cookies::{CookieManagerLayer};
|
use tower_cookies::CookieManagerLayer;
|
||||||
use tower_http::trace::{self, TraceLayer};
|
use tower_http::trace::{self, TraceLayer};
|
||||||
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
|
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};
|
||||||
|
use tower_http::services::{ServeDir, ServeFile};
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod controllers;
|
pub mod controllers;
|
||||||
pub mod entities;
|
pub mod entities;
|
||||||
pub mod services;
|
pub mod services;
|
||||||
use config::{PORT};
|
use config::{HOME_URL, PORT};
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// 初始化日志记录器
|
// 初始化日志记录器
|
||||||
@ -29,11 +30,11 @@ async fn main() {
|
|||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/auth", get(crate::services::auth_otp)) // http://127.0.0.1:3000
|
.route("/auth", get(crate::services::auth_otp)) // http://127.0.0.1:3000
|
||||||
.route("/login", get(login_page).post(login))
|
.route("/login", get(login_page).post(login))
|
||||||
|
.route("/register", get(register_page))
|
||||||
.route("/register_start/:username", post(auth::start_register))
|
.route("/register_start/:username", post(auth::start_register))
|
||||||
.route("/register_finish/:username", post(auth::finish_register))
|
.route("/register_finish", post(auth::finish_register))
|
||||||
.route("/webauthn_login_start/:username", post(start_authentication))
|
.route("/webauthn_login_start/:username", post(start_authentication))
|
||||||
.route("/webauthn_login_finish/:username", post(finish_authentication))
|
.route("/webauthn_login_finish", post(finish_authentication))
|
||||||
|
|
||||||
.layer(
|
.layer(
|
||||||
TraceLayer::new_for_http()
|
TraceLayer::new_for_http()
|
||||||
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
|
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
|
||||||
@ -42,7 +43,8 @@ async fn main() {
|
|||||||
.with_state(state.clone())
|
.with_state(state.clone())
|
||||||
.layer(CookieManagerLayer::new())
|
.layer(CookieManagerLayer::new())
|
||||||
.layer(session_layer);
|
.layer(session_layer);
|
||||||
let aaronhu = Router::new().nest("/aaron/", app);
|
// 嵌套防止撞路由
|
||||||
|
let aaronhu = Router::new().nest(&HOME_URL, app);
|
||||||
let port = PORT.parse::<u16>().unwrap_or(3000);
|
let port = PORT.parse::<u16>().unwrap_or(3000);
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
||||||
// run it with hyper on localhost:3000
|
// run it with hyper on localhost:3000
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use axum::{
|
|||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tower_cookies::Cookies;
|
use tower_cookies::{Cookie, Cookies};
|
||||||
use tower_sessions::Session;
|
use tower_sessions::Session;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ use tracing::*;
|
|||||||
// 1. Import the prelude - this contains everything needed for the server to function.
|
// 1. Import the prelude - this contains everything needed for the server to function.
|
||||||
use webauthn_rs::prelude::*;
|
use webauthn_rs::prelude::*;
|
||||||
|
|
||||||
use crate::config::{COOKIE_NAME, SESSION_ACTIVE_TIME};
|
use crate::config::{COOKIE_DOMAIN, COOKIE_NAME, SESSION_ACTIVE_TIME};
|
||||||
use crate::ServerState;
|
use crate::ServerState;
|
||||||
|
|
||||||
// 2. The first step a client (user) will carry out is requesting a credential to be
|
// 2. The first step a client (user) will carry out is requesting a credential to be
|
||||||
@ -71,25 +71,29 @@ fn auth_user(cookie_content:String,session_table:&mut HashMap<Uuid, Instant>) ->
|
|||||||
if *expire <= Instant::now() {
|
if *expire <= Instant::now() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
session_table.insert(uuid, Instant::now()+Duration::from_secs(*SESSION_ACTIVE_TIME));
|
session_table.insert(
|
||||||
|
uuid,
|
||||||
|
Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME),
|
||||||
|
);
|
||||||
tracing::info!("valid cookie {}", uuid);
|
tracing::info!("valid cookie {}", uuid);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
#[debug_handler]
|
|
||||||
pub async fn start_register(
|
pub async fn start_register(
|
||||||
State(state): State<Arc<ServerState>>,
|
State(state): State<Arc<ServerState>>,
|
||||||
cookies: Cookies,
|
cookies: Cookies,
|
||||||
session: Session,
|
session: Session,
|
||||||
Path(username): Path<String>,
|
Path(username): Path<String>,
|
||||||
) -> Result<Json<CreationChallengeResponse>, String> {
|
) -> Result<Json<CreationChallengeResponse>, String> {
|
||||||
tracing::info!("Start register");
|
tracing::info!("开始注册");
|
||||||
// todo!("Auth User");
|
// todo!("Auth User");
|
||||||
let Some(cookie_content) = cookies.get(&COOKIE_NAME) else {
|
let Some(cookie_content) = cookies.get(&COOKIE_NAME) else {
|
||||||
|
tracing::info!("用户没有Cookie");
|
||||||
return Err(WebauthnError::AuthenticationFailure.to_string());
|
return Err(WebauthnError::AuthenticationFailure.to_string());
|
||||||
};
|
};
|
||||||
let mut session_table = state.session.lock().await;
|
let mut session_table = state.session.lock().await;
|
||||||
if !auth_user(cookie_content.value().to_string(), &mut session_table) {
|
if !auth_user(cookie_content.value().to_string(), &mut session_table) {
|
||||||
|
tracing::info!("用户Cookie无效,不在Session表中");
|
||||||
return Err(WebauthnError::AuthenticationFailure.to_string());
|
return Err(WebauthnError::AuthenticationFailure.to_string());
|
||||||
}
|
}
|
||||||
// Remove any previous registrations that may have occurred from the session.
|
// Remove any previous registrations that may have occurred from the session.
|
||||||
@ -166,7 +170,7 @@ pub async fn finish_register(
|
|||||||
session: Session,
|
session: Session,
|
||||||
Json(reg): Json<RegisterPublicKeyCredential>,
|
Json(reg): Json<RegisterPublicKeyCredential>,
|
||||||
) -> Result<impl IntoResponse, String> {
|
) -> Result<impl IntoResponse, String> {
|
||||||
info!("Confirming registration....");
|
info!("完成注册...");
|
||||||
let pool = &state.db;
|
let pool = &state.db;
|
||||||
let Ok(Some((user_name, user_id, reg_state))) = session
|
let Ok(Some((user_name, user_id, reg_state))) = session
|
||||||
.get::<(String, Uuid, PasskeyRegistration)>("reg_state")
|
.get::<(String, Uuid, PasskeyRegistration)>("reg_state")
|
||||||
@ -175,18 +179,17 @@ pub async fn finish_register(
|
|||||||
return Err(WebauthnError::AuthenticationFailure.to_string()); //Corrupt Session
|
return Err(WebauthnError::AuthenticationFailure.to_string()); //Corrupt Session
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = session.remove::<(Uuid, PasskeyAuthentication)>("reg_state").await;
|
let _ = session
|
||||||
|
.remove::<(Uuid, PasskeyAuthentication)>("reg_state")
|
||||||
|
.await;
|
||||||
|
|
||||||
let res = match state.webauthn.finish_passkey_registration(®, ®_state) {
|
let res = match state.webauthn.finish_passkey_registration(®, ®_state) {
|
||||||
Ok(key) => {
|
Ok(key) => {
|
||||||
info!("Passkey is okay");
|
info!("Passkey 正常");
|
||||||
let uid = &user_id.to_string();
|
let uid = &user_id.to_string();
|
||||||
let username = &user_name.to_string();
|
let username = &user_name.to_string();
|
||||||
// Check if the user_id already exists
|
// Check if the user_id already exists
|
||||||
let record = sqlx::query!(
|
let record = sqlx::query!("SELECT COUNT(KEY) AS count FROM users WHERE KEY = $1;", uid)
|
||||||
"SELECT COUNT(KEY) AS count FROM users WHERE KEY = $1;",
|
|
||||||
uid
|
|
||||||
)
|
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
|
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
|
||||||
@ -329,6 +332,7 @@ pub async fn start_authentication(
|
|||||||
pub async fn finish_authentication(
|
pub async fn finish_authentication(
|
||||||
State(state): State<Arc<ServerState>>,
|
State(state): State<Arc<ServerState>>,
|
||||||
session: Session,
|
session: Session,
|
||||||
|
cookies: Cookies,
|
||||||
Json(auth): Json<PublicKeyCredential>,
|
Json(auth): Json<PublicKeyCredential>,
|
||||||
) -> Result<impl IntoResponse, String> {
|
) -> Result<impl IntoResponse, String> {
|
||||||
let pool = &state.db;
|
let pool = &state.db;
|
||||||
@ -383,10 +387,7 @@ pub async fn finish_authentication(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_name = sqlx::query!(
|
let user_name = sqlx::query!("SELECT NAME FROM users WHERE KEY = $1;", uid)
|
||||||
"SELECT NAME FROM users WHERE KEY = $1;",
|
|
||||||
uid
|
|
||||||
)
|
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
|
.map_err(|_| WebauthnError::AuthenticationFailure.to_string())?;
|
||||||
@ -399,7 +400,17 @@ pub async fn finish_authentication(
|
|||||||
.insert("user_name", user_name.NAME)
|
.insert("user_name", user_name.NAME)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| WebauthnError::InvalidUsername.to_string())?;
|
.map_err(|_| WebauthnError::InvalidUsername.to_string())?;
|
||||||
|
let mut lock = state.session.lock().await;
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
let expires = Instant::now() + Duration::from_secs(*SESSION_ACTIVE_TIME);
|
||||||
|
lock.insert(
|
||||||
|
uuid,
|
||||||
|
expires
|
||||||
|
);
|
||||||
|
let mut new_cookie = Cookie::new(COOKIE_NAME.to_string(), uuid.to_string());
|
||||||
|
new_cookie.set_domain(COOKIE_DOMAIN.to_string());
|
||||||
|
cookies.add(new_cookie);
|
||||||
|
info!("从passkey登录创建了新Session{},过期时间{}s后",uuid,*SESSION_ACTIVE_TIME);
|
||||||
StatusCode::OK
|
StatusCode::OK
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ use tower_cookies::{Cookie, Cookies};
|
|||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::config::{HOME_URL, REGISTER_PAGE_HTML};
|
||||||
use crate::{ServerState, UserLoginForm};
|
use crate::{ServerState, UserLoginForm};
|
||||||
use super::config::{COOKIE_DOMAIN, COOKIE_NAME, LOGIN_PAGE_HTML, SESSION_ACTIVE_TIME};
|
use super::config::{COOKIE_DOMAIN, COOKIE_NAME, LOGIN_PAGE_HTML, SESSION_ACTIVE_TIME};
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ pub async fn login_page(headers: HeaderMap<HeaderValue>) -> impl IntoResponse {
|
|||||||
let uri = "?original_url=".to_owned() + uri;
|
let uri = "?original_url=".to_owned() + uri;
|
||||||
return Html(
|
return Html(
|
||||||
template
|
template
|
||||||
.render(context! { url => uri })
|
.render(context! { url => uri, home_url => HOME_URL.to_string() })
|
||||||
.unwrap_or("Error".to_string()),
|
.unwrap_or("Error".to_string()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -122,7 +123,18 @@ pub async fn login_page(headers: HeaderMap<HeaderValue>) -> impl IntoResponse {
|
|||||||
}
|
}
|
||||||
Html(
|
Html(
|
||||||
template
|
template
|
||||||
.render(context! { url => String::new() })
|
.render(context! { url => String::new(), home_url => HOME_URL.to_string() })
|
||||||
|
.unwrap_or("Error".to_string()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub async fn register_page(headers: HeaderMap<HeaderValue>) -> impl IntoResponse {
|
||||||
|
tracing::info!("Headers: {:#?}", headers);
|
||||||
|
let mut env = Environment::new();
|
||||||
|
env.add_template("register.html", REGISTER_PAGE_HTML).unwrap();
|
||||||
|
let template = env.get_template("register.html").unwrap();
|
||||||
|
Html(
|
||||||
|
template
|
||||||
|
.render(context! { url => String::new(), home_url => HOME_URL.to_string() })
|
||||||
.unwrap_or("Error".to_string()),
|
.unwrap_or("Error".to_string()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -165,7 +177,7 @@ pub async fn login_with_passkey(
|
|||||||
let Ok(mut conn) = conn else {
|
let Ok(mut conn) = conn else {
|
||||||
return Err((StatusCode::BAD_GATEWAY, "db连接错误"));
|
return Err((StatusCode::BAD_GATEWAY, "db连接错误"));
|
||||||
};
|
};
|
||||||
tracing::info!("{:?}", &frm);
|
tracing::info!("开始使用passkey登陆{:?}", &frm);
|
||||||
let target = sqlx::query_as::<_, UserLoginForm>(
|
let target = sqlx::query_as::<_, UserLoginForm>(
|
||||||
r"
|
r"
|
||||||
SELECT NAME, KEY FROM USERS WHERE NAME = ?
|
SELECT NAME, KEY FROM USERS WHERE NAME = ?
|
||||||
@ -174,7 +186,7 @@ pub async fn login_with_passkey(
|
|||||||
.bind(frm.username)
|
.bind(frm.username)
|
||||||
.fetch_optional(&mut *conn)
|
.fetch_optional(&mut *conn)
|
||||||
.await;
|
.await;
|
||||||
tracing::info!("{:?}", &target);
|
tracing::info!("数据库返回 {:?}", &target);
|
||||||
|
|
||||||
if let Ok(Some(target)) = target {
|
if let Ok(Some(target)) = target {
|
||||||
if check_otp(target.otp, frm.otp) {
|
if check_otp(target.otp, frm.otp) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user