I'm designing a public API to my company's data. We want application developers to sign up for an API key so that we can monitor use and overuse.
Since the API is REST, my initial thought is to put this key in a custom header. This is how I've seen Google, Amazon, and Yahoo do it. My boss, on the other hand, thinks the API is easier to use if the key becomes merely a part of the URL, etc. "http://api.domain.tld/longapikey1234/resource". I guess there is something to be said for that, but it violates the principle of the URL as a simple address of what you want, and not how or why you want it.
Would you find it logical to put the key in the URL? Or would you rather not have to manually set HTTP headers if writing a simple javascript frontend to some data?
It should be put in the HTTP Authorization header. The spec is here https://www.rfc-editor.org/rfc/rfc7235
If you want an argument that might appeal to a boss: Think about what a URL is. URLs are public. People copy and paste them. They share them, they put them on advertisements. Nothing prevents someone (knowingly or not) from mailing that URL around for other people to use. If your API key is in that URL, everybody has it.
It is better to use API Key in header, not in URL.
URLs are saved in browser's history if it is tried from browser. It is very rare scenario. But problem comes when the backend server logs all URLs. It might expose the API key.
In two ways, you can use API Key in header
Basic Authorization:
Example from stripe:
curl https://api.stripe.com/v1/charges -u sk_test_BQokikJOvBiI2HlWgH4olfQ2:
curl uses the -u flag to pass basic auth credentials (adding a colon after your API key will prevent it from asking you for a password).
Custom Header
curl -H "X-API-KEY: 6fa741de1bdd1d91830ba" https://api.mydomain.com/v1/users
passing api key in parameters makes it difficult for clients to keep their APIkeys secret, they tend to leak keys on a regular basis.
A better approach is to pass it in header of request url.you can set user-key header in your code .
For testing your request Url you can use Postman app in google chrome by setting user-key header to your api-key.
I would not put the key in the url, as it does violate this loose 'standard' that is REST. However, if you did, I would place it in the 'user' portion of the url.
eg: http://me#example.com/myresource/myid
This way it can also be passed as headers with basic-auth.
Related
I need to configure Keycloak to get a JWT token as in this blog post, but I have to do it with cUrl. They create a client and then update it setting access type to confidential, Direct Grant Flow to direct grant, and Browser Flow to browser. The PUT request from the web UI that does this has some uuids that they seem to have pulled out of nowhere. Here is the relevant part of the payload:
"authenticationFlowBindingOverrides":{"browser":"6d77c4c7-15cf-4474-9b9f-7439dbc83b83","direct_grant":"5cb10cdb-9902-4f7f-b9da-68f887c49a75"}
The docs for the ClientRepresentation are no help. They show all fields are optional, which doesn't make sense, and the authenticationFlowBindingOverrides is a Map, but the link in their docs for the Map is dead.
Does anyone know where they get the uuids for browser and direct_grant from?
There is also nothing in the PUT payload that sets the Access Type to confidential.
If anyone has a cUrl implementation of the UI steps in the blog post that would be greatly appreciated.
The PUT request from the web UI that does this has some uuids that
they seem to pull out of nowhere.
Those uuids are generated by keycloak to get them you need to call the endpoint:
GET KEYCLOAK_HOST/auth/admin/realms/<YOUR_REALM>/authentication/flows
From the JSON response you need to parser it and get the field id of both the alias: "browser" and the alias: "direct grant".
After that call the endpoint:
PUT KEYCLOAK_HOST/auth/admin/realms/<YOUR_REALM>/clients/<YOUR_CLIENT_ID>
with the following payload:
'{"publicClient":false,"clientAuthenticatorType":"client-secret","authenticationFlowBindingOverrides":{"direct_grant":"<DIRECT_GRANT_ID>","browser":"<BROWSER_ID>"}}'
There is also nothing in the PUT payload that sets the Access Type to
confidential.
You need to set the field publicClient to false.
I have a GET url http://ip.domain/api/items to fetch data from the database and display it (pagination -- the users aren't logged in, else I'd have used a token). But the thing is, that It's public and anyone can use it. Is it possible to accept calls only from my domain? I completely understand that It's impossible to totally secure it. But just to make it a bit difficult for those who will try to.
Sure, add the following header to your script:
Access-Control-Allow-Origin: http://ip.domain
Set the domain to your actual domain.
Here's a good article on it: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
We received a request to create a REST api. I was a little confused in the example of provided by our client. As you can see below, they've identified the app_id and secret in the URL before the #. The remainder of the URI looks like what I would expect.
Is this valid? I thought maybe this is some weird cURL format I haven't seen before.
https://{application_id}:{api_secret}#api.example.com/entity/{entity_id}/
https://{application_id}:{api_secret}#api.example.com/entity/{entity_id}/entity_locations/{locations_id}/
Just seeing if anyone has seen this format before?
A URI is made up of various parts, one of them being the authority part, which can feature optional username:password element.
The full scheme is:
scheme://username:password#domain:port/path?query_string#fragment_id
This way your REST api remains stateless [not relying on previous app states like storing stuff in session]. But I advice you not to explicitly go with the username:password#stuff route, but to rely on Basic HTTP Auth, so the credentials are sent encoded in Base64 at least.
EDIT: a brief note about BasicAuth now you're asking - things go like this:
you make a request to http://johndoe:12345#service/api/foo/bar;
are credentials good? Ok, you get a 200 OK response with proper body;
are they not? You get a 401 Unauthorized response.
In the latter case, it's the browser [or any other program / script performing the request] that should prompt the user with the login popup.
Usually browsers ask you to cache credentials not to ask them every time, but this does not mean that they are not sent - it's just that every request to protected resources are featured with such header:
Authorization Basic base64encode(username:password)
Where base64encode is your custom way to encode the username:password string.
I'm using the ASIHTTPRequest lib in my iOS app for making REST requests to a web app. I'm doing my best to use the correct verbs (GET, POST, PUT, DELETE) when making the various requests, but when making a POST request, I'm not sure I understand why it matters if I include the parameters in the POST request or in the URL. It works both ways, so why should I include the parameters in the POST request instead of just including them in the URL? As I understand it, the only reason for include the parameters in a POST request is to keep them from being visible in the URL in case someone is looking over your shoulder, or something like that. But if I'm making a POST request from my iOS app and there's no browser involved, then does it really matter which way I do it?
Thanks so much for your wisdom, I'm still learning!
When using a POST request, it is good practice to put the parameters in the data instead of the URL. In your case, it works to put it in the URL, but this isn't always true. Some scripts will expect the parameters to be in a specific place and not find them if they aren't there. As for what POST is good for, it allows you to send more data. The URL is limited to a length of 255 characters, so you need to use some other method if you want to send more data than that. The data in a POST request also doesn't need to be encoded to be compatible with the URL specification.
As I understand it, the only reason for include the parameters in a POST request is to keep them from being visible in the URL in case someone is looking over your shoulder, or something like that.
You misunderstand it.
There are other issues. If your site makes changes to data based off a GET request, it's possible that spambots, search engines, browser prefetchers, and other automated tools will trigger potentially destructive data changes.
If the endpoint isn't under your control, it's entirely possible that it won't even accept the parameters as GET parameters. Most APIs require proper usage of the GET vs. POST verbs.
Just wanted to get feedback on how I am planning to architect my API. Dummy methods below. Here's the structure:
GET http://api.domain.com/1/users/ <-- returns a list of users
POST http://api.domain.com/1/users/add.xml <-- adds user
POST http://api.domain.com/1/users/update.xml <-- updates user
DELETE (or POST?) http://api.domain.com/1/users/delete.xml <-- deletes user
Questions:
Is it OK to use just GET and POST?
Is it a good idea that I plan to rely on the filename to indicate what operation to do (e.g. add.xml to add)? Would it be better to do something like this: POST http://api.domain.com/1/users/add/data.xml?
What's a good way to keep these resources versioned? In my example, I use a /1/ after domain name to indicate version 1. Alternatives would be: http://api1.domain.com... or http://api-1.domain.com... or http://apiv1.domain.com... or http://api-v1.domain.com... or http://api.domain.com/v1/... or
What's the best way to authenticate?
Before you dig into REST, here are some terms you really need to grasp:
Resource - The things/data you want to make available in your API (in your case a "User")
URI - A universally unique ID for a resource. Should mention nothing about the method being performed (e.g. shouldn't contain "add" or "delete"). The structure of your URI however doesn't make your app any more or less RESTful - this is a common misconception.
Uniform Interface - A fixed set of operations you can perform on your resources, in most cases this is HTTP. There are clear definitions for the purpose of each of these HTTP methods.
The most unrestful thing about your URIs as they are right now is that they have information about the operation being performed right in them. URIs are IDs and nothing more!
Let's take a real world example. My name is Nathan. "Nathan" could be considered my ID (or in restful terms URI – for the purpose of this example assume I'm the only "Nathan"). My name/ID doesn't changed based on how you would like to interact with me, e.g. My name wouldn't change to "NathanSayHello" when you wanted to greet me.
It's the same for REST. Your user identified by http://api.domain.com/users/1 doesn't change to http://api.domain.com/users/1/update.xml when you want to update that user. The fact that you want to update that user is implied by the method you're using (e.g. PUT).
Here is my suggestion for your URIs
# Retrieve info about a user
GET http://api.domain.com/user/<id>
# Retrieve set all users
GET http://api.domain.com/users
# Update the user IDed by api.domain.com/user/<id>
PUT http://api.domain.com/user/<id>
# Create a new user. The details (even <id>) are based as the body of the request
POST http://api.domain.com/users
# Delete the user ID'd by api.domain.com/user/<id>
DELETE http://api.domain.com/user/<id>
As for your questions:
Use PUT and DELETE when appropriate and avoid overloading POST to handle these functions as it breaks HTTP's definition of POST. HTTP is your uniform interface. It is your contract with the API user about how they can expect to interact with your service. If you break HTTP, you break this contract.
Remove "add" altogether. Use HTTP's Content-Type header for specifying the mime-type of posted data.
Are you referring to the version of your API or the version of the resource? ETag and other response headers can be used to version the resources.
Many options here. Basic HTTP Auth (easy but insecure), Digest Auth, custom auth like AWS. OAuth is also a possibility. If security is of main importance, I use client side SSL certs.
1) On your design probably not. POST is not idempotent! So you should not use for the update or the delete, instead use PUT and DELETE from Rest
2) A better choice is to use the header Content-Type on the WS call, like: application/xml
3) Also on the header Content-Type u can use it: application-v1.0/xml
4) Not sure if it is the best, but probably the easiest way is to use HTTP's built-in authentication mechanisms in RFC 2617. An example: AWS Authentication
In REST, the HTTP "verb" is used to denote the operation type: you won't be able to express all the CRUD operations with only "GET" and "POST"
no: the URL of the resource is usually where the "document identifier" should appear
The version of the "document" can be transmitted in an HTTP response header upon creation/modification of the said resource. It should be the duty of the server to uniquely identify the resources - trying to do this on the client side will prove a daunting challenge i.e. keeping consistency.
Of course, there are many variations on the topic...
I did authentication based on headers. Something like
X-Username:happy-hamster
X-Password:notmyactualpassword
If you're concerned about security - do it through SSL.
Other implementations exist, of course. For instance, Amazon with their S3:
http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html
If you don't have ability to make PUT and DELETE requests, it's considered a good practice to tunnel them through POST. In this case the action is specified in URL. If I recall correctly, RoR does exactly this:
POST http://example.com/foos/2.xml/delete
or
POST http://example.com/foos/3.xml/put
...
<foo>
<bar>newbar</bar>
</foo>
It's a bit offtop, but in regards to versioning and REST overall you might want to take a look at CouchDB. Here is a good book available on-line
Using post for create and delete functionality is not a good rest api design strategy. Use Put to create, post to update and delete to delete the resources.
For more information on designing rest apis follow the link - best practices to design rest apis