70

We are building a mobile and web app on AWS using API Gateway and Lambda and are currently evaluating if we should use AWS Cognito or Firebase Auth.

AWS Cognito integrates nicely into API Gateway and Lamdba e.g. only authenticated users can execute certain API calls. Can the same behaviour be reached if we use Firebase Authentication instead? Any good or bad experience with this?

6 Answers 6

54

We are doing the same.

We started with Cognito but moved to Firebase because we were not satisfied with the way AWS Android SDK implements the authentication flow with Google and Facebook: the code is quite old, it makes use of deprecated methods and generally requires rewriting. On the other hand, Firebase authentication is obviously working seamlessly.

When you don't use Cognito, you need to implement your custom authenticator in AWS API Gateway which is quite easy and is described in https://aws.amazon.com/blogs/mobile/integrating-amazon-cognito-user-pools-with-api-gateway/. Firebase instructions for token validation are in https://firebase.google.com/docs/auth/admin/verify-id-tokens

The following is an excerpt of my authenticator's code:

'use strict';

// Firebase initialization
// console.log('Loading function');
const admin = require("firebase-admin");
admin.initializeApp({
  credential: admin.credential.cert("xxx.json"),
  databaseURL: "https://xxx.firebaseio.com"
});
// Standard AWS AuthPolicy - don't touch !!
...
// END Standard AWS AuthPolicy - don't touch !!

exports.handler = (event, context, callback) => {
    // console.log('Client token:', event.authorizationToken);
    // console.log('Method ARN:', event.methodArn);

    // validate the incoming token
    // and produce the principal user identifier associated with the token

    // this is accomplished by Firebase Admin
    admin.auth().verifyIdToken(event.authorizationToken)
        .then(function(decodedToken) {
            let principalId = decodedToken.uid;
            // console.log(JSON.stringify(decodedToken));

            // if the token is valid, a policy must be generated which will allow or deny access to the client

            // if access is denied, the client will recieve a 403 Access Denied response
            // if access is allowed, API Gateway will proceed with the backend integration configured on the method that was called

            // build apiOptions for the AuthPolicy
            const apiOptions = {};
            const tmp = event.methodArn.split(':');
            const apiGatewayArnTmp = tmp[5].split('/');
            const awsAccountId = tmp[4];
            apiOptions.region = tmp[3];
            apiOptions.restApiId = apiGatewayArnTmp[0];
            apiOptions.stage = apiGatewayArnTmp[1];
            
            const method = apiGatewayArnTmp[2];
            let resource = '/'; // root resource
            if (apiGatewayArnTmp[3]) {
                resource += apiGatewayArnTmp[3];
            }
            

            // this function must generate a policy that is associated with the recognized principal user identifier.
            // depending on your use case, you might store policies in a DB, or generate them on the fly

            // keep in mind, the policy is cached for 5 minutes by default (TTL is configurable in the authorizer)
            // and will apply to subsequent calls to any method/resource in the RestApi
            // made with the same token

            // the policy below grants access to all resources in the RestApi
            const policy = new AuthPolicy(principalId, awsAccountId, apiOptions);
            policy.allowAllMethods();
            // policy.denyAllMethods();
            // policy.allowMethod(AuthPolicy.HttpVerb.GET, "/users/username");

            // finally, build the policy and exit the function
            callback(null, policy.build());
        })
        .catch(function(error) {
            // Firebase throws an error when the token is not valid
            // you can send a 401 Unauthorized response to the client by failing like so:
            console.error(error);
            callback("Unauthorized");
        });
};

We are not in production, yet, but tests on the authenticator show that it behaves correctly with Google, Facebook and password authentication and it is also very quick (60 - 200 ms). The only drawback I can see is that you will be charged for the authenticator lambda function, while the Cognito integrated authenticator is free.


Update after almost 1yr

I moved away from API Gateway custom authenticator, mainly because I've not been able to automate its deployment with cloudformation scripts. My solution is now to have authentication directly within the API caching tokens for some time, like the Authenticator does, so to avoid excessive validations.

5
  • can you share more why you were not satisfied with Cognito for Facebook/Google authentication?
    – Alex
    Dec 30, 2016 at 4:08
  • 2
    Update after almost 1yr: - I moved away from API Gateway custom authenticator, mainly because I've not been able to automate its deployment with cloudformation scripts. My solution is now to have authentication directly within the API caching tokens for some time, like the Authenticator does, so to avoid excessive validations.
    – pmosconi
    Aug 25, 2017 at 12:49
  • Would third party jwt library firebase.google.com/docs/auth/admin/… save round trip to verify firebase accout? May 24, 2018 at 9:32
  • AFAIK the provided library does the same just saving you from coding everything from scratch. No roundtrip is required unless you have to refresh keys.
    – pmosconi
    May 25, 2018 at 12:18
  • 4
    Do you know if the things you didn't like about Cognito are still true? Jul 5, 2018 at 17:39
39

TL;DR; Firebase > Cognito

We started with Cognito first, but we eventually realized it has an atrocious smell to it when it comes using Federated Identities (e.g., Google Sign-in, Facebook Login, etc.). For Cognito User Pools (i.e., allowing a user to sign up/in with a username and password), you can use the built-in API Gateway Cognito User Pool Authorizer and it works beautifully. You don't need to write your own custom authorizer or anything.

However, if you want to support Federated Identities, you need to change the authentication on your API gateway to IAM Auth, and then have EVERY client sigv4 sign the requests, which turned out to be a thorn in our side and cost significant development time. Option 2 was to have API Gateway generate you code for your API calls for every client... which in my opinion is a testament to the cumbersome integration with Cognito.

We got Firebase working through the custom authorizer for API Gateway. Was a breeze on all clients (iOS, Android and Web). The API Gateway endpoints were linked up to Lambda functions, which were able to communicate with DynamoDB, S3, and other web services on behalf of the user calling the endpoint. The lambda functions knew who the calling user was because the custom authorizer returned the email address in the JWT.

Here's a pretty basic Firebase custom authorizer that returns the user email in the JWT as the 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: '*'
                }
            ]
        }
    };
}

You can then use $context.authorizer.principalId in your API Gateway mapping template to retrieve the email and pass it to lambda X.


I initially thought latency would be an issue, but that really doesn't seem to be the case. Any and all latency I've encountered is due to latency of the lambda that is being called due to cold start. I've noticed authorization lambdas live much longer than other lambdas.


This lambda gets called for every backend request. There are couple of things though:

  1. caching is enabled for 1hr for each JWT, so that greatly simplifies the calls.
  2. The lambda is being called constantly, so there shouldn't be a cold start, and
  3. The first MILLION lambda requests/month are free, and then it's $0.20 for every million requests/month after that. So unless you're having your API called BILLION times per month, you aren't going to incur outrageous cost.
8
  • Thanks for sharing. I just ran into a roadblock with Unity3d + Cognito when I realized that User Pools was not implemented there. I'd like to try Firebase but not switch completely to Firebase or GCP for backend, keeping AWS/API Gateway. Did you also get federated identities, e.g. Facebook & Google working with Firebase+API Gateway? Any snags you ran into while doing so?
    – dferraro
    Jul 23, 2017 at 18:12
  • 3
    I did get federated identities working with Firebase+API Gateway. Honestly there was nothing specific/custom I needed to do on the backend side. For clients, just make sure you're getting the JWT from Firebase after connecting the federated identity. If you're interested in seeing how I did it in iOS client using Swift, let me know and I'll post a gist.
    – azizj
    Jul 24, 2017 at 19:32
  • @AzizJaved how is the latency calling firebase auth from lambda, do you think using Cognito would be much faster since it's AWS?
    – Alex
    Nov 14, 2017 at 18:46
  • 1
    I initially thought latency would be an issue, but that really doesn't seem to be the case. Any and all latency I've encountered is due to latency of the lambda that is being called due to cold start. I've noticed authorization lambdas live much longer than other lambdas.
    – azizj
    Nov 14, 2017 at 19:57
  • 1
    No. All that does is check expiration of the JWT and a few other things local things. You have to make the call to ask Google if the token was actually generated by them.
    – azizj
    May 24, 2018 at 22:20
10

Aws documentation is pretty confused. The callbacks system for the different authentication steps is better documented in Firebase. The result is a cleaner code and a better control on the authentication flow. In addition, the Firebase user interface is more user-friendly. If you are planning to use content providers and sync adapters, I would suggest to use Firebase because you will have simple methods for data synchronization between the local and the remote (Firebase) db

7

aws cognito gives more ways to authenticate users than firebase. Especially, if you are building a game, it gives facility to login through google and ios game centres. It provides syncing leaderboards and achievements of game centre provides. Automatic state synchronisation feature is there in Cognito. But definitely, it is very confusing. It takes too much time for implementation. On the other hand, firebase authentication is pretty fast to implement.

7

For me the deal breaker is ability to export users with all the details, if you decide to move to some other authentication service provider.

While this is possible in Firebase, it is not available in AWS Cognitio! You can enter any time of year but you can never leave :). https://forums.aws.amazon.com/thread.jspa?threadID=296932

1
4

Just in case that you are using Unity, currently the Unity SDK does not support Cognito User Pool. (That is, AWS hosted list of users) I am currently hesitating because of this. See my post here that they confirmed it is true, currently (26/06/2017) the feature is still not available which might shows a lack of attention to Unity users from them.

However if I use Firebase for logins I would need some more integrations for those credentials to use AWS services. (I would like to use S3 and DynamoDB but only logged in user can use it.) This also made me realize I should move everything to Firebase to save my time and frustrations as soon as possible. (The real time DB is pricier than S3/DynamoDB, but Unity has its own replacement of AWS MobileAnalytics)

AWS S3 recently got nicer UI, which I think is close to Google's level. But other than that I think Firebase's UI is much more joyful to use.

Also, Firebase authentication is free while Cognito is free up to 50k monthly active user. (The next 50k will cost 0.0055 which means if you have 100k MAU it will be 50000 * 0.0055 = 275 USD https://aws.amazon.com/cognito/pricing/)

One more thing, the AWS .NET documentation is a nightmare to read/search in my opinion.

1
  • 3
    I was easily able to use S3 and DynamoDB with Firebase Authentication. Your client calls an API Gateway endpoint that is hooked up to lambda X, passing the JWT from Firebase. You verify the JWT token in a custom authorizer, and then pass the user email to lambda X. As long as X has IAM access to DynamoDB and S3, it can get the user's resources, and return it as a response from the API Gateway.
    – azizj
    Jun 26, 2017 at 19:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.