I'm working on the front-end for our web app which is constructed using Angular.
The backend is a rails app with a modification to allow it to act as an API as well as a normal Rails application (pre-existing application here so it makes a-lot of sense to keep it)
I'm using JWT's on the front-end to validate a user but I also issue a refresh_token so they can get a new JWT after a certain amount of time. The code looks like this:
def delegation
user = User.user_from_refresh_token params[:refresh_token]
render json: {jwt: user.generate_auth_token}
end
This is setup as a POST in the route file:
post "auth/delegation"
So the line in question is this one:
user = User.user_from_refresh_token params[:refresh_token]
which would mean you'd expect the user.rb model to contain a method called user_from_refresh_token which I do:
def self.user_from_refresh_token token
refresh_token = RefreshToken.find_by!(token: token)
raise API::Unauthorized.new("This refresh token has been revoked") if refresh_token.revoked?
find(refresh_token.user_id)
end
But the error that I get when I call this is:
NoMethodError: undefined method `user_from_refresh_token' for User
Question 1 - How come I can't call this like it is? Shouldn't this just work?
Note:
I fixed this issue by modifying the autoloader to add this in:
config.autoload_paths += %W(#{config.root}/lib #{config.root}/model/concerns)
Question 2 - Why does this have to be done in order for the previous part to work?
Related
I’m currently working on changing the rails backend of a project from REST to graphql and I’m running into an error with authentication following their tutorial - https://www.howtographql.com/graphql-ruby/4-authentication/
I’m using the GraphiQL engine to test all my requests and receiving error status 422 unprocessable Entity, User Must Exist
Which makes sense because the mutation I am executing is to create a new color - which has a belongs_to relationship to User.
About halfway through the page linked above ^^^ it says this:
With the token that the signinUser mutation provides, apps can
authenticate subsequent requests. There are a couple of ways this can
be done. In this tutorial, we are just going to use the built-in
session, since this doesn’t add any requirements to the client application. The GraphQL server should be able to get the token from
the session header on each request, detect what user it relates to,
and pass this information down to the resolvers.
I’m able to successfully return an auth token through the signinUser method like the docs show previously on the same page - the method that it posts to also saves the token to this supposed session in this method here (also from the same link posted above ^^^) :
def call(_obj, args, ctx)
input = args[:email]
return unless input
user = User.find_by email: input[:email]
return unless user
return unless user.authenticate(input[:password_digest])
crypt = ActiveSupport::MessageEncryptor.new(ENV["SECRET_BASE_KEY"])
token = crypt.encrypt_and_sign("user-id:#{ user.id }")
puts "please **********************************"
p ctx[:session]
ctx[:session][:token] = token
puts "please **********************************"
p ctx[:session]
OpenStruct.new({
user: user,
token: token
})
end
You’ll be able to see in my desperate struggle that I p’d out the session right before the method returns and not surprisingly saw that it contained the token for that users sign in.
However, when I proceeded to execute the mutation to create a color, my expectation was that the session would still contain that token and I’d be able to commit that color successfully. That was not the case and when I p’d out the session for this request, it return an empty hash.
I cant find any information about how the built in graphql session works - and I’m brand new to graphql in general.
My main questions would be - is the graphql session supposed to be caching token information? Why is it that the information is not carrying over to requests following signinUser ? Should I even bother with trying to use the auth in this tutorial since the docs claim that this authentication method is not a long term solution?
I know this is a lot but would really appreciate an extra brain on this.
Thanks in advance!
PS. I understand the tutorial uses links and I am using colors here - that is intentional and I have done my best to make sure that semantic differences were not causing any errors.
Rails version - 5.2.2 (using api only)
graphql - 1.7.4
graphiql rails - 1.4.4
Same as REST APIs GraphQL does not store any information between two subsequent requests, You have to pass authentication token returned in sign in mutation to all subsequent requests where you want current user-related information.
You should do something like below in graphql_controller.rb
class GraphqlController < ApplicationController
def execute
variables = ensure_hash(params[:variables])
query = params[:query]
operation_name = params[:operationName]
context = {
current_user: current_user
}
result = GraphqlTutorialSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
render json: result
end
private
# set current user here
def current_user
# you can token here
token = request.headers['Authorization']
return nil unless token
# find current user from this token
end
# Handle form data, JSON body, or a blank value
def ensure_hash(ambiguous_param)
# ...code
end
end
A colleague of mine pointed out that "session" is part of rails and should create a cookie that would be accessible from the next request.
I mentioned that I was using Rails version - 5.2.2 (using api only) - well when you use the -api flag when initializing a new rails project, it adds these lines to application.rb
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
Notice this line in particular - Middleware like session, flash, cookies can be added back manually.
I commented out config.api_only = true and this added cookies back to the application/ allowed me to make the next request with an existing user.
You can also add these lines I found from "Lysender" on his post - Rails 5 – API Only – Enable Cookies and Sessions if you'd prefer not to remove the api-only feature.
config.api_only = true
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_coookie_name', expire_after: 30.days
I'm using responders gem to dry up my controllers. Here's my current code:
class OfficehoursController < ApplicationController
def new
#officehour = Officehour.new
end
def create
#officehour = Officehour.create(officehour_params)
respond_with(#officehour, location: officehours_path)
end
def officehour_params
params.require(:officehour).permit(:end, :start, :status)
end
end
The problem that I'm facing right now is:
When I send valid parameters to create, it redirects to officehours/ as expected, however when I get 422 (validation error), it changes the URL from officehours/new to officehours/ (however it stays at the form page... idk why). The same happens for edit/update actions.
So, I want to stay at the .../new or .../edit when I get 422 error, how can I do this?
I don't think the issue comes from the gem. It just follows RESTFUL API, so does Rails.
That means the path for creating office hours is /officehours. Let's talk about your case:
There is nothing to say when we creating successfully. In your case, you redirect users to officehours_path.
When we creating unsuccessfully, we need to re-render the error form to users. But we were rendering the form in create action. As I said above, the URL for creating is /officehours, so you will see the /officehours instead of officehours/new
In order to keep the url /officehours/new:
We can set /officehours/new instead of /officehours for :create action. But you should not do that, are we going to break RESTFUL API?
Creating via AJAX to keep the URL persisted and you have to handle everything, eg: set flash messages on client, using Javascript to redirect in case of success, render the form error in case failure, ... And I don't think the gem help you dry up your application anymore.
Hope it helps!
I don't think so that it's a problem in responders gem, as I've noticed the same in rails applications. It seems like the default behaviour of rails applications.
take a look at this link for the explanation.
class User < ActiveRecord::Base
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
end
In the rails console
user = User.first
user.facebook.get_object("me")
First question is, User model has a column called oauth_token, which is magically getting replaced in Koala::Facebook::API.new(oauth_token) here, even though I am not passing oauth_token explicitly
Second question, what is the need of caching #facebook variable here? I am following this rails casts tutorial
https://www.youtube.com/watch?v=JqeZy2G2C1g
#First question
What do you mean by getting replaced?
From what I see when initializing API (API.new(oauth_token)) you are passing ouath_token that is assigned to specific user. When calling User.first you're loading first user from database, and ActiveRecord automatically assigns oauth_token to newly created instance. If ouath_token was never saved (thus present in db record) it would be nil.
#Second question
This is in general done for performance reasons in order to avoid multiple API calls within same request. However, usually response from API call is cached not the API instance itself. Maybe that Koala API instance calls webservice directly in constructor (in its initialize method).
My note - keeping such methods like facebook calling external webservice within User model ends with very difficult to change code.
It's better to move it somewhere out from this model.
As a RoR newbie, I'm trying to understand how/where to correctly subclass a gem.
I'm using the ruby-box gem (https://github.com/KonaTeam/ruby-box) in my application, and attempting to follow the example on their github page where they subclass session.rb in order to obtain a refreshed token as follows:
class MyBoxSession < RubyBox::Session
# override call to refresh token so we can update the tokens store
def refresh_token(refresh_token)
ut = MyTokens.where(refresh_token: refresh_token, provider: 'box').first
begin
super
ut.access_token = #access_token.token
ut.refresh_token = #access_token.refresh_token
ut.save!
rescue OAuth2::Error => e
# token pair must just be bad
ut.destroy
end
end
end
I'm my box controller I use ruby-box to create the initial session, but I'm unclear on where I would add in the code above? In the box controller, or do I need to pull in session.rb?
Any and all assistance is greatly appreciated. Thanks.
If you write just a ruby script, put the code inline. If you are using rails I believe in library lib/, and when you have need just call a library methods from a controller/helper. If the connection is persisted, initialize it once on startup.
Does anyone know where in the Auth Hash the user_likes are stored?
I've tried different combos:
auth.extra.raw_info.user_likes
auth.extra.raw_info.likes
I've got the permission, I just don't know how to get to the user_likes array.
Omniauth-Facebook gem
After some time (edit)
Alternatively I could do something like this using the HTTParty gem:
url = "https://graph.facebook.com/#{auth.uid}/likes&access_token=#{auth.credentials.token}"
#likes = HTTParty.get(url)
But I really want to know if its possible through the omniauth authentication process...
The omniauth-facebook gem uses the /me call to fill the raw_info hash. Per the facebook docs http://developers.facebook.com/docs/reference/api/user/, likes are not included in the user object but are a connection that can be accessed by calling https://graph.facebook.com/me/likes.
Hope this response though late, helps someone who is trying to figure out what is inside the different hashes part of auth hash object (schema reference: https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema)
Instead of trying different combinations (as posted in the question), one could simply render the object as an yaml to browser. Now page source of the browser output would give a clear picture of what is returned part of the auth object. Other option is to put a debug break and inspect the auth object in the callback controller (an ide would be very helpful in this case).
routes.rb
...
match '/auth/facebook/callback' => 'authentications#create'
...
authentications_controller.rb (or your controller that receives the call back)
class AuthenticationsController < ApplicationController
...
def create
# a simple code to understand the content of the auth object
render :text => request.env["omniauth.auth"].to_yaml
end
end