You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
3.9 KiB
JavaScript
133 lines
3.9 KiB
JavaScript
"use strict";
|
|
|
|
const Promise = require("bluebird");
|
|
const scryptForHumans = require("scrypt-for-humans");
|
|
|
|
const errors = require("../../../errors");
|
|
const normalize = require("../../../middlewares/normalize-payload");
|
|
const validate = require("../../../middlewares/validate-payload");
|
|
const processDeviceId = require("../../../process-device-id");
|
|
const matchValue = require("../../../match-value");
|
|
const canonicalizePhoneNumber = require("../../../canonicalize-phone-number");
|
|
const validateUserIdentifier = require("../../../validate/user-identifier");
|
|
|
|
function normalizeIdentifier(payload) {
|
|
if (payload.identifier != null) {
|
|
/* Never overwrite the `identifier` key if it already exists */
|
|
return null;
|
|
} else if (payload.user != null) {
|
|
return {
|
|
identifier: {
|
|
type: "m.id.user",
|
|
user: payload.user
|
|
}
|
|
};
|
|
} else if (payload.address != null) {
|
|
return {
|
|
identifier: {
|
|
type: "m.id.thirdparty",
|
|
medium: payload.medium,
|
|
address: payload.address
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
function normalizePhoneNumber(payload) {
|
|
if (payload.identifier != null && payload.identifier.type === "m.id.phone") {
|
|
return {
|
|
identifier: {
|
|
type: "m.id.thirdparty",
|
|
medium: "msisdn",
|
|
address: canonicalizePhoneNumber(payload.identifier.country, payload.identifier.phone)
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = function(state) {
|
|
let {db, configuration, fullyQualifyUser} = state;
|
|
|
|
let router = require("express-promise-router")();
|
|
|
|
router.get("/r0/login", (req, res) => {
|
|
res.json({
|
|
flows: [{ type: "m.login.password" }]
|
|
});
|
|
});
|
|
|
|
function validateLoginPayload(payload) {
|
|
let {assertProperties, isPresent, isString, isOneOf} = require("../../../validator-lib");
|
|
|
|
assertProperties(payload, {
|
|
type: [ isPresent, isOneOf("m.login.password", "m.login.token") ],
|
|
device_id: [ isString ],
|
|
initial_device_display_name: [ isString ],
|
|
});
|
|
|
|
matchValue(payload.type, {
|
|
"m.login.password": () => {
|
|
assertProperties(payload, {
|
|
identifier: [ isPresent, validateUserIdentifier ],
|
|
password: [ isPresent, isString ]
|
|
});
|
|
},
|
|
"m.login.token": () => {
|
|
assertProperties(payload, {
|
|
token: [ isPresent, isString ]
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
router.post("/r0/login", normalize([normalizeIdentifier, normalizePhoneNumber]), validate(validateLoginPayload), (req, res) => {
|
|
// https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-login
|
|
// Authenticates the user, and issues an access token they can use to authorize themself in subsequent requests.
|
|
|
|
/* FIXME: Rate-limit */
|
|
|
|
return Promise.try(() => {
|
|
if (req.body.identifier.type === "m.id.user") {
|
|
return db.accounts.byUsername({
|
|
username: req.body.identifier.user
|
|
});
|
|
} else if (req.body.identifier.type === "m.id.thirdparty") {
|
|
// FIXME
|
|
} else {
|
|
/* NOTE: We're not handling m.id.phone here, as that's normalized into m.id.thirdparty in normalizePhoneNumber */
|
|
throw new errors.Unreachable("Invalid identifier type");
|
|
}
|
|
}).then((user) => {
|
|
let deviceId = processDeviceId(req.body.device_id);
|
|
|
|
return Promise.try(() => {
|
|
if (req.body.type === "m.login.password") {
|
|
return scryptForHumans.verifyHash(req.body.password, user.password_hash);
|
|
} else if (req.body.type === "m.login.token") {
|
|
// FIXME
|
|
} else {
|
|
throw new errors.Unreachable("Invalid login type");
|
|
}
|
|
}).then(() => {
|
|
return db.devices.createDeviceSession({
|
|
name: req.body.initial_device_display_name,
|
|
deviceId: deviceId,
|
|
userId: user.id
|
|
}).then((device) => {
|
|
res.send({
|
|
access_token: device.token,
|
|
user_id: fullyQualifyUser(user.username),
|
|
home_server: configuration.hostname,
|
|
device_id: device.device_id
|
|
});
|
|
});
|
|
});
|
|
}).catch(errors.InvalidUsername, (error) => {
|
|
throw errors.Forbidden.chain(error, "Invalid username specified");
|
|
}).catch(scryptForHumans.PasswordError, (error) => {
|
|
throw errors.Forbidden.chain(error, "Invalid password specified");
|
|
});
|
|
});
|
|
|
|
return router;
|
|
} |