Is AWS API Gateway custom authorizer useful? - authentication

I am looking to use some "serverless api server" for AWS Lambda /zappa that uses a custom API Gateway authorizer for user authentification. In serverless AWS lambda service is there a considerable security or cost benefit in using custom authorizer rather than checking the issued JWT token directly in your code controller? For me checking with the code could be more convenient.
UPDATE
I went for pre request hooks, however there is header level authorizer, it is easier to use for CORS, yet it is not supported by zappa I believe. Also setting mock API for Options might be possible via swagger upload, will update if succeed.

I don't think there is strictly speaking a security or cost benefit. The benefit is that it is a single place, a single piece of code, that handles your authorization. It prevents you from having to duplicate that authorization code in every single Lambda function you deploy behind your API. And it allows you to update a single function to make any changes to your authorization logic.
From the perspective that it provides a single source of truth for your authorization logic, and it allows you to implement separation of concerns, it could be said to enhance your application's security.
That being said, if your entire API consists of a single Lambda function, then the benefit is somewhat dubious. I think API Gateway Custom Authorizers really become beneficial when your API has many Lambda functions or when your API Gateway is sitting in front of traditional HTTP servers.

#Mark B makes excellent points I certainly don't dispute anything in his answer. I'd like to contribute to the conversation nevertheless.
An answer tailored more specifically to you might depend on where the JWTs come from, and how they're being acquired, used, and refreshed. Using a custom authorizer may make sense in these scenarios:
Use Case 1
Custom authorizers can be useful if you want to secure a single Lambda behind several different flavors of authorization. For example, you can create three different API Gateway endpoints that each invoke the same Lambda, but use distinct authorizers. This ties into Mark's point about the DRY benefits.
Use Case 2
Custom authorizers afford you the ability to build IAM permissions inline in your authorizer code. Rather than assigning a pre-existing IAM role to a caller, you can build any arbitrary set of permissions you like. Note that this can easily become a nasty attack vector if you're somehow using (untrusted) user input to assign IAM permissions.
Use Case 3
Lambdas are great for hiding secrets. For example, you have a front-end JS app and you need to participate in OAuth 2.0 flows that require client id and client secret. Or you need to call endpoints that require API keys of some sort.
Clearly, you can't expose these secrets to the browser.
These values can be encrypted and stored in environment variables specific to the Lambda function. While you could certainly take this approach with your back-end lambda, using an authorizer instead has the following benefit:
I like being able to restrict the scope of these secrets as tightly as possible. By using an authorizer, my application can remain blissfully unaware of these secrets. This ties into Mark's point about separation of concerns.
IAM and Least Privilege
I prefer that my back end code never gets invoked by unauthorized parties. For this reason, I use an authorizer of some type on virtually every API Gateway resource I create. I have used custom authorizers, but they're kind of my last resort. I lean on IAM authorization most of the time, using Cognito to trade tokens for temporary IAM credentials.
If you perform authorization in your back-end lambda, rather than in an authorizer, you can't be as restrictive when defining the IAM contols around your back-end lambda. This is a violation of the principle of least privilege. This isn't just a matter of code organization and application architecture; it's a legitimate security concern. You're essentially exposing more attack surface than you have to.
Furthermore, the real power of IAM shines when your back end grows. Your back-end lambda may need to write to S3, invoke other Lambdas, publish to SNS or SQS, interact with RDS or DynamoDB, etc. I'd suggest that "best practice" dictates that all of this access is governed by strict IAM policies. In my experience, using an API Gateway authorizer (of any type, not necessarily custom) to assign a role is the most straightforward way to accomplish this.

To the cost part of the question.
Authorizer will be a separate charge for one additional lambda call, and new cpu time with its own 100ms minimum charge.
This may be significant costs if there are many short lambdas in backend, could be doubling cost of each of them.

Related

Handling authorization with IdentityServer4

I'm extremely confused on how to use a centralized IDP with both authentication and authorization. The architecture for my project was to be a single web API and one React client. I wanted to keep things structured out into microservices just to try something more modern, but I'm having major issues with the centralized identity, as many others have.
My goal is fairly simple. User logs in, selects a tenant from a list of tenants that they have access to, and then they are redirected to the client with roles and a "tid" or tenant id claim which is just the GUID of the selected company.
The Microsoft prescribed way to add identity in my scenario is IdentityServer, so I started with that. Everything was smooth sailing until I discovered the inner workings of the tokens. While some others have issues adding permissions, the authorization logic in my application is very simple and roles would suffice. While I would initially be fine with roles refreshing naturally via expiration, they must immediately update whenever my users select a different tenant to "log in" to. However, the problem is that I cannot refresh these claims when the user changes tenants without logging out. Essentially, I tried mixing authorization with authentication and hit a wall.
It seems like I have two options:
Obtain the authorization information from a separate provider, or even an endpoint on the identity server itself, like /user-info but for authorization information. This ends up adding a huge overhead, but the actual boilerplate for the server and for the client is minimal. This is similar to how the OSS version of PolicyServer does it, although I do not know how their paid implementation is. My main problem here is that both the client and resource (API) will need this information. How could I avoid N requests per interaction (where N is the number of resources/clients)?
Implement some sort of custom state and keep a store of users who need their JWTs refreshed. Check these and return some custom response to the caller, which then uses custom js client code to refresh the token on this response. This is a huge theory and, even if it is plausible, still introduces state and kind of invalidates the point of JWTs while requiring a large amount of custom code.
So, I apologize for the long post but this is really irking me. I do not NEED to use IdentityServer or JWTs, but I would like to at least have a React front-end. What options do I have for up-to-date tenancy selection and roles? Right when I was willing to give in and implement an authorization endpoint that returns fresh data, I realized I'd be calling it both at the API and client every request. Even with cached data, that's a lot of overhead just in pure http calls. Is there some alternative solution that would work here? Could I honestly just use a cookie with authorization information that is secure and updated only when necessary?
It becomes confusing when you want to use IdentityServer as-is for user authorization. Keep concerns seperated.
As commented by Dominick Baier:
Yes – we recommend to use IdentityServer for end-user authentication,
federation and API access control.
PolicyServer is our recommendation for user authorization.
Option 1 seems the recommended option. So if you decide to go for option 1:
The OSS version of the PolicyServer will suffice for handling the requests. But instead of using a json config file:
// this sets up the PolicyServer client library and policy provider
// - configuration is loaded from appsettings.json
services.AddPolicyServerClient(Configuration.GetSection("Policy"))
.AddAuthorizationPermissionPolicies();
get the information from an endpoint. Add caching to improve performance.
In order to allow centralized access, you can either create a seperate policy server or extend IdentityServer with user authorization endpoints. Use extension grants to access the user authorization endpoints, because you may want to distinguish between client and api.
The json configuration is local. The new endpoint will need it's own data store where it can read the user claims. In order to allow centralized information, add information about where the permissions can be used. Personally I use the scope to model the permissions, because both client and api know the scope.
Final step is to add admin UI or endpoints to maintain the user authorization.
I ended up using remote gRPC calls for the authorization. You can see more at https://github.com/Perustaja/PermissionServerDemo
I don't like to accept my own answer here but I think my solution and thoughts on it in the repository will be good for anyone thinking about possible solutions to handing stale JWT authorization information.

OAuth flow for third party API access

There is a lot of information on the web about OAuth 2, its different types of flows and where/how to use them. I find that most of these resources discuss authenticating a user for an application, but I am struggling to understand what the best/correct approach would be when consuming third party APIs (i.e. when our own API is the "middleman" between a user and their data in a third party API).
With the help of an example scenario and some diagrams, I would be really grateful for advice/opinions on how I should properly implement integration with third party APIs and what the pros & cons of each approach are.
Starting point
As a starting point, suppose we have a web app, structured as follows:
Frontend - SPA (Angular), hosted with AWS S3 + Cloudfront
Backend - Node, running as stateless AWS lambda functions with AWS API Gateway
Auth0 for handling auth/signin etc. The frontend uses an Implicit OAuth2 flow to obtain access_tokens, which are stored in local storage and included as a header in all requests to the backend.
Potentially also native mobile app(s), consuming the same backend API.
Goal
Now suppose that we wish to add integration with Google Sheets. The new feature would allow users to use their own Google Sheets (i.e. stored in their own Google account) as a data source, with the app having read&write access to the sheet. There may be other integrations in the future, so I am assuming that other APIs would require a similar process.
Problem statement
In addition to the existing OAuth process (which allows users to sign in to the "MyApp" frontend and communicate with the "MyApp API"), there needs to be an additional OAuth process for users to connect MyApp to the third party Google Sheets API.
The documentation has two Quickstart examples, but neither seems to quite fit my needs:
Browser - https://developers.google.com/sheets/api/quickstart/js
Node.js (console app) - https://developers.google.com/sheets/api/quickstart/nodejs
The data from the third-party (Google) API is one of potentially several integration points, so intuitively it seems more logical (and more secure) that all communication with the Google Sheets API should happen in the MyApp API, and not on the frontend/client side. The MyApp API would fetch data, process/manipulate/format it in some way and then present it for display in the frontend or mobile apps.
We require access to each user's own data, so the Client Credentials flow is not suitable. I am focussing on the Implicit or Authorization Grant workflows.
Important note: The trickiness seems to come from the fact that the MyApp API is stateless, so there is no long-lived session in which to store tokens. On that basis, it seems like tokens need to be stored either in the frontend (e.g. local storage/cookies etc) or in a backend database.
Below is my interpretation of two possible approaches. I'd appreciate thoughts/corrections.
Option 1: Implicit flow - tokens stored FE, passed along to BE which then makes requests to Google
Pros:
Allows access to user's own data
Simpler flow, access_token retrieved immediately without needing the code step
Less steps to implement between initial sign-in process and actually obtaining data
No need for a backend database, can resend the token with each request
Cons:
Frontend (browser) has access to Google access_token which seems unnecessary and is a potential security concern
It seems like a strange process to pass the access_token from FE to BE, purely to allow BE to then use that token to make another request
I'm not sure how we would refresh/renew tokens since I understand that storing refresh_tokens on the client is bad practice. It would not be a good user experience if the user had to frequently sign in to reconnect their account
Option 2: Authorization Code Flow - all communication with Google via BE, tokens stored in BE database
Pros:
Allows access to user's own data
Other than the code-request / consent page, all communication with Google is implemented backend, so the tokens are not accessible on the client
Client secret can be used from the BE
Cons:
More complex flow, requires extra steps
Given that the BE is stateless, it's not clear how best to store the tokens. It seems like it would require storing them in a database which is extra complication and seems like it would have security implications - how would you properly secure / encrypt the access_token/refresh_tokens in said database?
Conclusion
Given that the data processing is to happen on the backend, option 2 seems slightly more suitable because the sensitive tokens can be hidden from the frontend app, and several clients (web frontend, mobile apps) have less obligation to be involved in the process with the exception of the initial sign in / user consent. However I’m not sure whether having a database full of user auth tokens is a good idea or how I could properly protect this database.
The good news is that both options are perfectly valid and equally secure. The concern about a short-lived Access Token being in the browser isn't an issue. Equally, if you only held the tokens on the BE, then you would need to implement your own client authentiation/session/JWT blah blah, which presents the same attack surface.
I've done both, and am currently migrating from BE to FE. In my case the reason is that everything I need to do, I can do on the FE, so I end up with no BE at all. This isn't strictly true since I do some onboarding/payment with the BE, but that's about all.
So the best approach depends on factors beyond those in your question, such as the nature of app, what the BE cost is and how important that is, what your devops skillsets look like for maintaining two environments, to what extent a BE is required anyway, vs being completely optional.

How can i limit access to the aws-cognito service?

I would like to limit the access to my cognito service.
My scope is to filter all the calls to cognito adding a server layer that allow me to do specific actions when the user uses the service.
I know that triggering events is the right way to do this but the events in cognito are very limited and a lot of stuff can not be done. For example if the Authentication fails for some reason the postAuthentication event is not triggered.
Another use case can be the validation of the attribute, or limit the reading of some attribute.
The only idea I've is to hide the IdentityPoolId or the UserPoolClientId but seems pretty unsafe.
There are two approaches in using AWS Cognito UserPools.
Using Hosted UI.
Using the SDKs/RESTAPI and Implement Your Own API and UI interface.
Using Hosted UI
If you go with the first option, pretty much of the things are handled by AWS and the control you have is limited. However, you can do certain configuration using triggers. On the other hand, you have lesser risks from your side since you are maintaining a limited code.
Using the SDKs/RESTAPI
If you go with the second option, you can limit Cognito UserPools as required putting the logic inside your own code. This allows limiting access as you desire. However, you have to write custom code to authenticate and signup users.
Mix of Both
It is also possible to use a mix of these approaches. For example, you can write custom code for signup but for sign in use hosted UI.
Custom Attributes and Claims
Also if you plan to return attributes based on your custom rules, use the trigger Pre token generation. Amazon Cognito invokes this trigger before token generation allowing you to customize identity token claims.
Note: A similar approach can be done for the Cognito Identity Pools.

How to get OAuth 2.0 right for consuming external APIs in my Custom API .net core

I want to create a custom API that behind the scenes, call number of other APIs which use OAuth 2.0 for authentication. I want to manage this internally so that my custom endpoint somewhat abstract this.
Or to begin with I want to do what app like buffer (https://buffer.com) do - where you connect to different social services and than post your status.
How can I achieve this in .NetCore ?? I don't want to login with these (a lot of samples are catering this scenario), user login is different than this. I just want to establish these connections (like API Connections if you look at Azure API Management) and then perform some operations against those endpoints.
I hope i convey my point. please let me know if this isn't clear.
Thanks
Sanjay
OAuth2 systems are all based on the same workflow.
here's an authorization url, you pass some ids in an authorization header, if everything is correct you get a token, you then use the token to do whatever you are allowed to do. What changes are the credentials you use for authentication and the urls you hit for the various parts of this workflow.
You could write your own OAuth2 library which deals with all this, that's pretty much what I did and simply changed the details for every specific system I had to interact with.
This being said you can always use one of the existing implementations to connect to the various systems you care about, they all have an API you could use, all you have to do is make sure you follow the OAuth2 flow correctly.

Should I use data contained in an authentication JWT on the client-side?

A server provides a JWT to the client during authentication. That JWT contains information which is then later used by the server. For example, JWT may contain permissions array with the list of all permissions granted to a specific user.
Is it considered bad practice, if client parses the JWT and uses the permissions information contained within it? Is it better for client to make additional call to server (GET /permissions, for example) and behave according to that response?
This will strongly depend on a lot of small details; I'll try not to forget anything, but in theory it should be fine to do so and if certain conditions are met I would not consider it a bad practice.
OAuth2 states that access tokens should be opaque to clients, but JWT is just a token format (Learn JSON Web Tokens) and it's usage in other circumstances does not imply the same rules as OAuth2.
Also note that getting the information from an additional request has the same end result with the additional overhead of one more call. There would be a slight benefit if permissions are very volatile given you could repeat the calls.
However, the important part is more focused on what you mean by the client and how would the client use that information so I'll elaborate on this.
Assumptions:
the client you mention can be deployed as browser-based application (SPA's), native application or be some server-side component acting as a client.
both the server and client are controlled by the same entity.
the client and server components can be seen as a single application, that is, for an end-user the fact there's client and server components makes no difference; they use them as a whole.
Explanation
In this situation the token issued by the server is just a way for the client to later access protected resources without requiring explicit user authentication again; it's a mechanism to maintain a session between the two components.
Given the same entity controls both the client and server, it's acceptable to treat the received token as a whitebox instead of a blackbox. The client can then interpret the information in the token and take advantage of it to provide a better experience for the end-user. However, this implies that the server will need to continue to validate the token and it's permissions accordingly; any interpretation of the data by the client is purely to provide optional functionality.
Furthermore, for clients deployed to hostile environments like it would be the case for a SPA application the decisions taken by looking into the data must only result in purely aesthetic decisions, as the user could fake the permissions data. For example, you could use it to conditionally hide/disable some user interface just so that the user wouldn't have to click it to find out it wasn't allowed to do so.
A good analogy would be Javascript based input validation in web forms; you should do it for better user experience, but the server will need to do it again because the user can bypass the Javascript validation.