TL;DR ; Firebase > Cognito
Nous avons commencé par Cognito, mais nous avons finalement réalisé qu'il avait une odeur atroce lorsqu'il s'agissait d'utiliser des identités fédérées (par exemple, Google Sign-in, Facebook Login, etc.). Pour les pools d'utilisateurs Cognito (c'est-à-dire permettant à un utilisateur de s'inscrire avec un nom d'utilisateur et un mot de passe), vous pouvez utiliser l'API Gateway Cognito User Pool Authorizer et cela fonctionne à merveille. Vous n'avez pas besoin d'écrire votre propre Authorizer personnalisé ou quoi que ce soit d'autre.
Cependant, si vous souhaitez prendre en charge les identités fédérées, vous devez modifier l'authentification sur votre passerelle API en IAM Auth, puis demander à CHAQUE client de signer les requêtes avec Sigv4, ce qui s'est avéré être une épine dans notre pied et a coûté beaucoup de temps de développement. L'option 2 consistait à demander à API Gateway de générer votre code pour vos appels API pour chaque client... ce qui, à mon avis, témoigne de la lourdeur de l'intégration avec Cognito.
Nous avons fait fonctionner Firebase via l'autorisateur personnalisé pour API Gateway. C'était un jeu d'enfant sur tous les clients (iOS, Android et Web). Les points de terminaison API Gateway ont été reliés à des fonctions Lambda, qui ont pu communiquer avec DynamoDB, S3 et d'autres services Web au nom de l'utilisateur appelant le point de terminaison. Les fonctions Lambda savaient qui était l'utilisateur appelant parce que l'autorisateur personnalisé renvoyait l'adresse électronique dans le JWT.
Voici un autorisateur personnalisé Firebase assez basique qui renvoie l'email de l'utilisateur dans le JWT en tant que principalId :
'use strict';
console.log('Loading function');
var admin = require('firebase-admin');
var serviceAccount = require('./my-secret-json.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://my-app.firebaseio.com'
});
exports.handler = (event, context, callback) => {
var token = event.authorizationToken;
if (token == null) {
callback('Invalid token');
}
else {
admin.auth().verifyIdToken(token)
.then(function (decodedToken) {
var email = decodedToken.email;
var policy = generatePolicy(email);
callback(null, policy);
}).catch(function (error) {
console.log(error);
callback('Unauthorized');
});
}
};
var generatePolicy = function (email) {
return {
principalId: email,
policyDocument: {
Version: '2012-10-17',
Statement: [
{
Action: 'execute-api:Invoke',
Effect: email ? 'allow' : 'deny',
Resource: '*'
}
]
}
};
}
Vous pouvez alors utiliser $context.authorizer.principalId
dans votre modèle de mappage API Gateway pour récupérer l'e-mail et le transmettre à lambda X.
J'ai d'abord pensé que la latence serait un problème, mais cela ne semble pas être le cas. Toutes les latences que j'ai rencontrées sont dues à la latence de la lambda qui est appelée en raison du démarrage à froid. J'ai remarqué que les lambdas d'autorisation vivent beaucoup plus longtemps que les autres lambdas.
Ce lambda est appelé pour chaque requête du backend. Il y a cependant deux choses à faire :
- La mise en cache est activée pendant 1 heure pour chaque JWT, ce qui simplifie grandement les appels.
- Le lambda est appelé en permanence, de sorte qu'il ne devrait pas y avoir de démarrage à froid.
- Les premiers MILLIONS de requêtes lambda/mois sont gratuits, puis 0,20 $ par million de requêtes/mois. Ainsi, à moins que votre API ne soit appelée des MILLIONS de fois par mois, vous n'aurez pas à supporter des coûts exorbitants.