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.
Related
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!
I am using rails 5.2.4 and ruby 2.5.8
api authentication with gem knock for jwt token with devise model
using gem rack-attack for this feature
I want to implement two cases followed,
I want to throttle request if user is authenticated If user is
authenticated with given access token in header to get resource
object in api then he can allow 60 authenticated request per hour.
Rack::Attack.throttle('login', limit: 60, period: 60.minute) do |req|
begin
# req.params for JSON is not yet available at this step
JSON.parse(req.body.string)['email'] if req.path == '/api/v1/user_token' && req.post?
rescue JSON::ParserError => e
end
end
I want to Allow2Ban(lock) request if user is unauthenticated If user
is unauthenticated with given access token in header to get resource
object in api then lock out any source IP that generates more than 10
unauthenticated requests within a 5 minute span for 2 hours.
Rack::Attack.blocklist('basic auth crackers') do |req|
Rack::Attack::Allow2Ban.filter(req.ip, :maxretry => 10, :findtime => 5.minute, :bantime => 2.hour) do
# Return true if the authorization header is incorrect
auth = Rack::Auth::Basic::Request.new(req.env)
auth.credentials != [my_username, my_password]
end
end
First case:
In the throttle code the request is throttle even if user pass wrong credentials and not getting JWT access token. I want to throttle request if authenticated user hits for more than 60 request per hour.
api details:
POST localhost:3000/api/v1/user_token
request raw JSON body(correct credentials): {"auth": {"email": "sean#public.com", "password": "password"}}
Second case:
In the allow2block code the request user is passing access token in header to call http://localhost:3000/api/v1/users. I am not getting how to check if the user has wrong access token or he is unauthenticated.
I am using this middleware for first time and any help appreciated thanks in advance.
Get Access Token request of OAuth1.0 only work once for Magento1.9 after being redirected back from Authorization URL. Next time when requesting for Access Token I get reject_token.
What I noticed there is difference in signature of both objects' signature.
Request 1(successful):
OAuth::Consumer.new(consumer_data)
OAuth::RequestToken.from_hash(some_hash)
request_token.get_access_token(oauth_verifier: 'asdfasdagbadbv')
with signature having
oauth_nonce=\"iIHmN7obLeONSitOxFFZQI71v0k4mAsEFLFen0Lw\",
oauth_signature=\"lwj0n1AK3VJLdaXHIWEOFlYp5qc%3D\"
Request 2(unsuccessful):
OAuth::Consumer.new(consumer_data)
OAuth::RequestToken.from_hash(some_hash)
request_token.get_access_token(oauth_verifier: 'asdfasdagbadbv')
with signature having
oauth_nonce=\"KciY4tiiPDu1u029Nbdu1C325svchfESTYV1l8mvw\",
oauth_signature=\"KciY4tiiPt5Du1u029Nbdu1CzCHzvc%3D\"
This may be or may not be the issue but this is the only difference I found so far in both requests.
Please someone help me in updating oauth_nonce and signature or devise some other solution.
The problem is in the second line.
request_token.get_access_token(oauth_verifier: 'asdfasdfa')
According to Auth documentation request token should be used one time. Request token expires once we use them. You are using expired request token in the second call which causes reject_token 401 unauthorized.
Solution
Actually, request tokens are used to generate Access Token. Access Tokens can be used multiple times. So what you need is to store Access Token somewhere, generated in first request_token.get_access_token(oauth_verifier: 'asdfasdfa') line. Then you can use saved access token in the reset of your API calls. The syntax of using access token is the following:
#consumer = OAuth::Consumer.new(...)
#token = OAuth::Token.new('ACCESS_TOKEN', 'ACCESS_TOKEN_SECRET') // saved access token and secret here
#consumer.request(:post, '/people', #token, {}, #person.to_xml, { 'Content-Type' => 'application/xml' })
Versions:
Ruby 2.2.4
Rails 4.2
Omniauth-oauth2 1.3.1
Omniauth-bnet 1.1.0
Issue:
Trying to complete the authorization and token request process to Blizzard's Battle Net Community API. While I can get the authorization_code returned, when I attempt to construct a POST back to the token endpoint it keeps telling me that its an invalid request/internal server error or just returns back the following object: <Net::HTTPFound 302 Found readbody=true> which has a blank string for a response body. Details for how Blizzard recommends handling the OAuth 2 process are located here: Battle.net OAuth 2 Guide. The omniauth-bnet gem is the one Blizzard suggested but doesn't seem to handle the entire OAuth authorization and token process but I'll freely admit I'm brand new when it comes to anything OAuth related so I could be wrong.
Any help you all can provide would be very welcome!
Controller Code:
def index
client_id = ENV[BNET_CLIENT_ID]
client_secret = ENV[BNECT_CLIENT_SECRET]
uri = URI('https://us.battle.net/auth/token?
redirect_uri=https%3A%2F%2f127%2f0%2f0%2f1%3A3001%2Fauth%2Fbnet%2Fcallback
&grant_type=authorization_code
&code=' + params["code"])
req = Net::HTTP::Post.new(uri)
req.basic_auth(client_id, client_secret)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') {|http| http.request(req)}
#bnet_response = res.body
end
Process:
Click link on index.html page which triggers the authorization process to start by sending a GET request to https://us.battle.net/oauth/authorize?client_id=&client_secret=&redirect_uri=&response_type=code
BNet API accepts the request and redirects user to Battle.Net Login screen
User authorizes app and is redirected (via redirect_uri) back to the index page with query parameters for the authorization_code
My app SHOULD make a POST request using the controller code above and the response should contain the actual access token.
This is just some hastily cobbled together code while I'm trying to get this working. I'll clean it up once I get over this little speed bump. I wouldn't hardcode the URI and such normally, just getting frustrated with things.
Things I've tried:
Using Rest-Client : There's a callback redirect that it can't handle apparently
Testing Postman : When using their OAuth 2 Authorization it works just fine, also works fine if I use Postman to get the authorization code (GET) and token (POST), so I'm assuming things work on the Blizz side and my code just sucks.
config/routes.rb
Rails.application.routes.draw do
root to: 'pages#index'
get '/auth/:provider/callback', to: 'pages#index'
end
I'm not familiar with the Battle.net API, however reading your process it seems you are getting an authorization code back from the redirect in the form of a query parameter (accessed by params[:code]) correct?
You also mention that this works using Postman indicating the flaw must be somewhere in the code.
I would suggest using the following snippet and let me know if it works. I would encourage using puts url after url = ... code to ensure the URI looks exactly as you want it (and has the correct code, client id, secret, redirect_uri).
require 'uri'
require 'net/http'
# Remember to change the redirect uri to match yours
redirect_uri = 'https://my-domain-name.com/auth/bnet/callback'
# Double check the environment variable names for BNET ID / Secret
url = URI("https://us.battle.net/oauth/token?redirect_uri=#{redirect_uri}&code=#{params[:code]}" \
"&grant_type=authorization_code&scope=wow.profile&client_id=#{ENV['BNET_CLIENT_ID']}" \
"&client_secret=#{ENV['BNET_CLIENT_SECRET']}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_body
Ensure your domain is accessible over https as this seems to be required.
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