Aws Cognito server side authentication / authorization - javascript

I am looking to use Cognito to implement full server side authentication flow. Due to requirements of my app, i need to control registration / sign-in / password-reset etc, on the backend instead of front end.
Iv been reading through the documentation and experimenting for hours to no avail.
i have set up server side credentials in which AWS Cognito states
Confidential client
A server-side application that can securely store a client secret. Cognito API requests are made from a central server.
I have attempted to use #aws-sdk/client-cognito-identity-provider to create the requests that i need but have had no such luck and the AWS documentation is convoluted at best.
This artical Seems kind of close but mentions nothing about client secrets.
Is what I want to achieve even possible, and if so can someone point me to some documentation on how to do this or any sort of example

Related

AWS Cognito - AdminInitiateAuth vs InitiateAuth

We're looking to leverage AWS Cognito for authentication with an architecture that looks like:
client (browser) -> our server -> AWS Cognito
With various configurations set, initiateAuth seems no different to AdminInitiateAuth and so I'd like to understand when under these configurations if it matters whether one is chosen over the other.
It seems that when I create an app with a client secret and use initiateAuth, it seems to be almost the same integration experience as adminInitiateAuth that uses the ADMIN_NO_SRP_AUTH auth flow. The latter does not even require AWS credentials as stated in the AWS documentation. My integration with Cognito is as below:
initiateAuth:
const payload = {
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: cognitoClientId,
AuthParameters: {
USERNAME: username,
PASSWORD: password,
SECRET_HASH: generateSignature(username)
}
}
const response = await cognitoClient.initiateAuth(payload).promise();
adminInitiateAuth:
const payload = {
UserPoolId: userPoolId,
AuthFlow: "ADMIN_NO_SRP_AUTH",
ClientId: cognitoClientId,
AuthParameters: {
USERNAME: username,
PASSWORD: password,
SECRET_HASH: generateSignature(username)
}
}
const response = await cognitoClient.adminInitiateAuth(payload).promise();
You can see the difference is the different AuthFlow values, calling different methods and ADMIN_NO_SRP_AUTH requiring the UserPoolId parameter which seems superficial to me.
We are also generating the signature based on the client secret which is something that we would handle securely.
I understand that you would like to know the difference between the InitiateAuth and the AdminInitiateAuth API calls in Amazon Cognito.
To clarify the usage of the API calls:
InitiateAuth is a client/browser side API call, and the API call does not need any sensitive credentials to give a challenge and other parameters.
AdminInitiateAuth is a meant to be run in the server side, and the API call always needs developer credentials to give a successful response. This is because the API call is an AWS SigV4 signed API call.
Furthermore, both the API calls support different Auth Flows as specified below.
InitiateAuth supports the following Auth Flows:
USER_SRP_AUTH
REFRESH_TOKEN_AUTH
USER_PASSWORD_AUTH
CUSTOM_AUTH
Kindly note that the AWS CLI documentation [a] currently states that ADMIN_NO_SRP_AUTH is a possible value. However, I have tested the API call on my end and I can confirm that the documentation for the CLI is currently incorrect.
UPDATE (12/09/2019): It looks like after this answer was written, Amazon Web Services has updated their documentation to the correct possible values. The documentation now states the following:
ADMIN_NO_SRP_AUTH is not a valid value.
AdminInitiateAuth supports the following Auth flows:
USER_SRP_AUTH
REFRESH_TOKEN_AUTH
CUSTOM_AUTH
ADMIN_NO_SRP_AUTH
USER_PASSWORD_AUTH
Example use-case of InitiateAuth: If you want your users to authenticate into your web application.
Example use-case of AdminInitiateAuth: Any use-case that needs server side authentication or access based on specific AWS Credentials to filter that only specific IAM users can authenticate using Cognito.
As stated by george earlier, InitiateAuth would be ideal for your use-case as your application is a client side application.
Additionally, if you are concerned about security, you could use the USER_SRP_AUTH with InitiateAuth. For more information about using the USER_SRP_AUTH flow in your production code, you could refer to the following NPM documentation[b].
References
[a]. https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/initiate-auth.html
[b]. https://www.npmjs.com/package/cognito-srp
initiateAuth and adminInitiateAuth do a similar thing, however, they have different use cases and flow.
initiateAuth is used when you have an end user client app. The user enters their creds and they are sent via Secure Remote Password Protocol. If the flow succeeds the end user gets a token back and is allowed access. This flow is used by the Android, IOS and Javascript SDKs because it's to do with the client side.
adminInitiateAuth is used when you don't have a client end user app but a secure back-end app using Java, Python or some other backend language. This method does not accept username-and-password user credentials for admin sign-in but requires AWS credentials.
In your case, if you had a client app ---> Cognito and use for example Android SDK or Javascript SDK directly then you should use initiateAuth from within the SDK passing the user credentials. However, browser -->back-end--> Cognito meaning you have a dedicated back-end so in your case you should adminInitiateAuth. More info here
AdminInitiateAuth only exists for one reason: so you can avoid the hassle of SRP in your server side code while still requiring client side code to use SRP.
SRP is more secure, but annoying/inconvenient to implement. Also, if you're writing code that runs on the server, a lot of the benefits that SRP provides are irrelevant anyway (your code is running in a secure, protected environment).
If you set up a Cognito app client like this:
[X] ALLOW_USER_SRP_AUTH
[ ] ALLOW_USER_PASSWORD_AUTH
[X] ALLOW_ADMIN_USER_PASSWORD_AUTH
... then any untrusted/public client side code must use SRP, but trusted server side code is free to use the plain old user/password flow. (Of course the server side code has to have AWS credentials in order to enjoy this privilege)
You're absolutely spot on though that the two are pretty much equivalent in functionality with some superficial differences.
I too spent quite some time researching scarce documentation on the topic of
when to use AdminInitiateAuth vs InitiateAuth.
https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html is supposed to help, but I find it poorly structured and very confusing.
From my understanding, you're right, you can use both approaches on the server:
InitiateAuth with AuthFlow=USER_PASSWORD_AUTH (requires app client to be created with client secret).
AdminInitiateAuth with AuthFlow=ADMIN_USER_PASSWORD_AUTH (replaced legacy ADMIN_NO_SRP_AUTH)
I believe second option makes more sense for the server usage scenario though. This way you can disable ALLOW_USER_PASSWORD_AUTH auth flow in the app client settings altogether. While probably not a huge risk, it feels cleaner to not have InitiateAuth API open to the public since it's not required.

React JS Rest API Security With GoLang Backend

I have built a ReactJS Frontend with GoLang Backend. All the data is being requested/sent by ReactJS from/to Golang Backend using Rest APIs.
I am quite confused on what will the best way to secure my API requests so that one does not pro-grammatically hit my backend server and make undesired changes. Any advice will be really appreciated.
I have been exploring JWT tokens and CSRF tokens but am not going anywhere with how to exactly implement it in my application due to my lack of expertise in the API security domain.
Some further details about my application are:
The frontend and backend server are separate.
The frontend server is using Nginx to serve the static files.
A new token should be generated everytime the page is opened and it should be valid for no more than 8 minutes.
Please suggest to me what would be the best way to secure my rest APIs given the structure of my application.
P.S.: There is no login or any other mechanism for this page which would establish the authenticity of the user.
I would suggest using JWT for API security as your token expiry time is short enough.
https://github.com/dgrijalva/jwt-go --> Use this package to generate token in backend and set expiry time and claims as per your need. Your claims can have granular control(Auth layer) too based on roles.
Send this token to your front end from login or any endpoint which is the gateway to your API.
Store the token in local storage/https cookie as per your wish at your front end.
With every API call which needed to be secured send this token in header, validate this token at back-end using the library mentioned above.
Challenge will be to invalidate these tokens on forgot or reset password/logout. You can use blacklist token technique by keeping them in redis/db and flushing them regularly with cron.

Oauth2 without server or with AWS lambda

I am trying to design an web application that will query data from Fitbit via its APIs and display it in different forms to the user. All this data functionality is implemented in Javascript and is executed on the client side (ie. in the browser) - there is no need for a backend or storage.
I am struggling however with the authentication. Fitbit provides Oauth2 and I have the following questions:
Must I have a server-side component (that offers a callback) or is it perhaps possible to handle it fully on the client side?
If there is a need for this server-side only for Fitbit's Ouath, does anyone has an example for doing it in AWS Lambda? I guess I would need only two functions: one for initiating the authentication and one for the callback. I am not sure however where/how to store the tokens and how to manage the user session.
You can perform oAuth authorization on the client side (JS) without any server side code using Implicit flow.
However there are few differences between server side implementation (Authorization Code flow) and client side implementaiton(Implicit flow). Specific details relevant to Fitbit-Implicit flow is listed here
You can use one of many oAuth2 client-side libraries to perform token exchange. I prefer using the oidc-client-js for oAuth2 token exchange (and for OIDC client side implementation)

Authenticate client-side app to REST API using CORS with local strategy

The Problem:
Serving a secure API to a client side app using only a local authentication strategy. The red arrows are part of the knowledge gap.
Context:
That is --- client.example.com is making a POST to api.example.com/login where on success client.example.com can gain access to a GET service like api.example.com/secret.
An idea!
Implimentation of OAuth 2.0 with hybrid grant type sitting in front of API.
Why hybrid?
It wouldn't be an Implicit Grant Flow aka Client-Side Web Applications Flow because there is no redirection to API server too grant access token. (i.e.) "Is it ok for so-and-so to access your data?"
It wouldn't be a Resource Owner Password Flow because a Client ID and Client Secret are passed along with the request so it's assumed the client app is server-side.
OK... so what about a little bit of both?
What if we used a CRSF token on page load of client-side app, and POST it with user credentials too OAuth 2.0 authentication endpoint to exchange for access token? You would authenticate each subsequent request with the access token and CRSF token after a successful login.
A good Node.js OAuth 2.0 library I found:
https://github.com/ammmir/node-oauth2-provider
Help Me!
I can not find a working example of an authentication measure that solves this problem! Point me in the right direction?
Ultimately, the goal here is too authenticate a client side app to a REST api using CORS with a local strategy --- i.e. username & password --- even if the convention above isn't possible.
To Accommodate Bounty:
This is a client side app, so let's stay trendy.
I'm looking for a working example using the Node.js OAuth 2.0 seed above for the API/Auth server and a front end framework like Angular.js or Backbone.js to make requests.
The example should match the context described above.
I'm working on an app with a pretty similar architecture though the services are .NET Web API rather than Node and we're using DotNetOpenAuth for the OAuth provider. Rather than the hybrid approach you're suggesting we're doing the following:
x.com serves up a login page
login page POSTs back credentials to x.com
server side logic at x.com combines client_id and client_secret with the credentials to submit a token request (resource owner password credentials grant that you've
mentioned above) receiving back both a temporary access token and a
refresh token
the refresh token is encrypted into a cookie issued by x.com
both the cookie (with encrypted refresh token) and the temporary access token are then sent to the browser
the client app (angular in my case) can now use the access token to hit api.x.com for services (It appears you're well aware of the limitations of CORS... we hacked a version of angular's $resource to facilitate this but it wasn't pretty since we wanted to use all HTTP verbs and support IE9)
when the access token expires, the client side app can request a new access token from x.com
server-side, x.com decrypts the cookie to get at the refresh token and issues another oauth call for a new access token
This is fairly high-level but hopefully gives you a sense for how to tackle your situation. In my case, and it appears in yours, we didn't want to use session state or a database to store the refresh token but obviously exposing that to the browser introduces security concerns so the encryption of the refresh token is important (among other security considerations) and the use of the cookie eliminates the need for session state or other persistent storage on x.com.
Not an answer running for the prize. Just my 2 cents :)
On my web server,
I do my authentication through a rest call with login/password with basic authentication over https. This call delivers a key to the client (a one page web app).
Then every subsequent REST call is signed with the key. The server checks that the signature is correct and everything still happen in https.
This mechanism is quite used I believe.
I don't see the issue with cross domain. I have a single source anf if I need something from another source, I'd use JSONP.
I use nginx as an https->http forwarder.
Not sure how it competes with an OAuth2 solution.
I've built this example using Node and PassportJS to show how to authenticate the users with Facebook or Local Strategy. Both sides are on different domains as you described and it requires CORS enabled.
GitHub: https://github.com/pablodenadai/Corsnection
Live demo: http://corsnection-client.herokuapp.com/
I can't promise that I have time to write working example but I can show you 2 paths :)
The biggest deal is CORS. After you solve that problem it is easy to use $http service. So, first and probably easiest may be to configure reverse proxy in x.com webserver which points to api.x.com. I wrote article here
Second approach is better, and created for exactly this purpose, to authorise specific domain to use your resource. It involves a bit of coding in api.x.com so you don't have to change anything in new web applications served in other domains. You simply need to authorise CORS requests in api.x.com service.
Create table in database where you can manage list of authorised domains
Add in that table record "x.com"
in api.x.com add request filter/interceptor what ever tech term you use for method which should be invoked after request is handled and add in response Access-Control-Allow-Origin: x.com if request comes from x.com (in other words check in request header refer value match to any value in table above and put that value in Access-Control-Allow-Origin response header).
That is all :) After this if you know how to use $http or jQuey.ajax you will be able to POST/PUT/DELETE/... any request to api.x.com from any authorised domain in just few minutes.
I very similar idea using vinilla js web app and cross domain authentication to GAE backend or OpenID connect.
The web app is run on CDN. When click login link, it goes to respective login server and redirect back to the web app (with XSRF security token and HTTPS only cookie). Login server accept cross domain request with credentials. XSRF token has to be set (in header) with every request. cookie is set by the browser. Since it is HTTP only cookie, JS cannot read it. The technique is very secure.
Once login, you can get secure assess from login server.
For detail description, you can find here and open source repo here.

Securing my Node.js app's REST API?

I could do with some help on my REST API. I'm writing a Node.js app which is using Express, MongoDB and has Backbone.js on the client side. I've spent the last two days trying to work out all of this and not having much luck. I've already checked out:
Securing a REST API
Securing my REST API with OAuth while still allowing authentication via third party OAuth providers (using DotNetOpenAuth)
http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/
http://tesoriere.com/2011/10/10/node.js-getting-oauth-up-and-working-using-express.js-and-railway.js/
I want to keep my backend and frontend as separate as possible so I thought about using a carefully designed REST API would be good. My thinking is that if I ever get round to developing an iPhone app (or something else like that), it could use the API to access data.
BUT, I want this to be secure. A user has logged into my web app and I want to ensure my API is secure. I read about OAuth, OAuth 2.0, OpenID, Hmac, hashes etc... I want to avoid using external logging in (Facebook/Twitter/etc) I want the registering and logging in to be on my app/server.
...but I'm still confused here. Maybe it's late at night or my brain is just fried, but I could really do with some steps on what to do here. What are the steps for me to create a secure API?
Any help, any information, any examples, steps or anything would be great. Please help!
In order of increasing security / complexity:
Basic HTTP Auth
Many API libraries will let you build this in (Piston in Django for example) or you can let your webserver handle it. Both Nginx and Apache can use server directives to secure a site with a simple b64encoded password. It's not the most secure thing in the world but it is at least a username and password!
If you're using Nginx you can add a section to your host config like so:
auth_basic "Restricted";
auth_basic_user_file /path/to/htpasswd;
(Put it in your location / block)
Docs: http://wiki.nginx.org/HttpAuthBasicModule
You'll need to get the python script to generate that password and put the output into a file: http://trac.edgewall.org/browser/trunk/contrib/htpasswd.py?format=txt
The location of the file doesn't matter too much as long as Nginx has access to it.
HTTPS
Secure the connection from your server to the app, this is the most basic and will prevent man in the middle attacks.
You can do this with Nginx, the docs for it are very comprehensive: http://wiki.nginx.org/HttpSslModule
A self-signed certificate for this would be fine (and free!).
API Keys
These could be in any format you like but they give you the benefit of revoking access should you need to. Possibly not the perfect solution for you if you're developing both ends of the connection. They tend to be used when you have third parties using the API, eg Github.
OAuth
OAuth 2.0 is the one to go with here. While I don't know the underlying workings of the spec it's the defacto standard for most authentication now (Twitter, Facebook, Google, etc.) and there are a ton of libraries and docs to help you get those implemented. That being said, it's usually used to authenticate a user by asking a third party service for the authentication.
Given that you doing the development both ends it would probably be enough to put your API behind Basic HTTP Auth and serve it over HTTPS, especially if you don't want to waste time messing around with OAuth.
Here's a different way of thinking about it:
Let's suppose for a moment that you're not using an API. Your user logs into the app, providing some credentials, and you give a cookie or similar token of some sort to the user, which you use to identify that user has logged in. The user then requests a page containing restricted information (or creating/modifying/deleting it), so you check that this token to ensure that the user is allowed to view that information.
Now, it sounds to me that the only thing you're changing here is the way that information is delivered. Instead of delivering the information as rendered HTML, you're returning the information as JSON and rendering it on the client side. Your AJAX requests to the server will carry that same logged-in token as before, so I suggest just checking that token, and restricting the information down to 'just what the user is allowed to know' in the same way.
Your API is now as secure as your login is - if anyone was to know the token necessary for accessing the api, they would also be logged into the site and have access to all the information anyway. Best bit is, if you've already implemented login, you've not really had to do any more work.
The point of systems such as OAuth is to provide this 'logging in' method, usually from a third party application and as a developer. This would potentially be a good solution for an iPhone app or similar, but that's in the future. Nothing wrong with the API accepting more than one authentication method!
The answers so far do a great job of explaining, but don't give any actual steps. I came across this blog post that goes into great detail about how to create and manage tokens securely with Node + Passport.
http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/
Tips valid for securing any web application
If you want to secure your application, then you should definitely start by using HTTPS instead of HTTP, this ensures a creating secure channel between you & the users that will prevent sniffing the data sent back & forth to the users & will help keep the data exchanged confidential.
You can use JWTs (JSON Web Tokens) to secure RESTful APIs, this has many benefits when compared to the server-side sessions, the benefits are mainly:
1- More scalable, as your API servers will not have to maintain sessions for each user (which can be a big burden when you have many sessions)
2- JWTs are self contained & have the claims which define the user role for example & what he can access & issued at date & expiry date (after which JWT won't be valid)
3- Easier to handle across load-balancers & if you have multiple API servers as you won't have to share session data nor configure server to route the session to same server, whenever a request with a JWT hit any server it can be authenticated & authorized
4- Less pressure on your DB as well as you won't have to constantly store & retrieve session id & data for each request
5- The JWTs can't be tampered with if you use a strong key to sign the JWT, so you can trust the claims in the JWT that is sent with the request without having to check the user session & whether he is authorized or not, you can just check the JWT & then you are all set to know who & what this user can do.
Node.js specific libraries to implement JWTs:
Many libraries provide easy ways to create & validate JWTs, for example: in node.js one of the most popular is jsonwebtoken, also for validating the JWTs you can use the same library or use express-jwt or koa-jwt (if you are using express/koa)
Since REST APIs generally aims to keep the server stateless, so JWTs are more compatible with that concept as each request is sent with Authorization token that is self contained (JWT) without the server having to keep track of user session compared to sessions which make the server stateful so that it remembers the user & his role, however, sessions are also widely used & have their pros, which you can search for if you want.
One important thing to note is that you have to securely deliver the JWT to the client using HTTPS & save it in a secure place (for example in local storage).
You can learn more about JWTs from this link

Categories

Resources