Skip to content

Instantly share code, notes, and snippets.

@thgbarros
Last active April 23, 2019 07:24
Show Gist options
  • Save thgbarros/ba379f03e5a3eea8d25020201a154132 to your computer and use it in GitHub Desktop.
Save thgbarros/ba379f03e5a3eea8d25020201a154132 to your computer and use it in GitHub Desktop.
OAuth 2.0 server implementation to Alexa Skill Account linking
const pgp = require('pg-promise')({});
const db = pgp(process.env.DATABASE_URL);
/*
* Get access token.
*/
module.exports.getAccessToken = function(bearerToken) {
return db.query('SELECT otk_token_acesso, otk_token_expira_em, otk_ocl_id, otk_refresh_token, otk_refresh_token_expira_em, otk_usr_id FROM otk_oauth_tokens WHERE otk_token_acesso = $1', [bearerToken])
.then(function(result) {
var token = result.rows[0];
return {
accessToken: token.access_token,
client: {id: token.client_id},
expires: token.expires,
user: {id: token.userId}, // could be any object
};
});
};
/**
* Get client.
*/
module.exports.getClient = function (clientId, clientSecret) {
let query = clientSecret ?
'SELECT ocl_id, ocl_secret, ocl_uris_redirecionamento, ocl_name, ocl_permissoes, ocl_escopo FROM ocl_oauth_cliente WHERE ocl_id = $1 AND ocl_secret = $2' :
'SELECT ocl_id, ocl_secret, ocl_uris_redirecionamento, ocl_name, ocl_permissoes, ocl_escopo FROM ocl_oauth_cliente WHERE ocl_id = $1';
let params = clientSecret ? [clientId, clientSecret] : [clientId]
return db.query(query, params)
.then(function(result) {
client = {
name: result[0].ocl_name,
id: result[0].ocl_id,
redirectUris: result[0].ocl_uris_redirecionamento.split(','),
grants: ['authorization_code', 'password']
};
return client;
})
.catch((error) => {
console.log(error)
});
};
/**
* Get refresh token.
*/
module.exports.getRefreshToken = function (bearerToken) {
return db.query('SELECT otk_token_acesso, otk_token_expira_em, otk_ocl_id, otk_refresh_token, otk_refresh_token_expira_em, otk_usr_id FROM otk_oauth_tokens WHERE otk_refresh_token = $1', [bearerToken])
.then(function(result) {
return result.rowCount ? result.rows[0] : false;
});
};
/*
* Get user.
*/
module.exports.getUser = function (username, password) {
return db.query('SELECT usr_id FROM usr_usuario WHERE usr_login = $1 AND usr_senha = $2', [username, password])
.then(function(result) {
return result.length > 0 ? result[0] : false;
});
};
/**
* Save token.
*/
module.exports.saveToken = function (token, client, user) {
return db.query('INSERT INTO otk_oauth_tokens(otk_token_acesso, otk_token_expira_em, otk_ocl_id, otk_refresh_token, otk_refresh_token_expira_em, otk_usr_id) VALUES ($1, $2, $3, $4, $5, $6)', [
token.accessToken,
token.accessTokenExpiresAt,
client.id,
token.refreshToken,
token.refreshTokenExpiresAt,
user.id
]).then(function(result) {
token.client = client;
token.user = user;
return token; // TODO return object with client: {id: clientId} and user: {id: userId} defined
});
};
module.exports.getAuthorizationCode = (code) => {
return db.query('SELECT * FROM oca_oauth_codigo_autorizacao WHERE oca_codigo = $1', [code])
.then(function(authorization) {
if (!authorization) return {};
authorization = authorization[0];
return Promise.all([
authorization,
db.query('select * from ocl_oauth_cliente where ocl_id = $1', authorization.oca_ocl_id),
db.query('select * from usr_usuario where usr_id = $1', authorization.oca_usr_id)
]);
})
.then((authorization) => {
code = authorization[0];
client = authorization[1][0];
user = authorization[2][0];
return {
authorizationCode: code.oca_codigo,
expiresAt: code.oca_expira,
redirectUri: code.oca_uri_redirecionamento,
scope: code.oca_escopo,
client: {id: client.ocl_id},
user: {id: user.usr_id}
};
});
};
module.exports.saveAuthorizationCode = (code, client, user) => {
let authCode = {
authorization_code: code.authorizationCode,
expires_at: code.expiresAt,
redirect_uri: code.redirectUri,
scope: code.scope,
client_id: client.id,
user_id: user.id
};
return db.query(`INSERT INTO oca_oauth_codigo_autorizacao(oca_codigo, oca_ocl_id, oca_usr_id, oca_expira, oca_uri_redirecionamento, oca_escopo) VALUES ($1, $2, $3, $4, $5, $6)`, [
authCode.authorization_code, authCode.client_id, authCode.user_id, authCode.expires_at, authCode.redirect_uri, authCode.scope
]).then(function() {
return code;
});
};
module.exports.revokeAuthorizationCode = (code) => {
return db.query('delete from oca_oauth_codigo_autorizacao where oca_codigo = $1',[code.authorizationCode])
.then(function(authorizationCode) {
return !!authorizationCode;
});
};
// list of valid scopes
const VALID_SCOPES = ['read', 'write', 'notifications', 'actions'];
module.exports.validateScope = (user, client, scope) => {
if (!scope) return scope
if (!scope.split(' ').every(s => VALID_SCOPES.indexOf(s) >= 0)) {
return scope;
}
return scope;
}
db.connect()
.then(obj => {
console.log('Banco de dados conectado.');
});
let oauthServer = require('oauth2-server');
let Request = oauthServer.Request;
let Response = oauthServer.Response;
let util = require('util');
let httpStatus = require('http-status');
oauthServer = new oauthServer({
debug: true,
model: require('../models'),
accessTokenLifetime: 4 * 60 * 60,
allowEmptyState: true
});
module.exports = (app) => {
let controller = {};
let indexData = {messages: {}, user: {}};
let backappService = app.services.backapp_service;
let oauth_model = oauthServer.options.model;
let oauth_service = app.services.oauth_service;
controller.get_token = (request, response) => {
var req = new Request(request);
var res = new Response(response);
oauthServer.token(req,res, {requireClientAuthentication: {authorization_code: false}})
.then(function(token) {
oauth_service.getState(request.body.code)
.then((state) => {
let redirect = `${request.body.redirect_uri}#access_token=${token.accessToken}&token_type=Bearer&state=${state}`;
response.status(httpStatus.OK)
.send({
"access_token": `${token.accessToken}`,
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": `${token.refreshToken}`
});
oauth_service.removeUserState(state);
oauth_service.removeClientCode(request.body.code);
});
}).catch(function(err){
console.log(err);
return response.status(500).json(err);
});
};
controller.authorize_page = (request, response) => {
let client_id = request.query.client_id;
let client_secret = request.query.client_secret;
let state = request.query.state;
oauth_model.getClient(client_id, client_secret)
.then((client) => {
if (!client){
indexData.message = `Não foi encontrado nenhum cliente com id: ${client_id}`;
indexData.error = {
status: 404,
stack: 'Cliente não encontrado.'
};
return response.render('error', indexData);
}
oauth_service.getLogged(state)
.then((logged) => {
if (logged) return logged;
response.redirect(util.format('/login?redirect=%s&client_id=%s&redirect_uri=%s&scope=%s&response_type=%s&state=%s',
request.path, request.query.client_id, request.query.redirect_uri,
request.query.scope, request.query.response_type, request.query.state));
return false;
})
.then((redirectAuthorizePage) => {
if (redirectAuthorizePage) {
return response.render('authorize', {
client_id: client.id,
grants: client.grants,
redirect_uri: request.query.redirect_uri,
client_name: client.name,
response_type: request.query.response_type,
scope: request.query.scope,
state: request.query.state
});
}
});
});
};
controller.authorize = (request, response) => {
let client_id = request.query.client_id || request.body.client_id;
let redirect_uri = request.query.redirect_uri || request.body.redirect_uri;
let scope = request.query.scope || request.body.scope;
let response_type = request.query.response_type || request.body.response_type;
let state = request.query.state || request.body.state;
oauth_service.getLogged(state)
.then((logged) => {
if (logged) return logged;
response.redirect(util.format('/login?redirect=%s&client_id=%s&redirect_uri=%s&scope=%s&response_type=%s&state=%s&',
request.path, client_id, redirect_uri, scope, response_type, state));
return false;
})
.then((authorize) => {
if (!authorize) return;
let req = new Request(request);
let res = new Response(response);
const options = {
authenticateHandler: {
handle: (data) => {
return {id: authorize.usr_id};
}
}
};
oauthServer.authorize(req, res, options)
.then((authorization) => {
oauth_service.setState(authorization.authorizationCode, request.body.state)
.then(() => {
let redirect = `${request.body.redirect_uri}?state=${request.body.state}&code=${authorization.authorizationCode}`;
response.redirect(redirect);
});
})
.catch((error) => {
console.log(error.message);
});
});
};
controller.login_page = (request, response) => {
indexData.redirect = request.query.redirect;
indexData.client_id = request.query.client_id;
indexData.redirect_uri = request.query.redirect_uri;
indexData.scope = request.query.scope;
indexData.response_type = request.query.response_type;
indexData.state = request.query.state;
response.render('login', indexData);
};
controller.login = (request, response) => {
let username = request.body.login;
let password = request.body.password;
let state = request.query.state || request.body.state;
oauth_model.getUser(username, password)
.then((user) => {
if (!user) {
indexData.messages = {title: 'Error', errors: ['Login ou senha inválido.']};
return response.redirect(util.format('/login?redirect=%s&client_id=%s&redirect_uri=%s&scope=%s&response_type=%s&state=%s',
request.query.redirect, request.query.client_id, request.query.redirect_uri,
request.query.scope, request.query.response_type, state));
}
oauth_service.login(user, state)
.then(() => {
var path = request.query.redirect || '/';
path = util.format('%s?client_id=%s&redirect_uri=%s&scope=%s&response_type=%s&state=%s',
path, request.query.client_id, request.query.redirect_uri,
request.query.scope, request.query.response_type, state);
return response.redirect(path);
});
});
};
return controller;
};
module.exports = (app) => {
let oauth_controller = app.controllers.oauth_controller;
app.route('/oauth/token')
.post(oauth_controller.get_token);
app.route('/oauth/authorize')
.get(oauth_controller.authorize_page)
.post(oauth_controller.authorize);
app.route('/login')
.get(oauth_controller.login_page)
.post(oauth_controller.login);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment