Skip to content

Commit fa0e2f4

Browse files
committed
Support the deprecated 'user' field on the compat /login endpoint
1 parent 84766e3 commit fa0e2f4

File tree

1 file changed

+65
-5
lines changed

1 file changed

+65
-5
lines changed

crates/handlers/src/compat/login.rs

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
// SPDX-License-Identifier: AGPL-3.0-only
55
// Please see LICENSE in the repository root for full details.
66

7-
use axum::{extract::State, response::IntoResponse, Json};
7+
use axum::{
8+
extract::{rejection::JsonRejection, State},
9+
response::IntoResponse,
10+
Json,
11+
};
812
use axum_extra::typed_header::TypedHeader;
913
use chrono::Duration;
1014
use hyper::StatusCode;
@@ -104,7 +108,9 @@ pub struct RequestBody {
104108
pub enum Credentials {
105109
#[serde(rename = "m.login.password")]
106110
Password {
107-
identifier: Identifier,
111+
identifier: Option<Identifier>,
112+
// This property has been deprecated for a while, but some tools still use it.
113+
user: Option<String>,
108114
password: String,
109115
},
110116

@@ -145,6 +151,12 @@ pub enum RouteError {
145151
#[error("unsupported login method")]
146152
Unsupported,
147153

154+
#[error("unsupported identifier type")]
155+
UnsupportedIdentifier,
156+
157+
#[error("missing property 'identifier'")]
158+
MissingIdentifier,
159+
148160
#[error("user not found")]
149161
UserNotFound,
150162

@@ -166,6 +178,9 @@ pub enum RouteError {
166178
#[error("invalid login token")]
167179
InvalidLoginToken,
168180

181+
#[error(transparent)]
182+
InvalidJsonBody(#[from] JsonRejection),
183+
169184
#[error("failed to provision device")]
170185
ProvisionDeviceFailed(#[source] anyhow::Error),
171186
}
@@ -188,11 +203,41 @@ impl IntoResponse for RouteError {
188203
error: "Too many login attempts",
189204
status: StatusCode::TOO_MANY_REQUESTS,
190205
},
206+
Self::InvalidJsonBody(JsonRejection::MissingJsonContentType(_)) => MatrixError {
207+
errcode: "M_NOT_JSON",
208+
error: "Invalid Content-Type header: expected application/json",
209+
status: StatusCode::BAD_REQUEST,
210+
},
211+
Self::InvalidJsonBody(JsonRejection::JsonSyntaxError(_)) => MatrixError {
212+
errcode: "M_NOT_JSON",
213+
error: "Body is not a valid JSON document",
214+
status: StatusCode::BAD_REQUEST,
215+
},
216+
Self::InvalidJsonBody(JsonRejection::JsonDataError(_)) => MatrixError {
217+
errcode: "M_BAD_JSON",
218+
error: "JSON fields are not valid",
219+
status: StatusCode::BAD_REQUEST,
220+
},
221+
Self::InvalidJsonBody(_) => MatrixError {
222+
errcode: "M_UNKNOWN",
223+
error: "Unknown error while parsing JSON body",
224+
status: StatusCode::BAD_REQUEST,
225+
},
191226
Self::Unsupported => MatrixError {
192-
errcode: "M_UNRECOGNIZED",
227+
errcode: "M_UNKNOWN",
193228
error: "Invalid login type",
194229
status: StatusCode::BAD_REQUEST,
195230
},
231+
Self::UnsupportedIdentifier => MatrixError {
232+
errcode: "M_UNKNOWN",
233+
error: "Unsupported login identifier",
234+
status: StatusCode::BAD_REQUEST,
235+
},
236+
Self::MissingIdentifier => MatrixError {
237+
errcode: "M_BAD_JSON",
238+
error: "Missing property 'identifier",
239+
status: StatusCode::BAD_REQUEST,
240+
},
196241
Self::UserNotFound | Self::NoPassword | Self::PasswordVerificationFailed(_) => {
197242
MatrixError {
198243
errcode: "M_FORBIDDEN",
@@ -228,17 +273,32 @@ pub(crate) async fn post(
228273
State(limiter): State<Limiter>,
229274
requester: RequesterFingerprint,
230275
user_agent: Option<TypedHeader<headers::UserAgent>>,
231-
Json(input): Json<RequestBody>,
276+
input: Result<Json<RequestBody>, JsonRejection>,
232277
) -> Result<impl IntoResponse, RouteError> {
278+
let Json(input) = input?;
233279
let user_agent = user_agent.map(|ua| UserAgent::parse(ua.as_str().to_owned()));
234280
let (mut session, user) = match (password_manager.is_enabled(), input.credentials) {
235281
(
236282
true,
237283
Credentials::Password {
238-
identifier: Identifier::User { user },
284+
identifier,
285+
user,
239286
password,
240287
},
241288
) => {
289+
// This is to support both the (very) old and deprecated 'user' property, with
290+
// the same behavior as Synapse: it takes precendence over the 'identifier' if
291+
// provided
292+
let user = match (identifier, user) {
293+
(Some(Identifier::User { user }), None) | (_, Some(user)) => user,
294+
(Some(Identifier::Unsupported), None) => {
295+
return Err(RouteError::UnsupportedIdentifier);
296+
}
297+
(None, None) => {
298+
return Err(RouteError::MissingIdentifier);
299+
}
300+
};
301+
242302
user_password_login(
243303
&mut rng,
244304
&clock,

0 commit comments

Comments
 (0)