mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-10-15 11:11:05 +00:00
Normalize the username server-side when registering
`POST /register` trims and lowercases the username before either the password or the upstream provider path looks at it, since Matrix localparts are lowercase.
This commit is contained in:
@@ -177,13 +177,17 @@ pub(crate) async fn post(
|
||||
) -> Result<Response, InternalError> {
|
||||
let user_agent = user_agent.map(|ua| ua.as_str().to_owned());
|
||||
|
||||
let Ok(form) = cookie_jar.verify_form(&clock, form) else {
|
||||
let Ok(mut form) = cookie_jar.verify_form(&clock, form) else {
|
||||
// An invalid token most likely comes from a page left open past the CSRF token lifetime
|
||||
tracing::debug!("Invalid CSRF token on the registration form, redirecting to a fresh one");
|
||||
let destination = Register::from(query.post_auth_action);
|
||||
return Ok((cookie_jar, url_builder.redirect(&destination)).into_response());
|
||||
};
|
||||
|
||||
// Matrix localparts are lowercase, so a stray capital or surrounding space
|
||||
// isn't worth an error
|
||||
form.username = form.username.trim().to_lowercase();
|
||||
|
||||
// Carry the username along so we can prefill it if the user gets to pick
|
||||
// one when they come back
|
||||
if let Some(provider_id) = &form.provider {
|
||||
@@ -201,9 +205,8 @@ pub(crate) async fn post(
|
||||
};
|
||||
|
||||
// The username is carried in a cookie, so its size has to be bounded
|
||||
let username = form.username.trim();
|
||||
let username = if username.len() <= MAX_USERNAME_LENGTH {
|
||||
username
|
||||
let username = if form.username.len() <= MAX_USERNAME_LENGTH {
|
||||
form.username.as_str()
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
@@ -364,6 +364,55 @@ mod tests {
|
||||
},
|
||||
};
|
||||
|
||||
/// The username is trimmed and lowercased before anything checks it
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_register_normalizes_username(pool: PgPool) {
|
||||
setup();
|
||||
let state = TestState::from_pool(pool).await.unwrap();
|
||||
let cookies = CookieHelper::new();
|
||||
|
||||
let request = Request::get(&*mas_router::Register::default().path_and_query()).empty();
|
||||
let request = cookies.with_cookies(request);
|
||||
let response = state.request(request).await;
|
||||
cookies.save_cookies(&response);
|
||||
response.assert_status(StatusCode::OK);
|
||||
let csrf_token = response
|
||||
.body()
|
||||
.split("name=\"csrf\" value=\"")
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.split('\"')
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let request = Request::post(&*mas_router::Register::default().path_and_query()).form(
|
||||
serde_json::json!({
|
||||
"csrf": csrf_token,
|
||||
"username": " John ",
|
||||
"email": "john@example.com",
|
||||
"password": "correcthorsebatterystaple",
|
||||
"password_confirm": "correcthorsebatterystaple",
|
||||
"accept_terms": "on",
|
||||
}),
|
||||
);
|
||||
let request = cookies.with_cookies(request);
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::SEE_OTHER);
|
||||
let location = response.headers().get(LOCATION).unwrap();
|
||||
let id = location
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.rsplit('/')
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let mut repo = state.repository().await.unwrap();
|
||||
let registration = repo.user_registration().lookup(id).await.unwrap().unwrap();
|
||||
assert_eq!(registration.username, "john");
|
||||
}
|
||||
|
||||
/// Test the registration happy path
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_register(pool: PgPool) {
|
||||
|
||||
Reference in New Issue
Block a user