En este tutorial vamos a aprender a hacer servicios autenticados en API Gateway. Para esto nos vamos a valer de una función Lambda, que operará como middleware conectándose con nuestro sistema de validación de Tokens preferido, yo en este caso voy a usar Firebase Auth, ya que es muy fácil de implementar.
¿Qué tecnologías usaremos?
Configurando Firebase
const firebase = require('firebase-admin')
const serviceAccount = require('./firebase.json')
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: 'https://******.firebaseio.com'
})
El archivo firebase.json
es un Service Account Key que se puede descargar desde console.cloud.google.com. Asegurate de tener seleccionado el proyecto correcto.
firebase.auth().createUser({
email,
emailVerified: false,
password,
displayName: name,
disabled: false
})
.then(firebaseResult => {
uid = firebaseResult.uid
saveUserInDatabase(uid, email, name)
// Handle response
})
.catch(error => {
// Handle response
})
<scriptsrc="https://www.gstatic.com/firebasejs/5.8.2/firebase.js"></script><script>// Initialize Firebasevar config = {
apiKey: "******",
authDomain: "*****.firebaseapp.com",
databaseURL: "https://*****.firebaseio.com",
projectId: "*****",
storageBucket: "*****.appspot.com",
messagingSenderId: "*****"
};
firebase.initializeApp(config);
</script>
Login:
login = (email, password) => {
returnnew Promise((resolve, reject) => {
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(() => {
return firebase.auth().signInWithEmailAndPassword(email, password)
})
.then(login => {
let {user} = login
user = JSON.parse(JSON.stringify(user))
console.log('Logged!', user)
const {accessToken} = user
saveAccessToken(accessToken)
resolve(user)
}).catch(error => {
reject(error)
})
})
}
Middleware con Lambda para validar los usuarios
functionhandler(event, context, callback){
const {authorizationToken: token} = event
firebase.auth().verifyIdToken(token).then((decodedIdTokens) => {
const {uid} = decodedIdTokens
getUser(uid)
.then(user => {
callback(null, allowAccess(user)
})
.catch(error => {
callback(null, denyAccess())
})
})
.catch(error => {
callback(null, denyAccess())
})
}
allowAccess(user)
y denyAccess()
son 2 funciones que retornan policies de IAM (Con esto API Gateway sabrá si el usuario está autenticado):
Allow access
{
principalId: JSON.stringify(user),
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "*"
}
]
},
"context": {
"stringKey": "value",
"numberKey": "1",
"booleanKey": "true"
},
"usageIdentifierKey": "abc"
}
Deny access
{
principalId: null,
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "*"
}
]
},
"context": {
"stringKey": "value",
"numberKey": "1",
"booleanKey": "true"
},
"usageIdentifierKey": "abc"
}
Ahora si, a configurar API Gateway
e le pusimos al rol ``
Lo primero es crear un rol para que API Gateway pueda ejecutar Lambda.
No voy a entrar en detalle en esta parte, pero digamos que le pusimos apigategay_invoke_lambda
También entenderé que ya hemos creado un endpoint con API Gateway. Vamos a la sección de Authorizers
y le damos crear uno nuevo.
Configuramos el authorizer apuntando a la función que acabamos de crear en Lambda y con el rol de ejecución:
El catching es opcional, pero es recomendado para mejorar el rendimiento de nuestro middleware.
principalId
en el Mapping template (Este es el que enviamos desde el middleware en la función de allowAccess(user)
):De esta forma ya no solo puedes crear endpoints con la alta disponibilidad de API Gateway + Lambda, sino que además los servicios pueden ir autenticados.
Cualquier inquietud no dudes en comentar, sé que algunas cosas requieren de mayor profundidad si no se tiene mucha experiencia con la nube de AWS.