Reputation: 71
I'm writing a Play application where I would need the authentication to be also handled by another web application. So, when the user logs into the other web application it should also log into the Play application.
To implement security in Play I used the instructions in the Play Framework documentation: http://www.playframework.com/documentation/2.0.1/ScalaSecurity
My idea on how to do the external authentication, is to have the other application do an ajax call to log into the Play application, as I thought this would write the session cookie for the user. But this doesn't work. I still have to login manually when to the Play application.
Here is my controller:
val loginForm = Form(
tuple(
"username" -> nonEmptyText,
"password" -> nonEmptyText) verifying("Invalid email or password!", result => result match {
case (email, password) => Admin.authenticate(email, password)
}))
def jsLogin = Action {
implicit request => {
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(toJson("Unauthorized!")),
user => {
Ok(toJson("Ok")).withHeaders(
ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
ACCESS_CONTROL_ALLOW_METHODS -> "POST",
ACCESS_CONTROL_MAX_AGE -> "300",
ACCESS_CONTROL_EXPOSE_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept"
).withSession("email" -> user._1)
})
}
}
And here is the code I've used to test this:
$.ajax({
type: "POST",
url: "http://localhost:9000/jsLogin",
data: {
username: "username",
password: "password"
}
})
After debugging, I know that the jsLogin method works ok and it does log the user in, and the response get's ok to the ajax method. But when I try to access my play application it still asks me to log in manually.
Is there some non kludgy way to get the user logged in from the outside?
Upvotes: 4
Views: 853
Reputation: 71
Ok, I got it to work. What I noticed is that the Set-Cookie header returned by the call was deemed Unsafe. To fix this I had to get the CORS headers correctly and send Credentials. So here is my new Query (used XmlHttpRequest instead of jQuery's ajax for debugging reasons, should work with ajax also).
var xmlhttp = new XMLHttpRequest();
var url = "http://localhost:9000/jsLogin";
var params = "username=username&password=password";
xmlhttp.open("POST", url, true);
xmlhttp.withCredentials = true;
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(params);
And here's the play controller
def jsLogin = Action {
implicit request => {
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(toJson("Unauthorized!")),
user => Ok(toJson("Ok")).withHeaders(
ACCESS_CONTROL_ALLOW_ORIGIN -> "http://sending.host.url",
ACCESS_CONTROL_ALLOW_METHODS -> "POST",
ACCESS_CONTROL_ALLOW_CREDENTIALS -> "true",
ACCESS_CONTROL_ALLOW_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept").withSession("email" -> user._1)
})
}
}
So the fix was in having "withCredentials = true" on client side and on server side have the CORS headers set up correctly.
Upvotes: 1
Reputation: 11284
So you want to handle security by using Play's session cookie? Have you checked the cookie is actually there after the AJAX request? Is "the other" app on the same domain? If no, the cookie won't be used by the other app.
By the way, a better way to handle this is described in a blog post by James Ward.
Upvotes: 0