No Hassle: Serverless Social Login Powered by AWS Cognito
An architectural perspective on choosing between possible solutions Context Let's assume that I have an application for which user No Need to Login just need to register with their social identity. If you're wondering why to think like this and how it can be done. Registration/signup can be a barrier for users as it involves multiple steps and data sharing. Most internet users have at least one social identity, while all mobile users have either a Google (Android) or Apple (iOS) identity. Users can access your app more easily if login is mostly password-free. You can collect user data from social providers, provided users grant permission. the most popular social like Facebook, Google, Apple, Amazon, LinkedIn, Github and many others. Since every IdP is expected to implement the OpenID Connect standard (built on top of OAuth2) and requires some configuration, let's explore the available options. Option 1: Native integration Every one has its own SDK and apis to integrate natively, so you can code in your app the integration for IdPs you want to use. Pros Granular Control: Manage each IdP integration individually with native support, allowing you to customize the user experience through configuration. You can also handle IdP-specific requests beyond the OAuth standard (more on that later...). Direct Integration: No intermediaries, ensuring a straightforward architecture. Leverage robust SDKs from providers like Google, Facebook, and Amazon, benefiting from their high availability and resilience. Cost-Effective: Most IdPs offer a free tier for their APIs, eliminating additional costs from that side. Cons challenge to scale: Each IdP come with its own SDK and unique requirement, making standardization difficult. Managing these variation requires extensive code, and even if you centralize authentication in library, every client must update it to apply changes. Complex Testing & Debugging: More code means more testing. Additionally, each IdP has specific nuances, requiring in-depth knowledge to troubleshoot and maintain integrations effectively. Option 2: use an OAuth Provider Since Social IdPs follow a standard, their implementations can be abstracted using interfaces. This allows seamless integration with an OAuth 2 service provider, reducing dependency on specific SDKs. Pros Simplified Integration: A single connection between your client and the OAuth identity platform means less code, fewer tests, fewer releases, and faster development. Scalability: Easily add or remove IdPs without affecting clients (as mentioned earlier). Centralized Authentication Management: Configure and govern authentication flows in one place, ensuring consistency across all IdPs while enabling monitoring, metrics, and analytics. Standards-Based Authentication: Build your authentication flow on industry standards for reliability and interoperability. Identity Platforms as a Service: Solutions like AWS Cognito, Auth0, Google Firebase, and others provide managed authentication services. Cons Limited IdP Support: You can only integrate with IdPs that your OAuth provider supports, restricting flexibility. Increased System Complexity: Adding an OAuth provider introduces extra components, making the architecture more complex. Potential Single Point of Failure: If the OAuth provider goes down, authentication becomes unavailable for users. Ensuring high availability and scalability of your OAuth provider is crucial. My choice: Option 2 with AWS Cognito I understand there are many constraints, and I can't list them all here. Given my context, I chose option 2 and implemented AWS Cognito as my OAuth provider. I also explored Auth0 and a few other services during my evaluation. I chose to accept Cognito's constraints and costs in exchange for a low-code implementation and easy setup—essentially prioritizing faster delivery—since I wasn't certain if a more complex solution would be worthwhile. Here is my implementation All you need is to: configure your integration on social Provider side.Here a reference for each provider i got form internet Google Facebook Apple Config Cognito integration. HERE AWS Doc for each Supported Providers Integrate your application with Amazon Cognito. Cognito provides an hosted ui for the login page, but you can create your own. Pitfalls: things to be careful about Here are some challenges I encountered during this integration. While this isn’t a complete list of potential issues with Amazon Cognito and the social login flow, it reflects my personal experience—things I discovered while working on it. Watch out for Cognito limits Serverless doesn’t mean unlimited, and Cognito is a prime example of this. In simple terms, Cognito's scaling policy isn't built for sudden traffic spikes. Its capacity scales based on user pool size, meaning more users allow for higher T

An architectural perspective on choosing between possible solutions
Context
Let's assume that I have an application for which user No Need to Login just need to register with their social identity.
If you're wondering why to think like this and how it can be done.
- Registration/signup can be a barrier for users as it involves multiple steps and data sharing.
- Most internet users have at least one social identity, while all mobile users have either a Google (Android) or Apple (iOS) identity.
- Users can access your app more easily if login is mostly password-free.
- You can collect user data from social providers, provided users grant permission.
the most popular social like Facebook, Google, Apple, Amazon, LinkedIn, Github and many others.
Since every IdP is expected to implement the OpenID Connect standard (built on top of OAuth2) and requires some configuration, let's explore the available options.
Option 1: Native integration
Every one has its own SDK and apis to integrate natively, so you can code in your app the integration for IdPs you want to use.
Pros
- Granular Control: Manage each IdP integration individually with native support, allowing you to customize the user experience through configuration. You can also handle IdP-specific requests beyond the OAuth standard (more on that later...).
- Direct Integration: No intermediaries, ensuring a straightforward architecture. Leverage robust SDKs from providers like Google, Facebook, and Amazon, benefiting from their high availability and resilience.
- Cost-Effective: Most IdPs offer a free tier for their APIs, eliminating additional costs from that side.
Cons
challenge to scale: Each IdP come with its own SDK and unique requirement, making standardization difficult. Managing these variation requires extensive code, and even if you centralize authentication in library, every client must update it to apply changes.
Complex Testing & Debugging: More code means more testing. Additionally, each IdP has specific nuances, requiring in-depth knowledge to troubleshoot and maintain integrations effectively.
Option 2: use an OAuth Provider
Since Social IdPs follow a standard, their implementations can be abstracted using interfaces. This allows seamless integration with an OAuth 2 service provider, reducing dependency on specific SDKs.
Pros
- Simplified Integration: A single connection between your client and the OAuth identity platform means less code, fewer tests, fewer releases, and faster development.
- Scalability: Easily add or remove IdPs without affecting clients (as mentioned earlier).
- Centralized Authentication Management: Configure and govern authentication flows in one place, ensuring consistency across all IdPs while enabling monitoring, metrics, and analytics.
- Standards-Based Authentication: Build your authentication flow on industry standards for reliability and interoperability.
- Identity Platforms as a Service: Solutions like AWS Cognito, Auth0, Google Firebase, and others provide managed authentication services.
Cons
Limited IdP Support: You can only integrate with IdPs that your OAuth provider supports, restricting flexibility.
Increased System Complexity: Adding an OAuth provider introduces extra components, making the architecture more complex.
Potential Single Point of Failure: If the OAuth provider goes down, authentication becomes unavailable for users. Ensuring high availability and scalability of your OAuth provider is crucial.
My choice: Option 2 with AWS Cognito
I understand there are many constraints, and I can't list them all here. Given my context, I chose option 2 and implemented AWS Cognito as my OAuth provider. I also explored Auth0 and a few other services during my evaluation.
I chose to accept Cognito's constraints and costs in exchange for a low-code implementation and easy setup—essentially prioritizing faster delivery—since I wasn't certain if a more complex solution would be worthwhile.
Here is my implementation
All you need is to:
-
configure your integration on social Provider side.Here a reference for each provider i got form internet
Config Cognito integration. HERE AWS Doc for each Supported Providers
Integrate your application with Amazon Cognito. Cognito provides an hosted ui for the login page, but you can create your own.
Pitfalls: things to be careful about
Here are some challenges I encountered during this integration. While this isn’t a complete list of potential issues with Amazon Cognito and the social login flow, it reflects my personal experience—things I discovered while working on it.
Watch out for Cognito limits
Serverless doesn’t mean unlimited, and Cognito is a prime example of this.
In simple terms, Cognito's scaling policy isn't built for sudden traffic spikes. Its capacity scales based on user pool size, meaning more users allow for higher TPS. However, the first threshold is set at 1 million users—whether you have 1 or 999,999 users, your TPS remains the same.
If logins are steady, this isn't an issue. But if your app experiences traffic surges, such as during specific time periods, you may face throttling errors from Cognito.
These diagram show successful federation logins and throttling errors:
i split into two distinct diagrams for better visualisation, but i want to point out that
around 20:50 i had ~7K throttling errors and ~1.5K of success (total requests: ~8.5K)
around 21:20 i had ~6K throttling errors and ~1.4K success (total requests: ~7.5K)
around 22:30 i had ~1.3K success with ZERO throttling errors
You can find Cognito's TPS calculation rules in a specific section of its documentation, and it's important to review them carefully.
As shown in the successful login metric diagram, handling throttling exceptions in your app can reduce user impact. While users may experience a slight delay, they will still be able to log in successfully.
I decided that it could be acceptable, and i traded it for easy setup and integration with Social Providers.
Since this decision affects the customer experience, I tried to mitigate it as much as possible. For example, I sent push notifications before traffic spikes to encourage users to log in earlier and distribute login requests more evenly.
Standards are not prescriptive
I love standards, everybody should love them in engineering.
Unfortunately, sometimes for good reasons and sometimes not, giants have bias to force standards a little bit.
Apple, i'm pointing my finger at you!
For instance, Apple’s guidelines mandate that if your app includes social login and is distributed on the App Store, it must also support Sign in with Apple. While this may feel restrictive, it’s a reasonable requirement.
Additionally, Apple enforces that the "User Cancellation" option must be easily accessible and clear, which is fair.
And here Apple does not adhere to the OAuth standard: if an Apple user allows Apple to share their data with your app, some kind of association between your app and the user also takes place in the Apple system, and if a user wants to cancel from your app (also known as your user pool), this association should also be removed.
To do that, you have to invoke Apple apis to:
generate a valid access or refresh token.
invalidate the freshly generated token.
Sounds weird, but this is exactly what this doc page prescribes.
And, guess what? Cognito doesn't handle it.
Even if Cognito could handle it because it has all the information it needs, especially the private key you created on the Apple side and provided to Cognito to request the tokens, that's reasonable from a product perspective: Cognito adheres to standards and can't track every specific implementation.
But it does mean that Apple won't include your app in the store if you don't take care of it.
Let's look at how to i implement this.
You can't handle it directly in the app since I used Cognito to separate the app from authentication providers, I wanted to maintain that separation. Plus, storing a private key on the device is not a good idea.
Instead, this needs to be implemented on the backend. My initial approach was to use Cognito events—when a user is deleted in Cognito, an event triggers a Lambda function that calls Apple's API to remove the user from Apple's system.
As far as I know, Cognito today has
Lambda triggers: user deletion not supported
Cloudtrail tracks all management api calls, and user cancellation is a management api. But Cloudtrail event doesn't have any reference to actual user (and it saved my day in an audit session, but this is another story)
Cognito Sync: it seems to handle user deletion. Quoting:
To remove a record, either set the op
to remove
, or set the value to null.
This is how it looks like:
still there are 2 problem:
- first, i have to put your Apple's private key in Cognito and in Secret Manager. Cognito can't retrieve it from Secret Manager. I raised this issue to Cognito team, keep you posted on this.
- second, Cognito user cancellation and Apple user cancellation are asynchronous: what if it success on Cognito side and than fails on Apple side? User wont be in our Cognito user pool anymore, so we can't rollback the operation. So I need to handle failures, and to handle it you need to store it. Let's add a DLQ for our deletion lambda
After update this is how it look:
After saving, you must analyse why the deletion failed and try again. How long can this take? It depends on the cause and your process, but until you've done that, users will still see their user associated with your app, and I'm not sure Apple would like it and approve your app submission.
You need to reverse the order of deletion, first on the Apple side and then on the Cognito side. If the Apple deletion fails, you can send an error message to the user and inform he/she that the deletion cannot be performed and they should try again later.
In the case of a Cognito error, you will have to do this later, but at least the user will not see that their user is linked to your app and Apple should be satisfied and approve your request.
Let's see how it looks like
I decided to implement a custom api for Apple user deletion because it can be implemented just in half our code base (not for Android version of the app), the integration is quite simple and Apple would be happy with this solution, but probably not with the alternative solution. Still an error handling mechanism still need to be implemented to catch Cognito deletion errors and to recover them.
Wrap up
I have shown you my solution to real-world problems and how you can make informed decisions by carefully weighing trade-offs between different solutions that best fit your context and constraints.
In other words, the daily work of an architect, simplified.
Architectures need to evolve as the context and constraints change over time. So always design your solutions so that they can easily evolve with them.
I hope it was useful for you!
Bye