4

I've built a website that provides search results on legal documents. Among the features of the site is a REST API. Currently when somebody submits a query, the entire page is reloaded and the API is hit server side. I'd like to rebuild the site so that as people tweak search parameters, the results are paged in client-side using AJAX that hits the REST API.

The API requires a login (so that it can be throttled if needed), but using the site in general doesn't.

How can I make it so that people can use the site without logging in, but their use of the site hits the API client side?

The best ideas I currently have are:

  1. Just put a username and password into the site's JavaScript and use them to log into the API when making queries. Give these credentials unthrottled access to the API. This could work, aside from putting credentials into my JavaScript.

  2. Continue doing all API work server-side, foregoing the speed and UX improvements that come from dynamic page reloads.

Doing number 1 sucks and doing number 2 is basically giving up on having the kind of site I want.

Ideas?

mlissner
  • 17,359
  • 18
  • 106
  • 169

3 Answers3

4

Perhaps this is a duplicated of "How to make sure that my AJAX requests are originating from the same server in Python"? I don't want to flag it in case I just don't understand the difference in what this poster is asking vs that poster.


According to your site's API info page, you're using tastypie, and BasicAuthentication is being used for your API users

Have you tried combining BasicAuthentication with another authentication option via MultiAuthentication? Perhaps you could use SessionAuthenticaion and using an authentication backend that allows anonymous. That way API users would continue to pass their username/password (API key if you go that route), and users of the site would fall back to an anonymous session, authenticated via SessionAuthenticaton.

Or instead of SessionAuthentication, create a custom authentication, as demonstrated here, and use some logic to determine if the consumer resides on the same host as the API.


Tastypie's ApiKeyAuthentication method can be configured to use a custom header...

enter image description here


Migrated and incorporated from comments:

Based on How does Google Maps secure their API Key? (I prefer Franci Penov's answer to the accepted one) If I were going to implement this I would use a two step process of ApiKeyAuthentication to confirm a valid API key, followed by a "referral check", to make sure the key is coming from the expected source. This is made simpler if the key is calculated based on the source, as suggested in the answer linked above.

There's actually code in tastypie's SessionAuthentication method (authentication.py) that does something similar...

...

referer = request.META.get('HTTP_REFERER')

if referer is None:
    return False

good_referer = 'https://%s/' % request.get_host()

if not same_origin(referer, good_referer):
    return False

...
Community
  • 1
  • 1
Eric Lease
  • 4,114
  • 1
  • 29
  • 45
  • Thanks for the response, but I think the thrust of my question is how to consume the API without leaving it wide open like you're suggesting. It does seem like some kind of custom authentication is needed, but I don't know what that would be. – mlissner Apr 09 '15 at 20:15
  • @mlissner So a new session is created for each API call in django, meaning SessionAuthentication, even with a backend allowing anonymous, would succeed, thus API consumers could not be throttled. Am I understanding that correctly? Well what if you tried to distinguish between API calls from localhost (assuming site and api are on same host) vs remote host? - using the custom authentication. I will do some more research when I have time. – Eric Lease Apr 09 '15 at 21:44
  • @mlissner I guess it doesn't make sense to try to differentiate between your site making the API call and someone else's making the API call, since the request is actually coming from the client. You obviously need some way to identify your site as an elevated user. When the initial request comes in for your site you can generate a temporary, unique API Key for that session, grant it unrestricted access to the API, and provide it to the client to use. Then you can have that API key expire with the session. – Eric Lease Apr 09 '15 at 22:08
  • @mlissner So even if you keep basic authentication for "3rd party" users of the API, you could give just yourself an API key and then secure it in your app following one of the many conventions out there. These links might be a useful starting point: [Securing API Keys in a Client Side JavaScript App](http://billpatrianakos.me/blog/2013/09/12/securing-api-keys-in-a-client-side-javascript-app/); [How does Google Maps secure their API Key?](http://stackoverflow.com/q/2256305/4342563) I found an answer in the 2nd link that sounds a similar to the method similar I mentioned in a comment above. – Eric Lease Apr 10 '15 at 13:59
  • I think this link about Google Maps is the best thing I've seen. It's a lightweight solution, but if it's good enough for Google maps, it should be good enough for me. I'm a little worried about referrer headers being slowly deprecated, but I'll do some more research and see if that's really a problem. Thanks for the research. – mlissner Apr 10 '15 at 17:29
  • If you want to put the concept about referrer headers in your answer, I'd be happy to award the bounty. – mlissner Apr 10 '15 at 17:32
3

In addition to the answers already provided here, one suggestions of what you could do in this situation is that you can have a custom authorization headers. One example could be the way amazon does it.

Having a custom authorization header reqiures you to create the header yourself.

What you could do is have the client(the one that makes the call, not necessarily the user) authenticate once per session. Store that auth-token somewhere, and use it to sign each request to the rest-api. That auth-token would only be valid for a certain period of time, so no danger in someone missusing it.

Here is one other article I've found on authorization with custom headers in .NET

TheBoyan
  • 6,802
  • 3
  • 45
  • 61
  • But if the user has to authenticate, that defeats the purpose. That'd be like saying you have to be signed in to use Google. The goal is that the user doesn't authenticate but the API does. – mlissner Apr 09 '15 at 20:17
  • @mlisner The user doesn't have to authenticate, you haven't even seen the model in the amason link, have you? The instance does....Instance as being the client, in that authentication header that you provide you are telling the API: "hej, this is me" – TheBoyan Apr 09 '15 at 21:29
  • @mlissner the whole sentence "The goal is that the user doesn't authenticate but the API does." doesn't make much sense to be honest. You are authenticating youself as a client towards an API. regardless if that is a user-based or applicatin-based, there still must be an auhtentication – TheBoyan Apr 09 '15 at 21:38
  • 1
    Thanks for the comments. I read through the Amazon link and I'm convinced there's really no good way to do this. The best solution really seems to be to use the referrer header, as mentioned in @eric-lease's comments. – mlissner Apr 10 '15 at 17:23
1

Best thing for you is Oauth2. It will address your problem in a handy way.

What you want is Give some privileges to your web site to access your REST API freely and Other users to have to log in using some sort of username and password (In case of Oath2, it will be a token.) , which is easily achievable by using Oauth2.

Here are some good reading materials.

  1. http://alexbilbie.com/2014/11/oauth-and-javascript/

  2. https://spring.io/team/dsyer (If you use Spring and angularjs)

  3. How do I implement secure OAuth2 consumption in Javascript?

Community
  • 1
  • 1
Maleen Abewardana
  • 13,600
  • 4
  • 36
  • 39
  • I read through these a bit, but they don't seem to help because they still require that my JavaScript log into OAuth2 and get a token. If my JS can do that, then people can look at the JS, figure out the logic, and go around the authentication. – mlissner Apr 09 '15 at 20:30
  • 1
    @mlissner what's the point of not having the authentication on your website then? If the user's can just freely use the API trough your webpage and access the content, then you might as well give them the credentials :) – TheBoyan Apr 09 '15 at 21:43
  • @mlissner If it is the case, Google and facebook would never use it. You must have a good idea about your endpoints. Take a look at this, https://developers.google.com/identity/protocols/OAuth2UserAgent – Maleen Abewardana Apr 10 '15 at 05:24
  • I think this answer boils down to using Oauth2 to generated a token that I can use in my client code. Not a bad idea, but I think I'm looking for a lighter weight solution. OAuth has consistently given me headaches.... – mlissner Apr 10 '15 at 17:28