mirror of
https://github.com/lasthead0/yandex2mqtt.git
synced 2025-08-07 08:40:29 +03:00
214 lines
9.2 KiB
JavaScript
214 lines
9.2 KiB
JavaScript
'use strict';
|
|
|
|
const oauth2orize = require('@poziworld/oauth2orize');
|
|
const passport = require('passport');
|
|
const login = require('connect-ensure-login');
|
|
const db = require('../db');
|
|
const utils = require('../utils');
|
|
|
|
// Create OAuth 2.0 server
|
|
const server = oauth2orize.createServer();
|
|
|
|
// Register serialization and deserialization functions.
|
|
//
|
|
// When a client redirects a user to user authorization endpoint, an
|
|
// authorization transaction is initiated. To complete the transaction, the
|
|
// user must authenticate and approve the authorization request. Because this
|
|
// may involve multiple HTTP request/response exchanges, the transaction is
|
|
// stored in the session.
|
|
//
|
|
// An application must supply serialization functions, which determine how the
|
|
// client object is serialized into the session. Typically this will be a
|
|
// simple matter of serializing the client's ID, and deserializing by finding
|
|
// the client by ID from the database.
|
|
|
|
server.serializeClient((client, done) => done(null, client.id));
|
|
|
|
server.deserializeClient((id, done) => {
|
|
db.clients.findById(id, (error, client) => {
|
|
if (error) return done(error);
|
|
return done(null, client);
|
|
});
|
|
});
|
|
|
|
// Register supported grant types.
|
|
//
|
|
// OAuth 2.0 specifies a framework that allows users to grant client
|
|
// applications limited access to their protected resources. It does this
|
|
// through a process of the user granting access, and the client exchanging
|
|
// the grant for an access token.
|
|
|
|
// Grant authorization codes. The callback takes the `client` requesting
|
|
// authorization, the `redirectUri` (which is used as a verifier in the
|
|
// subsequent exchange), the authenticated `user` granting access, and
|
|
// their response, which contains approved scope, duration, etc. as parsed by
|
|
// the application. The application issues a code, which is bound to these
|
|
// values, and will be exchanged for an access token.
|
|
|
|
server.grant(oauth2orize.grant.code((client, redirectUri, user, ares, done) => {
|
|
const code = utils.getUid(16);
|
|
db.authorizationCodes.save(code, client.id, redirectUri, user.id, user.username, (error) => {
|
|
if (error) return done(error);
|
|
return done(null, code);
|
|
});
|
|
}));
|
|
|
|
// Grant implicit authorization. The callback takes the `client` requesting
|
|
// authorization, the authenticated `user` granting access, and
|
|
// their response, which contains approved scope, duration, etc. as parsed by
|
|
// the application. The application issues a token, which is bound to these
|
|
// values.
|
|
|
|
server.grant(oauth2orize.grant.token((client, user, ares, done) => {
|
|
const token = utils.getUid(256);
|
|
db.accessTokens.save(token, user.id, client.clientId, (error) => {
|
|
if (error) return done(error);
|
|
return done(null, token);
|
|
});
|
|
}));
|
|
|
|
// Exchange authorization codes for access tokens. The callback accepts the
|
|
// `client`, which is exchanging `code` and any `redirectUri` from the
|
|
// authorization request for verification. If these values are validated, the
|
|
// application issues an access token on behalf of the user who authorized the
|
|
// code. The issued access token response can include a refresh token and
|
|
// custom parameters by adding these to the `done()` call
|
|
|
|
server.exchange(oauth2orize.exchange.code((client, code, redirectUri, done) => {
|
|
db.authorizationCodes.find(code, (error, authCode) => {
|
|
if (error) return done(error);
|
|
if (client.id !== authCode.clientId) return done(null, false);
|
|
if (redirectUri !== authCode.redirectUri) return done(null, false);
|
|
|
|
const token = utils.getUid(256);
|
|
db.accessTokens.save(token, authCode.userId, authCode.clientId, (error) => {
|
|
if (error) return done(error);
|
|
// Add custom params, e.g. the username
|
|
let params = { username: authCode.userName };
|
|
// Call `done(err, accessToken, [refreshToken], [params])` to issue an access token
|
|
return done(null, token, null, params);
|
|
});
|
|
});
|
|
}));
|
|
|
|
// Exchange user id and password for access tokens. The callback accepts the
|
|
// `client`, which is exchanging the user's name and password from the
|
|
// authorization request for verification. If these values are validated, the
|
|
// application issues an access token on behalf of the user who authorized the code.
|
|
|
|
server.exchange(oauth2orize.exchange.password((client, username, password, scope, done) => {
|
|
// Validate the client
|
|
db.clients.findByClientId(client.clientId, (error, localClient) => {
|
|
if (error) return done(error);
|
|
if (!localClient) return done(null, false);
|
|
if (localClient.clientSecret !== client.clientSecret) return done(null, false);
|
|
// Validate the user
|
|
db.users.findByUsername(username, (error, user) => {
|
|
if (error) return done(error);
|
|
if (!user) return done(null, false);
|
|
if (password !== user.password) return done(null, false);
|
|
// Everything validated, return the token
|
|
const token = utils.getUid(256);
|
|
db.accessTokens.save(token, user.id, client.clientId, (error) => {
|
|
if (error) return done(error);
|
|
// Call `done(err, accessToken, [refreshToken], [params])`, see oauth2orize.exchange.code
|
|
return done(null, token);
|
|
});
|
|
});
|
|
});
|
|
}));
|
|
|
|
// Exchange the client id and password/secret for an access token. The callback accepts the
|
|
// `client`, which is exchanging the client's id and password/secret from the
|
|
// authorization request for verification. If these values are validated, the
|
|
// application issues an access token on behalf of the client who authorized the code.
|
|
|
|
server.exchange(oauth2orize.exchange.clientCredentials((client, scope, done) => {
|
|
// Validate the client
|
|
db.clients.findByClientId(client.clientId, (error, localClient) => {
|
|
if (error) return done(error);
|
|
if (!localClient) return done(null, false);
|
|
if (localClient.clientSecret !== client.clientSecret) return done(null, false);
|
|
// Everything validated, return the token
|
|
const token = utils.getUid(256);
|
|
// Pass in a null for user id since there is no user with this grant type
|
|
db.accessTokens.save(token, null, client.clientId, (error) => {
|
|
if (error) return done(error);
|
|
// Call `done(err, accessToken, [refreshToken], [params])`, see oauth2orize.exchange.code
|
|
return done(null, token);
|
|
});
|
|
});
|
|
}));
|
|
|
|
// User authorization endpoint.
|
|
//
|
|
// `authorization` middleware accepts a `validate` callback which is
|
|
// responsible for validating the client making the authorization request. In
|
|
// doing so, is recommended that the `redirectUri` be checked against a
|
|
// registered value, although security requirements may vary across
|
|
// implementations. Once validated, the `done` callback must be invoked with
|
|
// a `client` instance, as well as the `redirectUri` to which the user will be
|
|
// redirected after an authorization decision is obtained.
|
|
//
|
|
// This middleware simply initializes a new authorization transaction. It is
|
|
// the application's responsibility to authenticate the user and render a dialog
|
|
// to obtain their approval (displaying details about the client requesting
|
|
// authorization). We accomplish that here by routing through `ensureLoggedIn()`
|
|
// first, and rendering the `dialog` view.
|
|
|
|
module.exports.authorization = [
|
|
login.ensureLoggedIn(),
|
|
server.authorization((clientId, redirectUri, done) => {
|
|
db.clients.findByClientId(clientId, (error, client) => {
|
|
if (error) return done(error);
|
|
// WARNING: For security purposes, it is highly advisable to check that
|
|
// redirectUri provided by the client matches one registered with
|
|
// the server. For simplicity, this example does not. You have
|
|
// been warned.
|
|
return done(null, client, redirectUri);
|
|
});
|
|
}, (client, user, done) => {
|
|
// Check if grant request qualifies for immediate approval
|
|
|
|
// Auto-approve
|
|
if (client.isTrusted) return done(null, true);
|
|
|
|
db.accessTokens.findByUserIdAndClientId(user.id, client.clientId, (error, token) => {
|
|
// Auto-approve
|
|
if (token) return done(null, true);
|
|
|
|
// Otherwise ask user
|
|
return done(null, false);
|
|
});
|
|
}),
|
|
(req, res) => {
|
|
res.render('dialog', { transactionId: req.oauth2.transactionID, user: req.user, client: req.oauth2.client });
|
|
},
|
|
];
|
|
|
|
// User decision endpoint.
|
|
//
|
|
// `decision` middleware processes a user's decision to allow or deny access
|
|
// requested by a client application. Based on the grant type requested by the
|
|
// client, the above grant middleware configured above will be invoked to send
|
|
// a response.
|
|
|
|
module.exports.decision = [
|
|
login.ensureLoggedIn(),
|
|
server.decision(),
|
|
];
|
|
|
|
|
|
// Token endpoint.
|
|
//
|
|
// `token` middleware handles client requests to exchange authorization grants
|
|
// for access tokens. Based on the grant type being exchanged, the above
|
|
// exchange middleware will be invoked to handle the request. Clients must
|
|
// authenticate when making requests to this endpoint.
|
|
|
|
module.exports.token = [
|
|
passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),
|
|
server.token(),
|
|
server.errorHandler(),
|
|
];
|