Last active
April 23, 2019 07:24
-
-
Save thgbarros/ba379f03e5a3eea8d25020201a154132 to your computer and use it in GitHub Desktop.
OAuth 2.0 server implementation to Alexa Skill Account linking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.'); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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