@di-zed/yandex-smart-home
Version:
The Yandex Smart Home skills for the different device types.
238 lines (237 loc) • 8.76 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const appError_1 = __importDefault(require("../errors/appError"));
const configProvider_1 = __importDefault(require("../providers/configProvider"));
const clientRepository_1 = __importDefault(require("../repositories/clientRepository"));
const userRepository_1 = __importDefault(require("../repositories/userRepository"));
/**
* Authorization Controller.
*/
class AuthController {
/**
* Protect Middleware.
*
* @param req
* @param res
* @param next
* @returns void
*/
protect(req, res, next) {
return __awaiter(this, void 0, void 0, function* () {
let token = '';
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
token = req.headers.authorization.split(' ')[1];
}
if (!token) {
return next(new appError_1.default(res.__('You are not logged in! Please log in to get access.'), 401));
}
try {
const tokenData = yield this.verifyToken(token);
req.currentClient = yield clientRepository_1.default.getClientById(tokenData.appId);
req.currentUser = yield userRepository_1.default.getUserById(tokenData.userId);
}
catch (err) {
return next(new appError_1.default(res.__('You are not logged in! Please log in to get access.'), 401));
}
return next();
});
}
/**
* GET Method.
* Get Login Form.
* https://yandex.ru/dev/dialogs/alice/doc/auth/how-it-works.html?lang=en
*
* @param req
* @param res
* @param next
* @returns void
*/
login(req, res, next) {
return __awaiter(this, void 0, void 0, function* () {
const authParams = this.getAuthParams(req.query);
if (!this.isAuthRequestValid(authParams)) {
return next(new appError_1.default(res.__('Invalid input data.'), 400));
}
try {
yield clientRepository_1.default.getClientByClientId(authParams.client_id);
}
catch (err) {
return next(new appError_1.default(res.__('This client does not exist.'), 404));
}
const viewAuthLogin = configProvider_1.default.getConfigOption('viewAuthLogin');
return res.status(200).render(viewAuthLogin || 'auth/login', {
title: res.__('Log into your account'),
params: authParams,
error: req.query.error || '',
});
});
}
/**
* POST Method.
* Authorization Action.
* https://yandex.ru/dev/dialogs/alice/doc/auth/how-it-works.html?lang=en#authorization
*
* @param req
* @param res
* @param next
* @returns void
*/
loginPost(req, res, next) {
return __awaiter(this, void 0, void 0, function* () {
const authParams = this.getAuthParams(req.body);
if (!this.isAuthRequestValid(authParams)) {
return next(new appError_1.default(res.__('Invalid input data.'), 400));
}
const { email, password } = req.body;
const loginUrl = '/auth/login?' + new URLSearchParams(authParams).toString();
if (!email || !password) {
return res.redirect(loginUrl + '&error=email');
}
let client;
let user;
try {
client = yield clientRepository_1.default.getClientByClientId(authParams.client_id);
}
catch (err) {
return next(new appError_1.default(res.__('This client does not exist.'), 404));
}
try {
user = yield userRepository_1.default.getUserByEmailAndPassword(email, password);
}
catch (err) {
return res.redirect(loginUrl + '&error=email');
}
return res.status(301).redirect(authParams.redirect_uri +
'?' +
new URLSearchParams({
code: this.signToken(client.id, user.id, '2 days'),
state: authParams.state,
client_id: authParams.client_id,
scope: authParams.scope,
}).toString());
});
}
/**
* POST Method.
* Get Token Information.
* https://yandex.ru/dev/dialogs/alice/doc/auth/how-it-works.html?lang=en
*
* This example shows getting an OAuth token in the web service.
* https://yandex.ru/dev/direct/doc/examples-v5/php5-file_get_contents-token.html?lang=en
*
* @param req
* @param res
* @param next
* @returns Response
*/
token(req, res, next) {
return __awaiter(this, void 0, void 0, function* () {
const code = req.body.code;
if (!code) {
return next(new appError_1.default(res.__('The parameter "%s" is required.', 'code'), 400));
}
try {
const codeData = yield this.verifyToken(code);
const client = yield clientRepository_1.default.getClientById(codeData.appId);
const user = yield userRepository_1.default.getUserById(codeData.userId);
return res.status(200).json({
access_token: this.signToken(client.id, user.id, '365 days'),
token_type: 'bearer',
expires_in: 60 * 60 * 24 * 365,
});
}
catch (err) {
return next(new appError_1.default(res.__('Page Not Found.'), 404));
}
});
}
/**
* Get Auth Parameters.
*
* @param data
* @returns AuthParams
* @protected
*/
getAuthParams(data) {
return {
state: data.state || '',
redirect_uri: data.redirect_uri || '',
response_type: data.response_type || '',
client_id: data.client_id || '',
scope: data.scope || '',
};
}
/**
* Is the Authentication Request valid?
*
* @param authParams
* @returns boolean
* @protected
*/
isAuthRequestValid(authParams) {
let isValid = true;
const requiredFields = ['state', 'redirect_uri', 'response_type', 'client_id'];
const authParamKeys = Object.keys(authParams);
authParamKeys.forEach((key) => {
const value = authParams[key];
if (requiredFields.includes(key) && !value) {
isValid = false;
}
});
if (!authParams.redirect_uri.startsWith(process.env.YANDEX_DIALOG_URI)) {
isValid = false;
}
return isValid;
}
/**
* Sign Token.
*
* @param appId
* @param userId
* @param expiresIn
* @returns string
* @protected
*/
signToken(appId, userId, expiresIn) {
const tokenData = {
appId: appId,
userId: userId,
};
return jsonwebtoken_1.default.sign(tokenData, process.env.JWT_SECRET, {
expiresIn: expiresIn,
});
}
/**
* Verify Token.
*
* @param token
* @returns Promise<TokenData>
* @protected
*/
verifyToken(token) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET, {}, (err, payload) => {
if (err) {
return reject(err);
}
return resolve(payload);
});
});
});
}
}
exports.default = AuthController;