Omniauth Steam with Rails 7 - ruby-on-rails

I am creating a new application with rails 7. I would like to add a way for the user to signup via steam. I used the code which works on rails 6, but on rails 7 I receive an error.
Access to fetch at 'https://steamcommunity.com/openid/login?openid.ax.theKeyIamHidingforStackOverflow'
(redirected from 'http://localhost:3000/auth/Steam')
from origin 'http://localhost:3000' has been blocked by
CORS policy: Response to preflight request doesn't pass access control
check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the
request's mode to 'no-cors' to fetch the resource with CORS disabled.
Clicking on the https://steamcommunity.com/openid/login?fooBar I get to stream and also redirected to my app and I am signed in.
I tried to set cors in config/initializers/cors.rb like:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'https://steamcommunity.com'
resource '*', headers: :any, methods: [:get, :post]
end
end
But this does not work. Do I need to allow the visit of third party websites before I try to redirect to them?
Did something change on rails 7 to protect redirect?
This is the post to the server
= form_tag '/auth/Steam', method: :post do
= submit_tag 'Steam'
Best Regards
Dennis

The answer to this, we need to disable turbo on making an Ajax request by using the form like this:
= form_tag '/auth/steam', method: :post, data: { turbo: false } do
= submit_tag 'Steam'
This Form contains data: { turbo: false } which disables turbo

Related

Rails DELETE route not working properly / not calling the controller function

I am using React and Rails API. To make a request from React I am using Axios library and the code looks like this:
const _deleteIssue = () => {
axios.delete(`${ROOT_API}/v1/issue/delete/${props.issue.id}`, {
headers: {
'Authorization': `Bearer ${authToken}`
}
}).then(res => {
props.updateProjectData()
}).catch(err => {
console.log(err)
})
}
In order to handle this request I have set up a route for it which looks like this:
Rails.application.routes.draw do
concern :base_api do
# other routes
post 'issues/create', to: 'issues#create'
delete 'issue/delete/:issue_id', to: 'issues#delete_issue'
end
namespace :v1 do
concerns :base_api
end
end
As you can see from the code snippet above, the route is set to call delete_issue function inside of the issues controller. I have created that function and it looks like this:
class V1::IssuesController < ApplicationController
# other functions
def delete_issue
Issue.find_by(id: params[:issue_id]).delete
render json: {}, status: 200
end
end
I am justing trying to find the issue with an id that is passed as params from Axios delete request.
It is supposed to delete it and return nothing with a status code of 200. What happens instead is that in my "Network" tab inside of developer tools in my browser(Firefox) shows the 200 OK request with the OPTIONS method. There is no DELETE method being sent or anything.
I even tried to comment out the delete_issue function from IssuesController and there was not 404 routing error. The result was the same. I can't find out what is wrong with it even though it is probably a pretty obvious error which I can't see.
Note that I am using Rails 6.
It seems you did not configure rack-cors. Simply add this to your cors.rb file:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:3001' # or you react app domain
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end

ng-token-auth passwordResetSuccessUrl not redirecting

I am building a rails app with AngularJs on the front end. When resetting a users' password, the email sends as expected, however the reset link does not redirect to the configured passwordResetSuccessUrl and simply goes to:
http://localhost:8000/auth/password/edit?reset_password_token=KNsWjzizNWNsMqzSss8F
As you can see, the redirect_url is not there, should I see it in the url? Also, when clicking through the link, the app does not fire the auth:password-reset-confirm-success event as noted in ng-token-auth documentation.
here's the request:
{"email":"myemail#gmail.com","redirect_url":"http://localhost:8000/#!/updatePassword"}
the response is a 500 error (i find this odd, the email is sent and no error in the server log) with the message:
{"message":"password reset email sent"}
Here's some relevant configuration code:
function authConfig($authProvider,envServiceProvider) {
let url = envServiceProvider.is('development') ? 'http://localhost:8000' : 'https://miles-board.herokuapp.com';
$authProvider.configure([{
'default': {
apiUrl: url,
emailRegistrationPath: '/users',
confirmationSuccessUrl: window.location.href,
validateOnPageLoad: false,
passwordResetSuccessUrl: url+'/#!/updatePassword'
}},
{'user': {
apiUrl: url,
emailRegistrationPath: '/users',
confirmationSuccessUrl: window.location.href,
validateOnPageLoad: false,
passwordResetSuccessUrl: url+'/#!/updatePassword'
}},
{'team_owner' : {
apiUrl: url,
emailRegistrationPath: '/team_owners',
confirmationSuccessUrl: window.location.href,
validateOnPageLoad: false,
passwordResetSuccessUrl: url+'/#!/updatePassword'
}
}]
);
console.log('is dev: ', envServiceProvider.is('development'))
}
I had issues with the email confirmation, as well, so I assume I've missed something in my set up, but I'm not sure what.
Being new to rails, I have to ask a very newbie question: I have the devise_token_auth gem installed, do I need to install devise as well, or does the devise_token_auth include that?
In my devise_token_auth.rb intializer, I have included this as well:
config.default_password_reset_url = 'http://localhost:8000/#!/updatePassword'
Please let me know if there's any other information I can provide.
Okay, I got this working, here's what I had to do go get it working with an AngularJS front end and Rails back:
Override the PasswordsController create, update, edit, and after_resetting_password_path_for
For the create and update functions, the primary issue was that I needed it to render a json response, so where it says something like respond_with resource I changed to render json: resource, status: <status>, && return (you can change resource and status to what you need for your application, same with the render method)
For edit, instead of using after_sending_reset_password_instructions_path_for, I grabbed the redirect URL from the email and simply do a redirect_to params[:redirect_url]
and I changed after_resetting_password_path_for to redirect where I want the user to be logged in to.
I also had to change the reset_password_instructions.html.erb template. the line which contains edit_password_url to this:
<p><%= link_to t('.password_change_link'), edit_password_url(#resource, reset_password_token: #token, config: 'default', redirect_url: message['redirect-url'].to_s+'?reset_token='+#token).html_safe %></p>
Then in routes.rb, I had to let devise know to use my controller:
mount_devise_token_auth_for 'User', at: 'auth', controllers: { passwords: 'passwords' }
I'm not thrilled about the redirect URL portion in edit, it has a code smell to it that I don't exactly like, but it works. If some one has advice on that front, I would appreciate it!

Angular $http and Rails 4 params

I'm using the rails-api gem to have just a Rails API and using Angular to power my frontend. Whenever I use $http, it will only work if I pass in params instead of data. Here's an example with trying to log in a user and create a new session:
'use strict';
app.controller('LoginCtrl', function($scope, $location, $http, tokenHandler) {
$scope.login = function() {
$http({
url: 'http://localhost:3000/api/admins/sign_in',
method: 'POST',
params: $scope.admin
}).success(function(data) {
if (data.success) {
$scope.ngModel = data.data.data;
tokenHandler.set(data.data.auth_token);
$location.path('/admin/blog');
} else {
$scope.ngModel = data;
$scope.user.errors = data.info;
}
}).error(function(msg) {
$scope.admin.errors = 'Something is wrong. Please try again.';
});
};
});
If instead of params I used data: { admin: $scope.admin }, Rails complains to me that params[:admin] is nil. It seems to not be coming through at all.
However, if I use params, I get this:
Started POST "/api/admins/sign_in?email=raderj89#gmail.com&password=[FILTERED]" for 127.0.0.1 at 2014-09-07 20:08:04 -0400
Processing by Admin::SessionsController#create as HTML
Parameters: {"email"=>"raderj89#gmail.com", "password"=>"[FILTERED]"}
Which I can work with. It's just weird that it seems to only work when the request is processed as HTML. When I use data, I get this:
Started OPTIONS "/api/admins/sign_in" for 127.0.0.1 at 2014-09-07 20:36:24 -0400
Processing by Admin::SessionsController#create as */*
Is it suppose to say processing by */*? I'd think it should understand it's supposed to process by json specifically.
My sessions controller looks like this:
class Admin::SessionsController < Devise::SessionsController
skip_before_filter :verify_authenticity_token
before_filter :authenticate_user!, except: [:create]
respond_to :json
# ...
end
The weird thing is I definitely got it working the first time just using data: { admin: $scope.admin }, but ever since, the params seem to never come through unless I use params: $scope.admin.
ALSO:
I'm using Devise for authentication, and I had to add this to my ApplicationController:
class ApplicationController < ActionController::API
include ActionController::MimeResponds
before_filter :set_cors_headers
before_filter :cors_preflight
private
def set_cors_headers
headers['Access-Control-Allow-Origin'] = AppConfig.client['origin']
headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
headers['Access-Control-Allow-Headers'] = '*'
headers['Access-Control-Max-Age'] = "3628800"
end
def cors_preflight
head(:ok) if request.method == :options
end
end
Anyone ever dealt with this before?
I've finally got it working and while I'm still confused, I think I've got somewhere close to what the problem was: My CORS configuration in my Rails API.
From what I've learned, Angular sends data in JSON format by default. This goes through as "Content-Type:application/json;charset=UTF-8", whereas in jQuery AJAX requests, it goes through as "Content-Type:application/x-www-form-urlencoded; charset=UTF-8", and is converted to a query string using $.param(). I'll admit, I've probably heard this before, but haven't truly registered this fact and its effects until now.
In my application controller, I configured my CORS settings like so:
def set_cors_headers
headers['Access-Control-Allow-Origin'] = AppConfig.client['origin']
headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
headers['Access-Control-Allow-Headers'] = '*'
headers['Access-Control-Max-Age'] = "3628800"
end
def cors_preflight
head(:ok) if request.method == :options
end
AppConfig is just an OpenStruct that tells my Rails API what origin to accept requests from. And then everything else was supposed to simply set the CORS headers.
For some reason of which I'm still not sure, this wasn't working for JSON requests. I got the above code from a tutorial using Angular and Rails, and in the case of the tutorial, they manually stripped out the asset pipeline, leaving everything else about Rails in, whereas rails-api strips out some Rails configuration. This may be why setting the CORS headers in ApplicationController wasn't working.
What did work was to use the rack-cors gem and then add this bit to development.rb:
config.middleware.use Rack::Cors do
allow do
origins 'localhost:9000'
resource '*', :headers => :any, :methods => [:get, :post, :options, :delete]
end
end
This tells my app to accept requests from localhost:9000, and to accept any headers. I thought I was accomplishing that with headers['Access-Control-Allow-Headers'] = '*' in my ApplicationController, but I guess not. Once I specified Rails to use those middleware settings, everything worked perfectly. My Rails API can now accept application/json from my Angular app.
If someone could fill in the gaps where I'm still confused, I'd appreciate it. But I hope this helps others.
You can send either :params or :data (or both, I guess). According to the angularjs docs at https://docs.angularjs.org/api/ng/service/$http
params – {Object.} – Map of strings or objects which
will be turned to ?key1=value1&key2=value2 after the url. If the value
is not a string, it will be JSONified.
data – {string|Object} – Data to be sent as the request message data.
The controller is expecting http-type parameters/form data, so passing the object via params works - it gets converted, whilst passing the same via :data doesn't because it doesn't get converted.
I don't know if there is a smart way to unpack the data format at the Rails Controller end, but you can convert the object within your $http request into serialized parameters using $.param(data)http://api.jquery.com/jQuery.param/
data: $.param($scope.your_data_object) e.g. $scope.admin
and then unpack params[:data] at the controller.

Verify if a request is GET / POST

I am using the twitter gem for ruby and need to send a POST request to users/lookup endpoint.
As per the gem source code documentation(https://github.com/sferik/twitter/blob/4e8c6dce258073c4ba64f7abdcf604570043af71/lib/twitter/rest/users.rb), the request should be POST by default, unless I pass :get :
#option options [Symbol, String] :method Requests users via a GET request instead of the standard POST request if set to ':get'.
def users(*args)
arguments = Twitter::Arguments.new(args)
request_method = arguments.options.delete(:method) || :post
flat_pmap(arguments.each_slice(MAX_USERS_PER_REQUEST)) do |users|
perform_with_objects(request_method, '/1.1/users/lookup.json', merge_users(arguments.options, users), Twitter::User)
end
end
I am calling it as follows:
users = #client.users(twitter_screen_names_arr, [:method, :post])
However, I am not sure if this is actually resulting in a POST request / a GET request.
How can I make sure if this is a POST/GET? I would like to print the request that is being made to get a clarity on what actually gets sent.
Thanks!
As you can see from the code it uses POST by default. This behavior is also specified with RSpec.
You can invoke the users method like this:
#client.users(twitter_screen_names_arr, :method => :post)
or simply
#client.users(twitter_screen_names_arr)
since POST is the default request method.
If you don’t trust the code or the specs, you could run the request through a proxy to verify this behavior manually.

Handling mix of HTTP and HTTPS links on a page

My setup: Rails 3.0.9, Ruby 1.9.2
My app requires that only a certain part of my site be SSL protected and the rest not. In case anyone thinks this isn't normal behavior, check out Amazon. When merely browsing for products, it's in HTTP mode, during checkout, it switches to HTTPS. Even in the middle of a secure checkout transaction, there are several other links on the same page that are HTTP only.
I looked at ssl_requirement gem and decided not to use it because it isn't a complete solution for my needs. I ended up setting up specific SSL routes like
resources :projects do
resources :tasks, :constraints => { :protocol => "https" }
end
In my view code, for HTTP specific links
<%= link_to 'Projects', project_url(#project, :protocol => "http") %>
and to handle HTTPS specific link
<%= link_to 'Task', new_project_task_url(#project, :protocol => "https") %>
I understand this isn't the cleanest way but it's what I have decided to do. The problem with this setup is how to properly set both HTTP and HTTPS links on every page. There is a proposed solution here but it requires wholesale changes _path to _url and I prefer to avoid that if at all possible. The solutions involves adding this method in
application_helper.rb
module ApplicationHelper
def url_for(options = nil)
if Hash === options
options[:protocol] ||= 'http'
end
super(options)
end
end
So my question is it possible to change this method or another one to change _path calls to explicit urls so I can use the above method to set the proper protocol.
you could try this, although I'm not 100% sure it works:
Use the proposed changes from the stackoverflow answer
Add this to application_controll.rb:
class ApplicationController < ActionController::Base
def url_options
{ :host => request.host }.merge(super)
end
end
According to the Docs it should add the full url even if you use _path:
:only_path - If true, returns the relative URL (omitting the protocol,
host name, and port) (true by default unless :host is specified).
My app requires that only a certain part of my site be SSL protected and the rest not.
That's your faulty assumption. The secure premise is that if some of your app requires SSL, then all of your app requires SSL. The correct assumption then is that your entire app requires SSL.
If your app requires SSL, then you should use something simple like rack-ssl which sets the HSTS header and enforces the secure flag on cookies in all responses.

Resources