Is AWS Amplify insecure? - react-native

I've been given the task of implementing a user Sign In / Sign Up flow in a react native app. There is nothing too fancy about this app in particular. The usual Sign In, Sign Up (with SMS Verification) and Password Reset screens suffice. So I went looking for identity providers. Auth0 and AWS Cognito were the most suitable finds. Auth0 was considered too expensive by my managers so we discarded it. Which left me with the Cognito option.
According to the docs, it is possible to completely replace the default UI (which is something that pleases the UI/UX team) but still using the underlying infrastructure. One thing that concerns our team very much is security. According to this and this, authorization requests should only be made through external agents (mobile user browsers). So I went digging into the aws-amplify's source code and found that ultimately what it does (and correct me if I'm wrong here, please) is just a simple API request to the AWS auth endpoints passing my ClientId and other attributes.
This got me a little worried about the security of the interactions with AWS. As AWS endpoints are secure, I know a mitm attack is discarded.
But what keeps an attacker of decompiling my mobile app, getting access to the ClientId and making direct requests to AWS? Is AWS Amplify's really that insecure or am I missing something here?

There are many attacks that are possible but at a high level 3 stand out
Credential compromise
Social engineering
DoS
Credential compromise
Your account credentials should not be exposed, STS credentials are time limited and need you to specifically grant permissions to the pool to access aws services
https://docs.aws.amazon.com/cognito/latest/developerguide/role-based-access-control.html
you need to give a least privilege, follow approach outlined here
https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege
Social engineering attack
I guess exposed ClientId from a decompiled source could be used but would need to be combined with other user data so as a general rule lock down everything that links to your account that could be combined with the Client Id in a social attack
Dos
AWS provides what it calls "Advanced Security" in pools
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-advanced-security.html
this should be required when building Cognito Apps its comprehensive
Security threats constantly evolve, AWS do a good job, there are security advantages in using
Cloudfront
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/security.html
CloudTrail
https://aws.amazon.com/cloudtrail/

Related

AWS Cognito how to query for the JWT Token after receiving an authorization code

my application requires an authorization code grant flow integration with Cognito and the website responded to me that Auth-Code accordingly.
https://<poolName>.auth.eu-central-1.amazoncognito.com/login?client_id=<clientID>&response_type=code&scope=email+openid+profile&redirect_uri=<redirectURI>
Now I want to know what email address has logged in. As fas as I understood, this is where I would need the JWT token.
How can I query the email adress of the token I have just received?
Any help is much appreciated!
As I was going through that, actually, I still am. I have talked a lot with Amazon Engineers for past weeks, have done a bunch of research on my own and let me clarify couple of things.
Hosted UI is a way to go if you can accept the limitations. This is after all hosted ui, you can only change that much. BUT (!!!!) and I can not stretch this enough: it works only for simple usage. If you start searching you'll see that it doesn't support CUSTOM_AUTH flows which is extremely useful if you want to implement even something as basic as MFA. So if you dream of Revolut-like login page with just phone number email verification - pity, hosted ui will not help.
Hosted UI is more than just UI! It's a whole server! That's why you can't simply replace it.
Now, as we established what hosted UI can't do. What are the alternatives? Of course, you can use other providers like Okta or Auth0, but I assume, you're here because you want to use AWS. But the recommended (by AWS) alternative is to actually implement your own authentication using Amplify SDK. It's quite simple to use, I must say that. But what they don't tell you explicitly, is that it's no longer OIDC flow. Instead, AWS suggests to use their custom flows, such as USER_PASSWORD flow or SRP (Secure Remote Password), where password doesn't fly over http(s) at all.
You might ask: can't I have OIDC with Cognito AND custom flows? Well... you can, but it's not that simple. Long story short, you can use both hosted ui and amplify and possibly create your own cool SSO. For details look at their github page where AWS Labs go through some details.
If you can't afford spending next 2 months working on SSO, but you don't necessarily need OIDC flow and get settle for another solution, you can easily go for SRP or USERNAME_PASSWORD flows.
If you're like me and you're migrating from the old legacy authentication system done... wherever (;)), go for USERNAME_PASSWORD and utilise user migration lambda trigger in Cognito, where you can automatically migrate users once they login with their old credentials! Neat
AWS Cognito is full of traps... Consider that as well
First, make sure your Cognito client includes the email OAuth scope. User Pools > my-user-pool > App client settings > Allowed OAuth Scopes.
Then, decode the id token and you will have the email. You can use JWT.io to quickly decode tokens for testing and development.
UPDATE: You can use the POST /oauth2/token endpoint to fetch the tokens. But in general, if you're creating a frontend for users, it's better to use someone else's UI. The Cognito hosted UI works, although it looks a bit dated and it doesn't support MFA/TOTP. The modern approach is to use the Amplify UI Authenticator component, which supports TOTP and all the flows you'd expect (sign-up, password reset, etc).

Mobile authentication approaches, JWTs and refresh tokens

Context
I'm developing togther with my dev team a mobile app in a client-server architecture, since there will be a webclient too, allowing some users (admins) to perform certain operations from the browser.
The REST Api currently authenticates users by returning access and refresh tokens in form of JWTs. Both local (username/password) and OAuth2.0 (only Google at the moment) flows are available, as I provide the user with these two different options for authenticating.
Problem
The flows that follow are working just fine when the API is called from the webclient, but now that we've started developing the mobile app a big question arised: **how do we keep the user authenticated on the mobile app even after the refresh token expires?**
All the famous apps out there do not prompt the user to authenticate let's say weekly or worst daily, but still I'm sure their authentication practices are (almost) flawless.
Tried paths
I've read many blog posts and articles, together with some StackExchange Q&As as reported below, but the right way to approach authentication and access persistence on mobile is still unclear.
Should I create a specific endpoint (or many) to provide non-expiring tokens only when the User-Agent header tells the API is being called by a mobile device?
As mentioned in JWT (JSON Web Token) automatic prolongation of expiration Auth0 abandoned JWT for mobile in favor of random generated strings. What implementations are available in this case? Should I use this string as a never-ending id of the authenticated device and approve all API calls that have it attached?
In the OAuth case, should I perform (I don't know how) silent calls to the OAuth provider to get back a new idToken and then request new tokens to my own API with it?
In the local case, should I keep user credentials stored locally? If so, how do I do that securely?
Consulted resources
What's the right OAuth 2.0 flow for a mobile app
JWT refresh token flow
Authenticating a mobile application with JWT and refresh tokens
https://softwareengineering.stackexchange.com/questions/318471/jwt-refresh-token-exponentially
https://auth0.com/docs/best-practices/mobile-device-login-flow-best-practices
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
https://mobile-security.gitbook.io/mobile-security-testing-guide/general-mobile-app-testing-guide/0x04e-testing-authentication-and-session-management
https://tech.justeattakeaway.com/2019/12/04/lessons-learned-from-handling-jwt-on-mobile/ (more focused on improving an already established architecture)
...and many more I'm not reporting as outside the scope of the question.
This question was originally posted here, https://softwareengineering.stackexchange.com/questions/430302/mobile-authentication-approaches-jwts-and-refresh-tokens/430315#430315
Some diagrams
These are the flows we've currently implemented, working as espected when the API is consumed by a webclient.
Local
OAuth2.0
I don't think the requirement is well formed and it feels like it is based on a sweeping statement from product owners, without considering costs v benefits:
Gmail keeps me signed in forever and I want my app to work like that
LARGE PROVIDERS
The likes of Google often use bespoke solutions around analyzing user patterns, periodically getting 2 factor confirmation and other actions that would be very expensive for normal companies.
OAUTH
For normal software companies the problem has been solved via OAuth and the AppAuth pattern. Curity Guides provide a good starting point if you are not familiar with it:
Once coded you can use many authentication options with zero code changes in your UIs and APIs
User friendly password-less options such as WebAuthn are supported
You can even support advanced options such as App2App Logins if needed
Mobile code and the architecture remains simple in all cases
USER CONSENT
Note also that OAuth is built around users agreeing to the app using their details for a period of time. I often stop and think if I am abusing this - and what would be the impact if a user's device was stolen - not sure how relevant this is for your scenario ...
MIDDLE GROUND
For most companies I would recommend this type of option so that usability is good:
Start with a user friendly option such as 30 day refresh tokens
If you are using password logins, ensure that password autofill works - AppAuth will enable this
TOKENS
These are issued by an Authorization Server (AS) not developed by you. Think of this as a Docker Container that provides HTTPS endpoints - use a free or low-cost one.
The motivation behind the Auth0 point you mentioned is explained well in this article. The mobile app just sends access tokens to APIs. There is no token issuing in your code and it remains simple.
SUMMARY
Prefer industry standard proven options with good cost v benefit results. OAuth is highly architectural though and there is a learning curve which your company needs to manage.

What is the security difference between API Keys and the client credentials flow of OAuth?

Consider an API that a client accesses directly (machine to machine) and that doesn't require user-specific authentication. The way I understand it, in client_credentials, the client must store a client_id and client_secret that it uses to acquire and refresh tokens. With an API key, the client just stores the key. What makes OAuth more secure in this case? It would appear to me that if the API key is never compromised, no attacker could pose as the intended client. And if the API key is compromised, it is effectively the same as compromising the client_id and client_secret, which an attacker would be able to use to obtain tokens and access the data in the API, posing as the client.
edit: clarified this is a machine-to-machine call
TLDR;
The difference comes down to direct access vs. delegated access.
OAuth allows you to make delegated access. The benefits of delegated access don't change if there is a user involved or not. The same arguments that make the OAuth Authorization code flow attractive for user-to-machine access, apply to the OAuth Client credentials flow for machine-to-machine access.
Ask yourself, do you want the resource server to handle client credentials or not?
On confidential clients for machine-to-machine access, the cost of delegated access vs. direct access may very well outweigh the benefits. That's why so many APIs still use API keys. You'll have to decide that for your individual use case.
Differences
In the OAuth client credentials flow, the client sends an access token to the resource server, which it got beforehand by the authorization server after presenting its client ID and secret. The resource server never sees the client secret. With an API key, the client sends the key with every request.
OAuth adds an additional layer of indirection with the authorization server, such that the credentials themselves never get transmitted to the resource server. This allows the authorization server to give the client only access for a limited amount of time or with limited permissions, without ever needing to change the actual client credentials. It also allows to revoke access tokens without revoking the credentials themselves. For multiple instances of a client this allows you to revoke access for some but not all.
Of course this all comes at the cost of a more complex implementation, and an additional roundtrip from the client to the authorization server.
I won't touch on transmission (URL, header, body, etc.) or format (random string, signed JWT, etc.), since these can be the same for access tokens just as for API keys.
Another, maybe not so obvious, advantage of OAuth is having a clear spec that libraries, documentation and discussions can be based on. With direct access there is no single best practice and different people may understand different things when referring to direct access methods like API keys.
With client credential flow your Client Id and Client Secret are sent to the authorization server to get back an access token. For all subsequent request to the API/resource servers, you pass the access token and not the client credentials themselves. The access token is usually a JWT, which is a set of encoded claims including the token expiry (exp), not before (nbf), token issuer (iss), authorized party (azp), roles, permissions, etc.
This has a number of advantages over a simple API Key approach. e.g.
If the access token (which is included in requests to the API/resource server) is compromised, it's only valid until it expires (which is typically ~1 day for M2M tokens). If an API Key is compromised, it can be used indefinitely or until it's explicitly blocked by the API/resource server.
JWT access tokens are encoded JSON objects that contains a number of fields (a.k.a. claims) that can be used for fine grained authorization e.g. roles, permissions, grant type, authorized party etc. An API Key is generally opaque and is all or nothing when it comes to auth.
You machine tokens can get validated and authorized on the API/resource servers the same way as your user tokens, so you don't end up with multiple auth implementations on the back-end.
OAuth Client Credentials Flow
What is the security difference between API Keys and the client credentials flow of OAuth?
OAuth client credentials flow is not meant to be used by public clients, just between machines.
From auth0.com/docs:
Client Credentials Flow
With machine-to-machine (M2M) applications, such as CLIs, daemons, or services running on your back-end, the system authenticates and authorizes the app rather than a user. For this scenario, typical authentication schemes like username + password or social logins don't make sense. Instead, M2M apps use the Client Credentials Flow (defined in OAuth 2.0 RFC 6749, section 4.4), in which they pass along their Client ID and Client Secret to authenticate themselves and get a token.
So, I am not sure what is your scenario, but I will assume in my reply that you are referring to public clients.
If it is in the public client code, then it is public
The way I understand it, in client_credentials, the client must store a client_id and client_secret that it uses to acquire and refresh tokens.
Yes, it needs to be stored in the client code for the client to be able to obtain the OAuth token.
If you use the client_secret from a web app or mobile app you are making it public, therefore not a secret anymore.
Extracting secrets from public clients
For example, in a web app all it takes to extract the client_secret is to hit F12 in the browser and search for it, thus how much time can this take?
Now, in a mobile app, some may think it's secure because they are compiled into a binary but is almost as easy as it is in the browser, because we have several open-source tools that can help us with this task, like the MobSF framework, and on Linux, you can even achieve this with the strings command. Using the MobSF to perform static binary analysis on the mobile app binary allows for anyone without hacking knowledge to easily extract the client_secret in minutes, just like I show in my article How to Extract an API key from a Mobile App with Static Binary Analysis:
The range of open source tools available for reverse engineering is huge, and we really can't scratch the surface of this topic in this article, but instead, we will focus in using the Mobile Security Framework(MobSF) to demonstrate how to reverse engineer the APK of our mobile app. MobSF is a collection of open-source tools that present their results in an attractive dashboard, but the same tools used under the hood within MobSF and elsewhere can be used individually to achieve the same results.
So, the process of extracting the api-key in my article is the same you will use to extract the client_secret or any other string of your interest in the mobile app binary.
OAuth or API Key?
What makes OAuth more secure in this case? It would appear to me that if the API key is never compromised, no attacker could pose as the intended client. And if the API key is compromised, it is effectively the same as compromising the client_id and client_secret, which an attacker would be able to use to obtain tokens and access the data in the API, posing as the client.
If used from a public client neither are secure, because if read my linked article, you understand by now how easy is to bypass an API Key or extract the client_secret and client_id.
So, if your client is public you should not use the OAuth client credential flow, thus you need to go with the insecure API key approach or you can be more diligent and try to apply defence-in-depth approaches, but this will depend if the API clients are only web apps or mobile apps or both.
If your API clients are only web apps I invite you to read my answer to the question Secure API data from calls out of the app, especially the section dedicated to Defending the API Server.
In the case the API clients are only mobile apps then I recommend you to read this answer I gave to the question How to secure an API REST for mobile app?, especially the sections Securing the API Server and A Possible Better Solution.
On the other hand, if your API clients are both a web app and a mobile app I recommend you to apply the security measures more relevant to you from both answers linked above.
Remember that security is always about adding as many layers of defences as you can afford or it's required by law. Even in the past century, the castles were built with a lot of different security defence layers, thus this is nothing new to the digital era.
Do You Want To Go The Extra Mile?
In any response to a security question I always like to reference the excellent work from the OWASP foundation.
For APIS
OWASP API Security Top 10
The OWASP API Security Project seeks to provide value to software developers and security assessors by underscoring the potential risks in insecure APIs, and illustrating how these risks may be mitigated. In order to facilitate this goal, the OWASP API Security Project will create and maintain a Top 10 API Security Risks document, as well as a documentation portal for best practices when creating or assessing APIs.
For Mobile Apps
OWASP Mobile Security Project - Top 10 risks
The OWASP Mobile Security Project is a centralized resource intended to give developers and security teams the resources they need to build and maintain secure mobile applications. Through the project, our goal is to classify mobile security risks and provide developmental controls to reduce their impact or likelihood of exploitation.
OWASP - Mobile Security Testing Guide:
The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security development, testing and reverse engineering.
For Web Apps
The Web Security Testing Guide:
The OWASP Web Security Testing Guide includes a "best practice" penetration testing framework which users can implement in their own organizations and a "low level" penetration testing guide that describes techniques for testing most common web application and web service security issues.

Developer-authenticated end users with Google Cloud Platform

For authenticating end users to things like IoT services, many cloud services have a custom option: The client authenticates with the dev's own server (however the dev implements that), which in turn gets a token from the cloud service and sends that to the client for authentication with the cloud service. Amazon and Twilio are examples of this. This allows for a fully customizable auth.
If I understand correctly, Google Cloud Platform requires end users to authenticate with Google's OAuth2 service, meaning they must sign in with a Google account. I don't see any way around this, but the limitation is so severe that I wonder if I'm missing something. Is there some way I can instead authenticate users my own way?
meaning they must sign in with a Google account
That's not entirely correct, you probably overlooked this in the very doc you referenced (emphasis mine):
Firebase Authentication gives you a robust, secure authentication
system-in-a-box that helps you do sign in with any account your
users want to use. Firebase Authentication supports password
authentication in addition to federated sign in with Google, Facebook,
Twitter, and more, allowing you to easily scale your authentication
system as you grow on desktop and mobile.
So you can have your users choose their username and password or login using one of their supported 3rd party non-Google accounts.
But it will still be Google handling the authentication for you, which is good if you plan to use other GCP products/services as the authentication can be propaged.
If you want to handle the authentication yourself - nothing stops you from doing that, but it may be difficult/impossible to integrate it with other GCP products/services. The Plain OAuth 2.0 might be what you're looking for (I don't understand it enough), search for it in the Compare Auth Options guide.

KeyCloak should be used as auth server for my users?

So I want to have single sign in, in all the products using a auth server but that's not only for employees, keycloak should be used to that like auth0?
There are also some advantages to Keycloak:
Keycloak is also available with support if you buy JBoss EAP (see http://www.keycloak.org/support.html). This might be cheaper than the enterprise version of Auth0. If you want to use custom DB, you need enterprise version of Auth0 anyway.
Keycloak has features which are not available in Auth0:
Fine-grained permissions and role-based access control (RBAC) and attribute-based access control (ABAC) configurable via web admin console or custom code or you can write yuour own Java and JavaScript policies. This can be also implemented in Auth0 via user rules (custom JavaScript) or Authorization plugin(no code, less possibilities). In Keycloak you can do more without code (there are more types of security policies available out of the box e.g. based on role, groups, current time, an origin of the request) and there is a good support for custom developed access control modules. Here some more detailed research would be interesting to compare them.
Keycloak also offers a policy enforcer component - which you can connect to from your backend and verify whether the access token is sufficient to access a given resource. It works best with Java Web servers, or you can just deploy an extra Java Server with Keycloak adapter which will work as a gatekeeper and decide which request go through and which are blocked. All this happens based on the rules which you can configure via Keycloak web interface. I am not sure such policy enforcer is included in Auth0. On top of that, Keycloak can tell your client application which permissions you need when you want to access a given resource so you do not need to code this in your client. The workflow can be:
Client application wants to access resource R.
Client application asks Keycloak policy enforcer which permission it needs to access resource R.
Kecloak policy enforcer tells the client application which permission P it needs.
The client application requests an access token with permission P from Keycloak.
The client makes a request to the resource server with the access token containing permission P attached.
Policy enforcer which guards the resource server can ask Keycloak whether permission P is enough to access resource R.
When Keycloak approves, the resource can be accessed.
Thus, more can be centralized and configured in Keycloak. With this workflow, your client and resource server can outsource more security logic and code to Keycloak. In Auth0 you probably need to implement steps 2,3,6 on your own.
Both Auth0 and Keycloak should be able to achieve your goal - assuming you want only social (facebook, google etc), and /or username & password authentication?
Auth0 is the less risky option, keycloak is good for non-commercial & where you can afford production outages without a global 24x7 support team. Here a few other reasons why I'd recommend Auth0 - the documentation is world class, they have quickstart samples so you can get up and running in minutes, and easy access to more advanced options - passwordless, authentication, MFA, anomaly detection, x9's reliability, rate-limiting, an extensive management api, extensions for everything eg exporting logs to log aggregator, and so on. Anyhow, good luck with your project, and obviously what suits best may simply be down to your own project requirements.
Should add, if you are doing mobile, then Auth0 put a lot of effort into adding the necessary specialised security flows to target mobile (native / hybrid) apps. For instance, PKCE usage when using /authorize endpoint. Please bear that in mind, as not certain how keycloak has been implemented to handle this - alot of IDMs still do this incorrectly today.