I'm making a simple authentication system using Playframework with their built-in CSRF filter and Security.Authenticator system, but I'm facing a problem :
When the user fill his login/password and submit enter, I have the following error :
CSRF token not found in session
I checked my form and the CSRF token is really present and correctly placed (inside the tag)
Here is my routes
:
GET /login controllers.Authentication.login
POST /login controllers.Authentication.authenticate
And my Authentication.java
class :
@play.filters.csrf.AddCSRFToken
public static Result login() {
if (Session.getAccount() != null) {
return redirect(controllers.routes.Instances.list());
}
LoginForm loginForm = new LoginForm();
if (ctx().session().containsKey("email")) {
loginForm.setEmail(ctx().session().get("email"));
}
if (request().queryString().containsKey("disconnected")) {
flash("warning", Messages.get("secure.logout")); // TODO : Warning flash message
}
Form<LoginForm> form = Form.form(LoginForm.class).fill(loginForm);
return ok(views.html.login.render(form));
}
@play.filters.csrf.AddCSRFToken
@play.filters.csrf.RequireCSRFCheck
public static Result authenticate() {
Form<LoginForm> form = Form.form(LoginForm.class).bindFromRequest();
if (form.hasErrors()) {
return badRequest(views.html.login.render(form));
}
LoginForm login = form.get();
Account account = Account.findByEmail(login.getEmail());
if (account == null || !account.checkPassword(login.getPassword())) {
form.reject("email", "secure.invalid.login");
return badRequest(views.html.login.render(form));
}
String next = null;
if (ctx().session().containsKey("next")) {
next = ctx().session().get("next");
if ("".equals(next)) {
next = null;
}
}
session().clear();
session("email", login.getEmail());
if (next != null) {
return redirect(next);
} else {
return redirect(controllers.routes.Instances.list());
}
}
The properties I setted in the application.conf :
csrf.token.name="csrf"
csrf.sign.tokens=true
Here's my form (to show that the token is correctly positionned) :
<form action="@controllers.routes.Authentication.authenticate" method="post" class="form-vertical" role="form">
<fieldset>
@defining(form("email")) { element =>
<div class="form-group fade-in two@if(element.hasErrors){ has-error}">
<label class="control-label" for="[email protected]">Email:</label>
<input type="email" name="@element.name" id="[email protected]" class="form-control" value="@element.value" placeholder="Enter your email" required />
@element.errors.map { error => <span class="help-block">@play.i18n.Messages.get(error.message)</span>}
</div>}
@defining(form("password")) { element =>
<div class="form-group fade-in three@if(element.hasErrors){ has-error}">
<label class="control-label" for="[email protected]">Password:</label>
<input type="password" name="@element.name" id="[email protected]" class="form-control" value="@element.value" placeholder="Enter your password" required />
@element.errors.map { error => <span class="help-block">@play.i18n.Messages.get(error.message)</span>}
</div>}
<div class="form-group actions fade-in four">
<div class="text-right">
<input type="submit" name="save" value="Sign me in" class="btn btn-primary" />
@helper.CSRF.formField
</div>
</div>
</fieldset>
</form>
Update 1: I removed all the @addCSRFToken
and @requireCSRFToken
and defined a global CSRF token instead, per @rhj recommandation. Now I got this error instead :
Invalid token found in form body
But as you can see in the HTML Form, the token is placed inside the form and should be identified!
Update 2: What bothers me the most, is that this problem only occurs in the Login page, and not elsewhere! Is there some session to enable first? I guess not but maybe ?!
Update 3: More odd, I just found out that if I change the value of csrf.token.name
and reload the page, it works. Then if I log out, open a new page and try to logs again, it fails with the message Invalid token found in form body
. If I again change the value of csrf.token.name
and do it again, it will works again.
Update 4: I narrowed down the problem. I also use the play.mvc.Security.Authenticator and it seems that the token verification fails ONLY when I display the page after a redirect()
. If I go straight to the login page, I won't have the error message displayed.
Final update! : I finally found the problem, it was in another class that I wasn't suspecting, that was called and cleared the session, making the token useless!
Finally the problem was located in an other part of my code that was cleaning the session in the login()
method, removing the csrf token with it ...