i want to pass the API token obtained in the login controller as an HTTP header to allow the ApiKeyAuthentificator to read it using $apiKey = $request->headers->get('apikey');.
I used to make it work using query parameters but this seems unsafe.
In the login controller
I set an apikey http header before returning the reponse:
$response = $this->redirectToRoute('home');
$response->headers->set('apikey', $data->token);
return $response;
In the home controller
After successful redirection to home, if I var_dump the headers, the header is not set and request->headers->get('apikey') returns null.
However, after redirection, if I open the network inspector and look for the POST request from the login controller, the header is set.
But the header is not set in the GET request of the home, which I guess is why I cannot get it in the home controller using $apiKey = $request->headers->get('apikey');
In the ApiKeyAuthentificator
Same issue. Cannot access the HTTP header using $apiKey = $request->headers->get('apikey');
If I set a cookie, I manage to get it in the home controller and the authentificator, why can't I with a header ?
How to correctly set and access an HTTP header using symfony requests / responses, and make it accessible from the authentificator ?
You've misunderstood the purpose of HTTP request/response headers here. Headers themselves are not designed in this raw form, as you are doing, to indicate any kind of session or persistence. HTTP itself is very stateless, with no request being related to any other that comes along.
Your application can, and is, giving a header in its response but there is literally no reason at all why a web browser should include that same header again in future requests for the purpose of linking requests. Only a custom HTTP client/implementation, such as a bespoke native mobile/tablet app, would ever deliberately include an API key/token in HTTP headers directly. (No standard web browser would so this without some beefy Javascript implementation to handle literally all navigation.)
Cookies are exactly what you need here. Typically you don't need to worry about them too, because PHP has session variables of which you can store as much as you like, with a single cookie being used to associate future requests with a set of session variables. (Session identifier cookie, typically named "PHPSESSID".)
The login controller should be storing anything related to the user's authenticated session in PHP's session variables once it has verified the user is genuine. Once that has been done a cookie will be passed to the client web browser with no further action required from you.
--
This explanation is just conceptual though. Symfony being a very complete framework has taken care of this concept for you. The comment // or if you want to use an "apikey" header, then do something like this: is mainly for a custom web client (apps). If your goal is having a custom client side HTTP/web client then it's your job to remember token variables elsewhere in the app's memory, which would not be in a HTTP module as standard.
I think what you may need from the article I linked is specifically the part about storing authentication in the session. The HTTP client will then only need to provide an API key once.
--
As for safety, a HTTP header is no safer than a query variable to anyone smart enough to know how a HTTP request is structured. (This is basic knowledge of HTTP.) This is on par with post variables being no safer than query variables.
If you're using a standard web browser the only benefit is not seeing the API key in the address bar, but then you should be using a session cookie for these clients anyway rather than an API key in every subsequent request.
If you're creating a custom web client for an app then the custom header would be visible to someone snooping in on the HTTP traffic anyway just as easily as a query variable.
Enforcing HTTPS is the only way you could effectively hide a custom HTTP header or query variable from traffic sniffers, offering exactly the same protection to each part of the request, and subsequent response.
How can I make a logged-in-check via an external call (external domain) to a main server? Should be a js client-server call.
In other words domain1.com makes a client-server call to the main-domain.com to check if the user is already logged-in in main-domain.com
There are two ways you can:
Use simply api token authentication on main domain.
Setup OAuth on main domain.
What you are looking for is crossdomain request with cookies, this requires that you must specify some header output on the domains, you can find more information # this stackoverflow link.
You'll need some kind of handler on the main domain that can start the session, verify that it has the necessary session data that says its logged in and return true/false for the caller.
According to me, easiest way is to maintain a flag in your main domain.
So what you need to do is.
Add one field in users table, let's say isLoggedIn
Create an API which return true or false when you make a call from domain1.com
Handle data based on the result of the API.
I have a slightly strange scenario, but a problem that needs solving all the same!
Please note, the websites described below are on different top level domains
I have two web applications, 1 ASP.NET MVC, and another in PHP, both on separate domains. Lets call them asp.com and php.com. Users authenticate on asp.com, and therefore have an authcookie set by ASP.NET.
Now the php.com website fetches data via a rest service from asp.com. This rest service authenticates via the same mechanism, so when I call this rest service via javascript JSONP from php.com it works fine. However I wish to call the same REST service from the server in PHP.
Is it possible to somehow get the asp.com website to copy and set an authcookie for php.com (the domain is known and trusted), and then in the PHP code pass this cookie on to athenticate against the REST service on asp.com?
It doesn't need to be the exact AUTH cookie, I could create a new cookie with the relevant session key, an long as a valid authcookie could be created and submitted to the REST service.
Questions
Is this possible?
How do I set the cookie for php.com in asp.com?
Short of one of the domains becoming compromised, are there any
security concerns?
No
The first site, asp.com, will have to redirect to a page in php.com. Then php.com can set the cookie itself, and redirect back to asp.com.
Yes, which is why you can't do it.
Also, see this answer.
I'm building a Drupal site that has a little voting system. I have a PHP page which takes in parameters and updates the voting database accordingly. How do I allow this page to be accessed through this AJAX on my Drupal site but otherwise restrict access to outside users just going to this URL to prevent forged voting?
AJAX == regular HTTP request. You cannot distinguish an AJAX request from a "normal access". There is no way to prevent this.
To prevent wrong votes, you need to build such security into your voting system. Require registration, use sessions and nonces or whatever is necessary. But trying to "block non-AJAX requests" is not a solution.
You can rely on 'X-Requested-With: XMLHttpRequest' HTTP header that is appended to each Ajax request by most standard browsers.
Yet, as #deceze pointed out, it's far from being considered 'secure'. It's quite easy to issue 'normal' http request that include this header, either by using Fiddler or programatically.
If you just need to restrict voting by simple refresh of the browser address bar, just change the script to get the vote value using http POST verb.
I am building a REST API in PHP to work with a JavaScript based app. All requests are handled as JSON and some requests require authentication.
An example request would be:
$.ajax({
type: 'GET',
url: 'http://domain.com/api/posts/recent.json',
headers: {
Authorization: 'X-TRUEREST ' + $.param({
username: 'johndoe',
password: '********'
})
},
success: function(response){
// Handle response here (includes auth errors too)
},
error: function(a,b,c) {
}
});
I have based the use of HTTP Authentication headers on the code in this plugin: https://github.com/kvz/cakephp-rest-plugin/blob/master/Controller/Component/RestComponent.php#L532
As you can see they take passed parameters with the header and then they are used to log a user into the system if they are not already. From what I can tell they expect the auth credentials to be passed WITH the request for data.
An example of this is (note not using CakePHP in my example app):
if ( $loggedIn ) { // logged in is true of false based on session existing
// Then return the JSON as normal
} else {
// Grab HEADERS INFO
$headers = $_SERVER['HTTP_AUTHORIZATION'];
$parts = explode(' ', $_SERVER['HTTP_AUTHORIZATION']);
// Then use the parts to find a user in the DB... and populate $user
if($user){
$this->Auth->login($user); // login the user with our authentication code
// Then return JSON as normal
} else {
print json_encode(array('auth'=>false))
}
}
A few questions that I have though:
Question 1: Why use the HTTP Authentication and headers? As as far as I can tell, they are not offering me anything unless I am using them incorrectly? Why not just pass the username and password in the data param of the JS?
Question 2: As stated earlier I have based my API design on the Cake plugin above, but from what I can see they ALWAYS pass the username and password with every request and then decide to use it or not if the user is logged in. Is this correct? To me, it seems more likely that authentication and requests for data should be handled separately. Otherwise I would have to store the username and password in my JavaScript somewhere so that I can send it with each request.
Which brings me to the next question...
Question 3: How do I know if a user is logged in? I could set a variable to true and then pass that as part of my JSON with every request, but can a session work with an external device?
If you are designing a REST api, you should adhere to REST principles. There are two important ones to highlight for authentication:
Identification of resources through URIs
State transitions through server-supplied links.
To adhere to principle 1, you need to keep your authentication out of your URI. http://example.org/list-of-stuff?auth-token=12345 is not a different resource from http://example.org/list-of-stuff?auth-token=67890, so it should not have a different URI. Having different URIs also makes it impossible to cache the resource across different users.
In general, if the resource would be different depending on some condition, that condition needs to be in the URI. For example, many websites have a /profile url, but what profile you see depends on the invisible "who is logged in" state. This is not RESTful. Instead the url should contain the user, e.g. /profiles/username. Whether you actually get to see that resource depends on whether you are authorized to see it, which depends on whether you are authenticated as a user that is authorized. Authentication is a separate layer from resource identification. (For example, suppose you have an admin user which can see other people's profiles. If you have just a /profile url, how would you architect a method for him to see other profiles? Clearly the presence of a resource is something different from the ability to see it and from who is looking at it.)
So we have established that authentication should not be via parameters in the URI. Since we are using HTTP, we can either provide it in a header, or provide it outside HTTP itself.
Although it's not very common, some REST apis handle authentication at the SSL layer using client certificates. This is great from a technical standpoint, but the user experience is baffling and terrible. Although this article is from 2008, nothing has improved. This approach is also not easily scriptable from browser JS, and even outside the browser it's cumbersome to write an application that has to provide client certificates. Development on the server side is difficult, too, because most web scripting environments do not give you easy access to SSL-layer stuff at all, let alone more esoteric SSL features like client-certificates. Your application may not be able to know what certificate identity was provided with the request.
So that leaves HTTP in the header. We can either use a traditional cookie-based auth, where we "log in" at a special url and get a token back, or HTTP authentication which is natively supported by HTTP.
HTTP authentication is far superior in terms of RESTful principles because it is stateless. (This makes your third question nonsensical--there is no "logged in" or "logged out" state--you either provided the right credentials for the request or you didn't!) You don't need to ensure you first visited a special url and got a magic token you need to save (which might suddenly expire), you just issue a request with your credentials every time. If you are not authorized to access a resource, there's a specific HTTP response code (401) and header (WWW-Authenticate, Authorization) and set of well-defined behaviors. It's also extensible with different authorization methods. There's a fair amount of javascript support too, at least if you stick to XmlHTTPRequest. (Heck, jQuery 1.7.2 even has username and password options, so you don't even need to base64-encode it yourself!)
The downside is that the only HTTP auth methods that are in common use and well supported are Basic and Digest, and both of them are not very secure. They're probably fine if you only use https, but otherwise they are terrible.
Also HTTP auth is useless for normal browser use by humans: no custom login pages, no way to present "forgot password" functionality or other authentication customizations, and still browser makers have provided no easy way to "log out" (i.e., forget the credentials for the current realm)!
Cookie-based auth provides you the most control, but you need to keep server- and client-side authentication state and worry about a whole host of other security issues, such as session fixation or even what constitutes a session! Is it IP address, user agent, some combination? How long should an authenticated session be valid before we expire it? What if there's a proxy involved and the IP address changes frequently? What should we do when the magic token expires? What should we do when the user is not authorized? (You can't use an HTTP 401 response--you need to define your own method specific to your site.) In essence, you need to define your own complex session and authentication protocol or adopt someone else's. At least with HTTP auth, the only thing you need to worry about is an attacker reading your Authenticate headers, and using SSL solves that problem.
Question 1: Why use the HTTP Authentication and headers? As as far as I can tell, they are not offering me anything unless I am using them incorrectly? Why not just pass the username and password in the data param of the JS?
HTTP Authentication here appears to be a personal choice of the developer. You could use OpenID. You could use a token. You could use HTTP Header Authentication. You could use sessions/cookies. Heck, you could even use RSA Key Authentication or SSL Client Certificates if you liked.
Question 2: As stated earlier I have based my API design on the Cake plugin above, but from what I can see they ALWAYS pass the username and password with every request and then decide to use it or not if the user is logged in. Is this correct? To me, it seems more likely that authentication and requests for data should be handled separately. Otherwise I would have to store the username and password in my JavaScript somewhere so that I can send it with each request.
You shouldn't expose the username and password of anything in plaintext, ever (technically you shouldn't even store it as such, but that's not what the question is about). Use a public token with an API, or a unique cookie ID. Personally, I use $_SESSION["id"] to see if a user is logged into anything. Let the AJAX submit and then map the ID to a token internally, if you have to.
Question 3: How do I know if a user is logged in? I could set a variable to true and then pass that as part of my JSON with every request, but can a session work with an external device?
I have a function I use for this linked in this answer. You should ideally check for a valid login using a session and setting the value of an index (such as $_SESSION["id"]) to a value that is unique to that account (usually, this is their database ID).
Edit: after reading the comments on the OP, you would have to make sure the session cookie was on any device that was logged in before making the request
Do not take a value: true field to be a login, as this is a security vulnerability.
I think you are mixing (perhaps on purpose) between passing information to the REST api using the URI and authorizing access to the specific server with http simple Authentication.
HTTP authentication can also be used to handle users, but It wouldn't be a very good choice (as elobrated in other answers). users, sessions, security tokens etc should be handled by the app logic while api access can also be safe guarded by the server (with http simple or digest security, NTLM, IP filtering or whatever). letting the web server take some of the security burden/seperating concerns/some kind of testing site/etc'.
an example scenerio is handling http security to protected server access in a Testing zone server, while the app REST api would accept and handle different users, tokens, permission levels etc to everyone who passed the server security checks.
look for this question for more reference
Question 1: Why use the HTTP Authentication and headers? As as far as
I can tell, they are not offering me anything unless I am using them
incorrectly? Why not just pass the username and password in the data
param of the JS?
The query string is part of the URI. The URI is a resource identifier by REST, so it is part of the resource state. The identity, credentials, etc... of the user is part of the client state. Mixing the client state and the resource state by adding client state to the URI would be a violation of the stateless constraint of REST. The data part of other requests, like POST, PUT, DELETE is for describing resource representations, so you should not send auth data with it. Sending headers is the best approach and since HTTP has a dedicated Authorization header, you should use that.
Question 2: As stated earlier I have based my API design on the Cake
plugin above, but from what I can see they ALWAYS pass the username
and password with every request and then decide to use it or not if
the user is logged in. Is this correct? To me, it seems more likely
that authentication and requests for data should be handled
separately. Otherwise I would have to store the username and password
in my JavaScript somewhere so that I can send it with each request.
Your server should authenticate every request, in order to stay stateless. I am not sure about the logic of this lib, I think login is the wrong term to describe what happens, but from REST perspective the implementation details does not matter as long as it uses the HTTP standard properly, and the communication is stateless. The code has changed since you asked your question.
Question 3: How do I know if a user is logged in? I could set a
variable to true and then pass that as part of my JSON with every
request, but can a session work with an external device?
The server does not care whether the client is logged in, since it is client state. If you send a request that requires permissions, then you have to send the data necessary to authenticate the user.