Rails CSRF token issue in Angular routes - ruby-on-rails

I am using AngularJS routes with rails. For authencation I am using using Angular Devise directive. I am facing some issue with CSRF token.
User Controller
function userController($scope,$http,$route,$location,Auth,User) {
$scope.login = function(){
Auth.login($scope.user).then(function(user) {
User.setCurrentUser(user);
$location.path("/logout");
}, function(error) {
// Authentication failed...
});
}
$scope.logout = function(){
Auth.logout().then(function() {
$location.path("/logout");
}, function(error) {
// An error occurred logging out.
});
}
}
Angular Routes.js
myApp.config(function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when("/login",
{ templateUrl: "/assets/user/login.html",
controller: "userController" })
.when("/logout",
{ templateUrl: "/assets/user/logout.html",
controller: "userController" })
.otherwise({ redirectTo: "/login" });
});
Templates/login.html
<div>
<label for="user_email">Email</label><br>
<input autofocus="autofocus" ng-model="user.email" id="user_email" name="user[email]" type="email" value="">
</div>
<div>
<label for="user_password">Password</label><br>
<input autocomplete="off" ng-model="user.password" id="user_password" name="user[password]" type="password">
</div>
<div>
<input name="commit" type="submit" ng-click="login()" value="Sign in">
</div>
Templates/logout.html
LOGOUT
When I click on Sign-in button which call login() function with user datas and login was successful , after that logout.html page is rendered using angular routes ($location.path) , if I click on logout button logout.html , it throws following error.
ActionController::InvalidAuthenticityToken
Since the authtoken was already used by login action and its no more valid. I am not sure how to generate new one with IN angularJS , If I refresh the page the logout is working.

I replaced
gem 'ng-rails-csrf' to gem 'angular_rails_csrf'
It started working , but I am not sure why Angular devise suggested "ng-rails-csrf" , which not refreshing the token. Simply adding 'angular_rails_csrf' did the trick.

Related

Recaptcha image challenge always occurs in Microsoft Edge after form submission (Invisible recaptcha)

I've just implemented invisible recaptcha into a web form. Everything works fine with Chrome. But with Microsoft Edge, the image challenge always occurs with every form submission. Which is embarrassing for the users of the website. An idea?
Thanks a lot for your insights and advice :o)
Laurent
Javascript code:
window.onScriptLoad = function () {
var htmlEl = document.querySelector('.g-recaptcha');
var captchaOptions = {
'sitekey': 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
'size': 'invisible',
'badge': 'inline',
callback: window.onUserVerified
};
var inheritFromDataAttr = true;
recaptchaId = window.grecaptcha.render(htmlEl, captchaOptions, inheritFromDataAttr);
};
window.onUserVerified = function (token) {
$.ajax({
url: 'process.php',
type: 'post',
dataType: 'json',
data : {
'lastname' : $("#lastnameField").val(),
'firstname' : $("#firstnameField").val(),
'city' : $("#cityField").val(),
'postalCode' : $("#postalcodeField").val(),
'g-recaptcha-response' : token
},
success:function(data) {
// informs user that form has been submitted
// and processed
},
error: function(xhr, textStatus, error){
// informs user that there was a problem
// processing form on server side
}
});
};
function onSubmitBtnClick () {
window.grecaptcha.execute;
}
HTML code:
<html>
<head>
<script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=onScriptLoad" async defer></script>
<script type="text/javascript" src="js/petition.js"></script>
...
</head>
<body>
<form id="petitionForm" onsubmit="return false;">
<input id="lastnameField" type="text" name="lastname" placeholder="Lastname" required value="Doe">
<input id="firstnameField" type="text" name="firstname" placeholder="Firstname" required value="John">
<input id="postalcodeField" type="text" name="postalCode" placeholder="Postal Code" required value="ABCDEF">
<input id="cityField" type="text" name="city" placeholder="City" value="Oslo">
....
<input type="submit" name="login" class="g-2" data-sitekey="xxxxxxxxxxxxxxxxxxxxxxx" id="signButton" data-callback='' value="Signer" onclick="onSubmitBtnClick();">
<div class="g-recaptcha" id="recaptchaElement" style="align-content: center"></div>
</form>
...
</body>
</html>

Session and cookie value using Cross Domain in .net

I have two projects - an MVC project and the other an API project. I have placed the login form and script in the MVC project and the back end is in the API project.
I have written the login form as below:
<form>
<div class="form-group">
<input type="email" class="form-control" id="email" placeholder="Username">
</div>
<div class="form-group">
<input type="password" class="form-control" id="pwd" placeholder="Password">
</div>
<button id="buttonSubmit" class="btn btn-default">LOG IN</button>
</form>
The script for login submit when a customer has filled in the above form:
var user =
{
UserName: $("#email").val(),
Password: $("#pwd").val(),
IsRemember: $(".customCheckBox").val()
}
$.ajax({
type: "POST",
url: "http://localhost:55016/api/ajaxapi/loginmethod",
data: user,
success: function (response) {
document.cookie = "UserName = " + response.UserName;
}
});
Then I have created session using API project as below:
[HttpPost]
[Route("api/ajaxapi/loginmethod")]
public UserValuesForLogOn AjaxLogOnMethod(UserValuesForLogOn user)
{
HttpContext.Current.Session["authToken"] = user;
return user;
}
After logged in I have called ajax post to get details as below, which is in the MVC project:
$.ajax({
type: "POST",
url: "http://localhost:55016/api/ajaxapi/caselistmethod",
success: function (response) {
}
});
Then I have written code in the API project to take session value as stored while login process:
[HttpPost]
[Route("api/ajaxapi/caselistmethod")]
public List<UserValuesForLogOn> AjaxCaseListMethod()
{
userDetails = (UserValuesForLogOn)HttpContext.Current.Session["authToken"];
return userDetails;
}
Both cookie and session values can't take in API project. Please help me. Is it possible to access session and cookie in a cross domain situation.
Thanks.

React Axios to Rails Knock

I am trying to send a POST request from axios to a Rails API using the following function in the React frontend:
export function registerUser({ name, email, password }) {
var postdata = JSON.stringify({
auth: {
name, email, password
}
});
return function(dispatch) {
axios.post(`${API_URL}/user_token`, postdata )
.then(response => {
cookie.save('token', response.data.token, { path: '/' });
dispatch({ type: AUTH_USER });
window.location.href = CLIENT_ROOT_URL + '/dashboard';
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR)
});
}
}
The Knock gem expects the request in the following format:
{"auth": {"email": "foo#bar.com", "password": "secret"}}
My current function seem to generate the correct format (inspecting the request in the browser devtools), but I'm getting the following error:
Uncaught (in promise) Error: Objects are not valid as a React child
(found: object with keys {data, status, statusText, headers, config,
request}). If you meant to render a collection of children, use an
array instead or wrap the object using createFragment(object) from the
React add-ons. Check the render method of Register.
class Register extends Component {
handleFormSubmit(formProps) {
this.props.registerUser(formProps);
}
renderAlert() {
if(this.props.errorMessage) {
return (
<div>
<span><strong>Error!</strong> {this.props.errorMessage}</span>
</div>
);
}
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
{this.renderAlert()}
<div className="row">
<div className="col-md-6">
<label>Name</label>
<Field name="name" className="form-control" component={renderField} type="text" />
</div>
</div>
<div className="row">
<div className="col-md-12">
<label>Email</label>
<Field name="email" className="form-control" component={renderField} type="text" />
</div>
</div>
<div className="row">
<div className="col-md-12">
<label>Password</label>
<Field name="password" className="form-control" component={renderField} type="password" />
</div>
</div>
<button type="submit" className="btn btn-primary">Register</button>
</form>
);
}
}
The error is caused by the following line in your code
errorHandler(dispatch, error.response, AUTH_ERROR)
The exception raised clearly explains that. Instead of setting error.response, try to use the actual data from error.response. E.g error.response.data. Also, you can try replacing the error.response with a string and see how that behaves, then reference the string you need from error.response.data.

How to clear a new record from ember.js frontend after it fails Rails validation (422 error)?

I'm working on a basic reddit clone app with Rails and ember.js (via the ember-rails gem). Basically I have a 'post' model/controller in Rails which works correctly, but when I add a new post from the ember post model's create action, even if it fails the Rails validation, if I then go to the 'posts' index page which lists all the posts, I can see it there (i.e. ember is keeping the data). When I refresh it goes away, but I'm wondering what is the best way to purge that data so that it gets deleted upon rejection from the backend? Another odd thing is that simply going to the posts/new page at all creates a new blank post which is then visible on the Then on the client-side, I have the following files in app/assets/javascripts/routes:
posts_route.js:
RedditJp.PostsRoute = Ember.Route.extend({
model: function() {
return this.get('store').find('post');
}
});
posts_new_route.js:
RedditJp.PostsNewRoute = Ember.Route.extend({
model: function(){
return this.get('store').createRecord('post'); },
actions: {
create: function() {
var newPost = this.get('currentModel');
var self = this;
newPost.save().then(
function() { self.transitionTo('posts'); },
function() { }
);
}
}
});
Here's the form I'm trying to use to submit the data in posts/new.hbs:
<h1> Add a post </h1>
{{#each error in errors.title}}
<p>{{error.message}}</p>
{{/each}}
<form {{action "create" on="submit"}}>
<div>
<label>
Title<br/>
{{input type="text" value=title}}
</label>
</div>
<div>
<label>
Address<br/>
{{input type="text" value=address}}
</label>
</div>
<div>
<label>
Vote count<br/>
{{input type="text" value=voteCount}}
</label>
</div>
<button>Save</button>
</form>
and then in assets/javascripts/templates/posts/ I have index.hbs:
<h1>Posts</h1>
<ul>
{{#each}}
<li>{{title}} at {{address}} vote count: {{voteCount}}</li>
{{else}}
<li>There are no posts.</li>
{{/each}}
</ul>
and here's my router.js:
RedditJp.Router.map(function() {
this.resource('posts', function() {
this.route('new')
});
this.resource('home', function() {
});
});
RedditJp.IndexRoute = Ember.Route.extend({
redirect: function(){
this.transitionTo('home')
}
});
I was thinking I could just add a check in the posts/index.hbs file and only show records that aren't dirty, but there must be a cleaner way of doing it, so I'm wondering what would be considered best practice in this case (I'm thinking there should be some code I could add to the promise in posts_new_route.js to deal with this, but I can't quite figure it out).
Thanks a lot! And let me know if you need any additional info.
You can check if model isNew in template to hide new Record ( also you can use isEmpty property )
var record = store.createRecord('model');
record.get('isNew'); // true
record.save().then(function(model) {
model.get('isNew'); // false
});
In template will look like {{each model}}
{{#if model.get('isNew')}}
record.save().then(function(){
// Success callback
}, function() {
model..deleteRecord();
});

How to config routeProvider with MVC4 and WebAPI2

I have a small app that shows a calendar with some events.
When I click on one of the events I would like to show details of the selection below the calendar.
The events' urls are generated within a loop like this :
'url': '#/concerts/' + data[idx].id
and my config is the following:
var app = angular.module('EventsApp', ['ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/concerts/:eventId', {
templateUrl: '/Views/Home/ng-view/details.html',
controller: 'EventCtrl'
}).
otherwise({
redirectTo: '/'
});
}]);
My Index.cshtml:
<div id="bodyApp" ng-app="EventsApp">
<div class="mainBody" ng-controller="EventCtrl as event" ng-init="event.getAllConcerts()">
<div class="search">
<input type="search" />
</div>
<div class="calendar">
<div id="calendar">
</div>
</div>
<div class="crudSection" ng-view>
</div>
</div>
At the moment I am just trying to get the details.html to be shown, but It doesn't redirect. The url is updated with the proper one but my view is not refreshed.
Also, How should the routeProvider config to do a webapi call without exposing the '/api/{controller}/{id}' info?
Edit.: I tried with the standard MVC route '/Home/{action} and a partialview and it works, but not a '/Home/{action}/{id}', so I think I'm missing something about how to combine both routings.
I found the solution in the following link:
AngularJS - How to use $routeParams in generating the templateUrl?
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/concerts/:eventId', {
templateUrl: function (params) { return '/Home/Details/' + params.eventId; },
controller: 'EventCtrl'
}).
otherwise({
redirectTo: '/'
});
}]);

Resources