AWS Cognito - AdminInitiateAuth vs InitiateAuth - javascript

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.

Related

Why isn't clientID enough for using Google APIs?

From Google's API explorer and using Authorize requests using OAuth 2.0 as I want to on my SPA, I see a YOUR_API_KEY is somehow derived from a login and used in subsequent calls to:
https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCE5Au4LfcBHxTQR_yLbncrQ&key={YOUR_API_KEY}
How do I get this key? I am working from GAPI.auth2.getAuthInstance() in a VueJS app.
I can see gapi being used in Google samples. But I just want to do simple direct fetch calls with this user's API key for the session. I do not want to use gapi.client, or do I have no choice?
Update: On the Oauth2 playground I see it refers to a client secret 1. I don't understand this at all, since with a SPA with no backend, you don't want to store a secret! Why isn't the client ID and the origin enough?
Here's another confusing example (no secret used) where the apiKey is used together with the clientId. Why isn't clientId enough, since it's restricted by origin?!
Generally, there are 2 types of OAuth flows to obtain an access_token when on the web. One is called implicit flow and other is called authorization code flow.
For the code flow, you would require the client_secret in order to exchange a code with an access_token. This usually happens on server side.
For the implicit flow, you can simply give a client_id to obtain an access_token and it is designed to work on client side.
The screenshot you've attached is the OAuth flow part where you exchange an auth code with an access_token. Because the playground is using the authorization code flow.
What you are looking for should be the implicit flow.
To achieve this in YouTube client library (or whatever Google javascript client lib), you don't need a client_secret. You can find a complete example using Google Drive here:
https://github.com/GoogleChrome/google-sign-in/blob/master/static/scripts/authorization_client.js

authentication using Doorkeeper from client-side application without transmitting secrets

I'm trying to use Doorkeeper in a Ruby on Rails API app for authentication. From a client-side AngularJS app, I want to get an access token. Currently, this involves a GET request to /oauth/authorize, which gives me a code, then I POST that code along with a client_id and a secret to /oauth/token. I don't want to have to send the client ID and secret from my client-side app, since they're stored in plain-text in a JavaScript file. I would like to follow this flow where response_type is token, not code, but I can't figure out how to do that with Doorkeeper. Their wiki examples all seem to involve POSTing the client ID and secret to /oauth/token.
Is Doorkeeper the right gem for this? How can I do Google OAuth2 from a client-side app, where no secrets are passed from client-side to the server?
Edit: looks like what I want is Implicit Grant, which Doorkeeper supports. Now I just have to find out how to do that in my Rails app...
No extra server-side configuration necessary.
When I created a new Doorkeeper::Application in my Rails app, the Doorkeeper interface gave me an Authorize link with response_type=code in it for that application.
I changed that to response_type=token and when I do a GET request to that, it responds immediately with access_token instead of code. The Authorize URL looks like http://my-rails-doorkeeper-app/oauth/authorize?client_id=1234&redirect_uri=http://my-angularjs-app&response_type=token.
Update :
This can only be applied when we also allow implicit grant for the grant flow.
By default, doorkeeper will allow its four kinds of flow (implicit grant, authorization grant, password, and client_credentials).
You can configure it in initializer/doorkeeper.rb if you don't want to let it happens since sometimes it can be dangerous.

Safe Twitter OAuth authentication in JavaScript / jQuery (plus server side helper)

What is the best way to do Twitter OAuth authentication safely in JavaScript?
I am trying to write a program to let the user analyze his Twitter usage and followers / friends. I've written a server side version which works using the python tweepy module.
I would like to share it with people, but I would like it to run in the browser to be scalable vs. running on my small server.
I see another question where the upshot is that it's not recommended and not safe:
JavaScript OAuth sign in with Twitter
Which makes sense if one were sending the consumer (app) secret or access (user) secret in the app's JavaScript.
But why couldn't I build the URL on the server side like here -
http://djangosnippets.org/snippets/1353/
Then send the authentication URL back to the browser, something like this from the OAuth Tool on Twitter's My Applications page (not valid credentials)
GET&https%3A%2F%2Fapi.twitter.com%2F1%2F&get%252Faccount%252Fverify_credentials_json%3D%26oauth_consumer_key%GD0bgcgMU4MDwNfKQpLFQS3%26oauth_nonce%3D24ad5049501dee1292afd8cf22307d68%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1329173626%26oauth_token%uPUpxsBc3D283768289LtQ6R1Ez1KeD8DOSsm5XpqJaKI28ysYh%26oauth_version%3D1.0
Then have jQuery use that to authenticate with the user's credentials and run the analysis.
It's a significant piece of work, I'd hate to do that and then find out it doesn't work or is an unsafe approach. (or it's already been done)
Is that safe? It doesn't seem to expose any secrets.
Will that work?
Any pointers/examples on the right way to do the authentication for a jQuery noob, with the necessary Authorization: header and cookie/redirect processing?
I feel like I'm missing something and either there's a reason this won't work, or it should already exist somewhere, but haven't found it. Many thanks!
The problem Mr. McNuts, is that the oAuth requires you to pass in your consumer secret, so even if you build the URL on the server, you'll still pass it back to the webpage, which will still expose your consumer secret via an HTTP Proxy.
To prevent exposing your secret, you'll need to use a proxy to do the twitter auth request, and return the oauth token back to the browser. If you're really worried about scale, I would look at a pay-for-scale solution like GAE or Heroku.
I don't understand very well the approach you are proposing. But in general terms OAuth can not be done safely implemented on a browser client side (except secure close environments like Java or Flash). Implementing OAuth process on Javascript is quite possible but you will expose your secret/consumer token. So anybody would be able to supplant your application identify with mischievous intentions like stealing your users sensitive data.
If you still want to work with JS, I recommend you to implement the secure process (authentification and final token storage) on server side using Node.js

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

Amazon SimpleDB Javascript interface possible?

I'm checking out amazon simpledb documentation. They mention only server side languages.
Is there anyway to insert data into the db directly from the client side without going through a server?
If not, how come?
Yes and no. Since you need to protect your secret key for AWS (hackers could use it to abuse your account), you can't authenticate requests in JS directly.
While you could create an implementation in JS, it would be inherently insecure. Practical for some internal uses, it could never be safely deployed (as that would expose your secret key). What you could do instead is use your server to authenticate the requests to SimpleDB and let the JS perform the actual request to Amazon. Though it's a bit roundabout, it would work.
The downside is that you'd need to do a bunch of processing on the client side. You're also likely fetching more data than your app consumes/outputs, so processing the data on the client instead of on the server would likely encounter more latency simply because you're transferring more data to the user and processing it more slowly.
Hope this helps
If not, how come?
Security. You authenticate to the DB with your developer account. Amazon does not know about your end users (which it would need to, in order to authenticate access directly from the browser). It is up to the application code to determine what end users are allowed to do and what not.
That said, there is the Javascript Scratchpad for Amazon SimpleDB sample application. It does access SimpleDB directly from the browser (you have to type in your AWS credendials).
SimeplDBAdmin is a Javascript/PHP based interface:
http://awsninja.com/2010/07/08/simpledbadmin-a-phpmyadmin-like-interface-for-amazon-simpledb/
The PHP side is a relay script[relay.php] which will pass the requests made from the Javascript client and send them on to the server, takes the response from the server and reformats it for the client. This is to easily get around the cross-domain problems with Javascript[if the web client had downloaded the web page containing the javascript code from www.example.com it will only allow javascript to connect back to www.example.com by default].
Everything else, including request signing, is done by the Javascript code.
Also note that Amazon has released a new beta service recently to allow you to setup sub-accounts under your Amazon account. The simpleDB protection is very basic[either on or off per account] but as it does provide some limited form of request tracking, it could be argued that using Javascript and giving each user their OWN userid and key for request signing is MORE secure. Having every user use the SAME userid and certificate would, of course, be insecure.
There is a free, pure JavaScript interface available. Please see https://chrome.google.com/webstore/detail/ddhigekdfabonefhiildaiccafacphgg
See this answer to the similar question on allowing secure, anonymous, read-only access to SimpleDB from untrusted clients: anonymous read with amazon simpledb .
Some variations from that answer:
don't set access policy to read-only. However, it allows fine grained control, so you may still wish to limit the kind of writes allowed
don't be anonymous. The AWS docs on token based auth and example apps show parallel paths: anonymous access or non-anonymous AWS/federated access with your credentials but without exposing your secret key.

Categories

Resources