oauth authorize: if use has totp enable, generate a code, store in a cookie and redirect to totp verification page

This commit is contained in:
Philippe Loctaux 2023-05-01 12:00:11 +02:00
parent fbbcb4e182
commit fd2d2672bb

View file

@ -2,8 +2,11 @@ use crate::routes::prelude::*;
use apps::App;
use authorization_codes::AuthorizationCode;
use hash::SecretString;
use rocket::http::{Cookie, CookieJar, SameSite};
use rocket::time::Duration;
use rocket::{get, post};
use settings::Settings;
use users::totp_login_request::{TOTP_REQUEST_COOKIE_NAME, TOTP_REQUEST_LEN};
use users::User;
#[get("/oauth/authorize?<auth_request..>", rank = 1)]
@ -90,12 +93,15 @@ fn user_archived(login: &str, request: AuthenticationRequest) -> Flash<Redirect>
flash(format!("User {login} is archived"), request)
}
pub const AUTHORIZATION_CODE_LEN: usize = 35;
#[post("/oauth/authorize?<auth_request..>", data = "<form>")]
pub async fn authorize_form(
form: Option<Form<Authorize<'_>>>,
user: Option<JwtUser>,
mut db: Connection<Database>,
auth_request: AuthenticationRequest<'_>,
cookie_jar: &CookieJar<'_>,
) -> Result<Either<Redirect, Flash<Redirect>>> {
let mut transaction = db.begin().await?;
@ -109,11 +115,11 @@ pub async fn authorize_form(
)
.await?;
let user_id = match user {
let (user_id, verify_totp) = match user {
Some(user) => {
transaction.commit().await?;
UserID(user.0.subject)
(UserID(user.0.subject), false)
}
None => {
let form = match form {
@ -152,35 +158,72 @@ pub async fn authorize_form(
return Ok(Either::Right(invalid_credentials(form.login, auth_request)));
}
user.id().to_owned()
(user.id().to_owned(), user.totp_secret().is_some())
}
};
if verify_totp {
// Generate totp token
let totp_token = task::spawn_blocking(|| SecretString::new(TOTP_REQUEST_LEN))
.await
.map_err(|e| e.to_string())?;
let totp_duration = 15;
// Save in database
let mut transaction = db.begin().await?;
users::totp_login_request::TotpLoginRequest::insert(
&mut transaction,
totp_token.as_ref(),
&user_id,
totp_duration,
)
.await?;
transaction.commit().await?;
// Store totp token as a cookie
let mut cookie = Cookie::new(TOTP_REQUEST_COOKIE_NAME, totp_token.to_string());
cookie.set_secure(true);
cookie.set_http_only(true);
cookie.set_same_site(SameSite::Strict);
cookie.set_max_age(Duration::minutes(totp_duration));
cookie_jar.add(cookie);
// Redirect to totp verification page
return Ok(Either::Left(Redirect::to(uri!(
crate::routes::oauth::totp_page(auth_request)
))));
}
// Generate authorization code
let code = task::spawn_blocking(|| SecretString::new(35)).await?;
let code = task::spawn_blocking(|| SecretString::new(AUTHORIZATION_CODE_LEN)).await?;
// Save authorization code
let mut transaction = db.begin().await?;
AuthorizationCode::insert(&mut transaction, code.as_ref(), app.id(), &user_id).await?;
transaction.commit().await?;
// Construct uri to redirect to
let uri = {
let uri_mode = match auth_request.response_mode.unwrap_or(ResponseMode::Query) {
ResponseMode::Query => "?",
ResponseMode::Fragment => "#",
};
// Redirect to oauth redirect uri
Ok(Either::Left(Redirect::found(redirect_uri(
auth_request,
&app,
&code,
))))
}
// Redirect + authorization code
let uri = format!("{}{}code={}", app.redirect_uri(), uri_mode, code.as_ref());
// Add state if present
if auth_request.state.is_empty() {
uri
} else {
format!("{}&state={}", uri, auth_request.state)
}
pub fn redirect_uri(auth_request: AuthenticationRequest, app: &App, code: &SecretString) -> String {
let uri_mode = match auth_request.response_mode.unwrap_or(ResponseMode::Query) {
ResponseMode::Query => "?",
ResponseMode::Fragment => "#",
};
Ok(Either::Left(Redirect::found(uri)))
// Redirect + authorization code
let uri = format!("{}{}code={}", app.redirect_uri(), uri_mode, code.as_ref());
// Add state if present
if auth_request.state.is_empty() {
uri
} else {
format!("{}&state={}", uri, auth_request.state)
}
}