AngularJs - CORS - WithCredentials Dilemma - ruby-on-rails

I have an API which servers two apps, one mobile and the other one web.
I'm using token based authentication to make it secure and in my web app works perfectly. However in my mobile app is not. And the thing is that in my webpage I have the same domain as my api, therefore no CORS issue.
In my mobile app I need to send my credentials to validate the user's token because as I don't have the same domain is not sending automatically.
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}]);
But te dilemma is, if I send the credendials usisng the code above I get:
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'
Because I have this set: headers['Access-Control-Allow-Origin'] = '*', where is supposed to be a specific domain but being a mobile app I don't have any.
If I remove all CORS validations I get other errors...
I don't know what to do... how to face this issue.
My Api was made using Ruby, and to validate the authentication I'm using this code:
def authenticate_current_user
head :unauthorized if get_current_user.nil?
end
def get_current_user
return nil unless cookies[:auth_headers]
auth_headers = JSON.parse(cookies[:auth_headers])
expiration_datetime = DateTime.strptime(auth_headers["expiry"], "%s")
current_user = User.find_by(uid: auth_headers["uid"])
if current_user &&
current_user.tokens.has_key?(auth_headers["client"]) &&
expiration_datetime > DateTime.now
#current_user = current_user
end
#current_user
end
If I set WithCredentials to false I get 401 unauthorized coz cookie[auth_headers] is empty.

Related

Rails API 422 Unprocessable Entity: No verification key available, heroku

I created a Rails API with a JWT authentication system and deployed it to Heroku. When I request the endpoints locally, all seems to be working fine but when I make requests to the live endpoints (i.e the Heroku deployed app) I get a: 422 Unprocessable Entity server error and the response body looks like this:
{
"message": "No verification key available"
}
The class responsible for encoding and decoding the auth token is defined as follows:
class JsonWebToken
# secret to encode and decode token
HMAC_SECRET = Rails.application.secrets.secret_key_base
def self.encode(payload, exp = 24.hours.from_now)
# set expiry to 24 hours from the creation time.
payload[:exp] = exp.to_i
# sign token with application secret
JWT.encode(payload, HMAC_SECRET)
end
def self.decode(token)
# get payload, first index in decoded Array
body = JWT.decode(token, HMAC_SECRET)[0]
HashWithIndifferentAccess.new body
# rescue from all decode errors
rescue JWT::DecodeError => e
# raise custom error to be handled by custom handler
raise ExceptionHandler::InvalidToken, e.message
end
end
I have an endpoint /signup where I can make a POST request to register a new user and POST /todos which is accessible and available only to registered users. Making a registration request works perfectly fine, but when I try to make the POST request to the /todos endpoint it raises an error.
The association between user and suit is 1:m respectively.
Please if you have any idea on how I can fix this, I'll be very grateful, thanks : ).
I finally figured a way out by altering the Rails.application.secrets.secret_key_base to Rails.application.secret_key_base. For a more detailed review on this please check out this link. Hopefully, this will help someone facing a similar issue.
This was also my problem. After checking out my json_web_token.rb file, I figured out that I had written the following line:
HMAC_SECRET = Rails.application.secrets.secret_key_base
There is an extra secrets reference, which is causing the problem. It should be:
HMAC_SECRET = Rails.application.secret_key_base
But as far as I'm concerned, you managed to figure it out yourself!

RoR / RSpec request cookie change not persisting when changed in test case

I'm trying to test that refreshing a user session works (using JWT web tokens). The server passes back an access cookie to be used for web and after hitting the refresh endpoint I am trying to set the request cookie to be the response cookie, and then making another request to validate that the new access cookie is valid.
The issue that I am facing is that despite setting the request cookie to the response cookie before making the next request it still uses the old request cookie (which is expired).
This is the test case:
it 'maintains a users session' do
JWTSessions.access_exp_time = 0
external_sign_in(identifier: user.mobile_number, password: 'password', client: :web)
JWTSessions.access_exp_time = 3600
puts request.cookies[JWTSessions.access_cookie]
post :refresh
request.cookies[JWTSessions.access_cookie] = response.cookies['jwt_access']
request.headers[JWTSessions.csrf_header] = response_json['csrf']
puts '======='
puts response.cookies['jwt_access']
puts request.cookies[JWTSessions.access_cookie]
puts '======='
get :index
puts request.cookies[JWTSessions.access_cookie]
expect(response_json['message']).to include(user.first_name)
end
Context: externa_sign_in is a helper method that sets the correct csrf header and request cookie when the client is :web.
Output from the put statements:
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjY3NTk5MzgsInVzZXJfaWQiOjIxNjYsInVpZCI6Ijc3NmIwYTIyLWI3ZWUtNDhmYy1hYWIzLWM5MTMyZGQ2ZDAyZiIsImV4cCI6MTU2Njc1OTkzOCwicnVpZCI6IjkzM2RmMTZjLWYwZjYtNDlmYy1hYWZhLTk5MmE2NjhmMTk3YyJ9.SWPGjSKzJVbCr7cBTFiAZieLOfgLnNWTjKfY2w3LTZc
=======
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjY3NjM1MzgsImV4cCI6MTU2Njc2MzUzOCwidXNlcl9pZCI6MjE2NiwidWlkIjoiNzAwNTdhODQtN2MzNC00M2M2LWE4MzYtZjI0ODIxNTdlM2Y2IiwicnVpZCI6IjkzM2RmMTZjLWYwZjYtNDlmYy1hYWZhLTk5MmE2NjhmMTk3YyJ9.0lkiJ9Iu3R3NHSg0RsGzoSh2rVhwGnp5X0ZYS2jvncQ
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjY3NjM1MzgsImV4cCI6MTU2Njc2MzUzOCwidXNlcl9pZCI6MjE2NiwidWlkIjoiNzAwNTdhODQtN2MzNC00M2M2LWE4MzYtZjI0ODIxNTdlM2Y2IiwicnVpZCI6IjkzM2RmMTZjLWYwZjYtNDlmYy1hYWZhLTk5MmE2NjhmMTk3YyJ9.0lkiJ9Iu3R3NHSg0RsGzoSh2rVhwGnp5X0ZYS2jvncQ
=======
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjY3NTk5MzgsInVzZXJfaWQiOjIxNjYsInVpZCI6Ijc3NmIwYTIyLWI3ZWUtNDhmYy1hYWIzLWM5MTMyZGQ2ZDAyZiIsImV4cCI6MTU2Njc1OTkzOCwicnVpZCI6IjkzM2RmMTZjLWYwZjYtNDlmYy1hYWZhLTk5MmE2NjhmMTk3YyJ9.SWPGjSKzJVbCr7cBTFiAZieLOfgLnNWTjKfY2w3LTZc
Also it seems like the csrf header updates, so not sure what's going on.
In order to do this you need to do cookies.delete(...) before setting the cookie again. Not sure why, but it seems to fix it.

Error in Access-Control-Allow-Origin when Angular request to Rails

I'm using Angular 2 to make an API(rails) request. When I make the http request through angular, I get the following error:
XMLHttpRequest cannot load https://api-url. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 404.
However, if I try to make a request through Postman or the browser itself, I don't get any error. The data is displayed normally
angular code:
makeRequest() {
let user = {"user": "user", "password": "password"};
let headers: Headers = new Headers();
headers.append('Authorization', 'Basic ' + btoa(user.user + ':'+user.password));
headers.append('Content-Type', 'application/vn.api+json')
let this.http.get(api-url, {headers: headers}).map(res => res.json()).subscribe(data => {
this.data = data;
})
}
In my rails server i using the gem 'jsonapi-resources'to open AP. In my api controller, i have this code to authenticate and set headers to requests:
module Api
class ApiController < JSONAPI::ResourceController
prepend_before_action :set_headers, :authenticate
def context
{ current_station: #user }
end
private
def authenticate
authenticate_or_request_with_http_basic do |token, _|
#user = User.where(api_key: token).first
end
end
def set_headers
response.headers["Access-Control-Allow-Origin"] = "*"
end
end
end
When i make request in browser or using postaman, the header appears normally, but in Angular i have the error.
This is because of CORS. Long story short, browsers forbid by default one domain (http://localhost:4200) to make AJAX requests to another one (http://api-url). It works in "postman" because this is an extension and then, the CORS does not apply. If you want to fix this issue, you have to configure your server to returns certain header saying to the client that it allows the CORS connection.
In fact, when a website is trying to make an AJAX request to another domain, it first send an OPTION request to ask what are the domain allowed. This list is returned by the server via the header Access-Control-Allow-Origin. For example, it could contain a star ("*") to indicate that anyone could make AJAX call to this server. If this header allows your client to make AJAX call, your actual request will be executed, otherwise, you'll get an error (probably the one you currently get)

Rails - Slack API OAuth Access - invalid_client_id

I'm building Slack integration for my Ruby on Rails application and I'm trying to get an access_token from the Slack API for my Slack App when a user clicks the Add to Slack button.
From Postman, I can successfully post the following:
https://slack.com/api/oauth.access?client_id=idgoes.here&client_secret=secretgoeshere&code=12345&pretty=1
However, within Rails I always get a response with invalid_client_id, regardless of the way I call the API. I have checked my ID is correct (a lot) and tried regenerating it, but I don't think that is the issue due to the postman success.
Within my get_oauth_access_token method I have tried the following implementations:
1.
rc = JSON.parse(HTTP.post('https://slack.com/api/oauth.access',
params: {
client_id: 'idgoes.here',
client_secret: 'secretgoeshere',
code: '12345'
}))
2.
response = Excon.post('https://slack.com/api/oauth.access',
headers: { 'Content-Type' => 'application/json; charset=utf-8' },
user: client_id, password: client_secret,
body: oauth_request_body.to_json)
Any implementation I try always ends up getting a invalid_client_id response.
I'm aware it may be something to do with environment config, but I'm not sure what would be helpful to debug, so please let me know what other information I can share. I'm running on localhost.
Update:
I just found out that many (maybe all) of the Slack APIs do not accept a JSON format body (which seems crazy seeing as they send a response in JSON.
Make sure to use x-www-form-urlencoded format body on your request or it will not work properly.
"Content-Type" => "application/x-www-form-urlencoded"
I use oauth2 gem to authorize. So I was able to get this to work by reading the slack documentation and using oauth2 in my controller:
class OauthController < ApplicationController
def authorize
options = {
site: 'https://slack.com/oauth/authorize'
}
client ||= OAuth2::Client.new(
'client-id',
'client-secret',
options
)
params = {
scope: 'incoming-webhook, commands',
redirect_uri: 'https://localhost:3000/oauth/callback'
}
redirect_to client.auth_code.authorize_url(params)
end
def authorize_callback
puts params["code"]
redirect_to root_url
end
end
Routes file:
get '/authorize', to: 'oauth#authorize'
get '/oauth/callback', to: 'oauth#authorize_callback'
Don't forget to set your callback url at Oauth settings on api.slack.com, I used localhost for testing purposes as you can see.

Rails: OAuth2 gem returns 400 error when attempting to connect to facebook

I'm attempting to add Facebook connect to our web app, and I'm running into a problem with. Everything works fine locally (I can authenticate through Facebook), but when I push the code to our dev server (which lives in the wild), every time I try to authenticate it returns the following error code:
OAuth2::HTTPError: Received HTTP 400 during request
That's really the only explanation I'm getting. Again, this works on my local machine, and the gems and such match between boxes, so I'm a bit confused. Here's the code I'm executing.
def facebook_connect
#Set the scope we want to pull from Facebook, along with the callback URL
options = {
:redirect_uri => facebook_callback_url,
:scope => "email,publish_stream"
}
#Go out and fetch the url
client = OAuth2::Client.new(FACEBOOK_API_KEY, FACEBOOK_SECRET, {:site => FACEBOOK_API_URL, :access_token_method => :post})
#Redirect to the callback for processing
redirect_to client.web_server.authorize_url(options)
end
def facebook_callback
#Client URL
client = OAuth2::Client.new(FACEBOOK_API_KEY, FACEBOOK_SECRET, {:site => FACEBOOK_API_URL, :access_token_method => :post})
#Parse out the access token
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => facebook_callback_url)
#Get the user
fb_user = JSON.parse(access_token.get('/me'))
#Do some authentication database stuff
end
def facebook_callback_url
uri = URI.parse(request.url)
uri.path = '/users/facebook_callback'
uri.query = nil
uri.to_s
end
I searched Google, but the solutions that show up aren't working. Also, if anyone knows how to parse and display OAuth2 errors, I would appreciate that, as well. Thanks
Assuming that Facebook OATH knows of your server's IP address(they are very strict about it), I would recommend that you use use 'rescue' to catch that exception, get the backtrace and then find where it is being raised and place a bunch of debug statements to check the state of both request and the response, as well as access tokens.
Or you can configure remote debugging with Rubymine or NetBeans which is not an easy task :)
The issue actually ended up being a problem with the "Faraday" gem. Our dev server wasn't set up to handle SSL, which was returning an error code. We patched it using the following answer:
OmniAuth & Facebook: certificate verify failed

Resources