I have a route with my JWT token as a path variable but it doesn't work with the JWT token eyJhbGciOiJub25lIn0.eyJjb25maXJtYXRpb25fc2VudF9hdCI6IjIwMTgtMDEtMjQgMDY6MDQ6MzEgKzEzMDAiLCJleHBpcmVzX2F0IjoiMjAxOC0wMS0yNyAwNjowNDozMSArMTMwMCIsInVzZXJfaWQiOjM5fQ.
When I switch the JWT token to something less complicated e.g. 5 then the route works. I'm guessing that the format of the JWT need some special treatment in the rails router?
get '/verify/:jwt', to: 'users#verify_email'
No route matches [GET] "/verify/eyJhbGciOiJub25lIn0.eyJjb25maXJtYXRpb25fc2VudF9hdCI6IjIwMTgtMDEtMjQgMDY6MDQ6MzEgKzEzMDAiLCJleHBpcmVzX2F0IjoiMjAxOC0wMS0yNyAwNjowNDozMSArMTMwMCIsInVzZXJfaWQiOjM5fQ
routes.rb
Rails.application.routes.draw do
extend DeviseRoutes
extend PageRoutes
# Root route
root to: "pages#home"
end
devise_routes.rb
module DeviseRoutes
def self.extended(router)
router.instance_exec do
devise_for :users, path: '', path_names: {
sign_in: 'login',
sign_out: 'logout',
sign_up: 'register',
edit: '/user/edit'
}, controllers: { registrations: 'users' }
# User profile management
devise_scope :user do
get '/profile/:id', to: 'users#profile_home', as: 'profile'
# Verify email
get '/verify', to: 'users#verify_email'
end
end
end
end
users_controller
class UsersController < Devise::RegistrationsController
include AuthenticationConcern
require 'utilities/custom_mailer'
require 'jwt'
def profile_home
#user_id = params[:id]
check_user_route_access current_user, #user_id
#user = User.includes(:skills).find_by(id: #user_id)
#skills = #user.skills.eager_load(:profession)
end
def create
super
if current_user
CustomMailer.send_initial_user_signup(user_id: current_user.id,
to_email: current_user.email,
user_full_name: current_user.full_name)
end
end
def verify_email
jwt_token = params[:token]
#jwt_token_decoded = true
# make sure the jwt_token can be decoded, if not crash softly via
# error on the page rather than hard crash
begin
decoded = (JWT.decode jwt_token, nil, false)[0]
rescue
#jwt_token_decoded = false
end
if #jwt_token_decoded
decoded_expires_at = decoded["expired_at"]
user_id = decoded["user_id"]
#user = User.find_by(id: user_id)
# 1. if user is verified, redirect to login page
if #user != nil and #user.confirmed_at != nil
# flash[:success] = t('successfully_created')
redirect_to new_user_session_path
end
# 2. once verified, provide option in view to go to login page
end
# render verification page
end
end
Does anyone have any suggestions?
In your route the JWT consists of various special characters like '.' because of which the rails router is unable to route it properly.
Solution :-
1:- Use route globbing using wildcard segments
Example :-
Change you route to this
get '/verify/*jwt', to: 'users#verify_email', constraints: { jwt: /.*/ }
This will give params[:jwt] = "passed jwt in url"
You can customize the regex used here to make it more meaningful for jwt token which consists of two '.' and other special characters.
Read this for more information :- Rails route globbing and wildcard segments
The most probable cause of this issue is that you have another route before than get '/verify/:jwt', to: 'users#verify_email'. Rails Router gives priority to the first instance it finds.
So if for example, your routes.rb looks like this:
resources :verify # this may not be your case, just an example
get '/verify/:jwt', to: 'users#verify_email'
In this case, rails will ignore get line, and whenever you GET /verify/<anything> will be routed to verify#show
On the other hand, if you swap those lines like this,
get '/verify/:jwt', to: 'users#verify_email'
resources :verify # this may not be your case, just an example
Then every GET /verify/<anything> will be routed to verify_email.
Related
Thank you so much to help me in advance. I'm working on an application where people authenticate with Slack and it send a notification to their workspace at the same time the contents generated automatically.
But oauth error occurs:
OAuth Error: Invalid client_id parameter
URL is like below:
https://slack.com/oauth/authorize?client_id=XXXXXXXXXXX&scope=bot%2Cusers%3Aread.email%2Ccommands%2Cusers%3Aread&state=XXXXXXXXXXXXX&tracked=1
I had a same issue when trying to build with omniauth_slack gem.
app/views/home/index.html.erb
<%= link_to "Sign in with Slack", slack_activate_path %>
app/controllers/slack_controller.rb
class SlackController < ApplicationController
def activate
oauth_state = "XXXXXXXXXXXXX"
uri = URI('https://slack.com/oauth/authorize')
uri.query = {
client_id: Rails.application.credentials[:slack][:client_id],
scope: 'bot,users:read.email,commands,users:read',
state: oauth_state,
}.to_query
redirect_to uri.to_s
end
def callback
end
end
config/routes.rb
Rails.application.routes.draw do
root 'home#index'
match '/slack/activate', to: 'slack#activate', via: 'get'
match '/slack/callback', to: 'slack#callback', via: 'get'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
I have mistyped client_id on credentials.
This requires not ..
could one advise me how to get a url like this in rails
http://www.example.com/users/5/ian
i tried the below but unsure:
route file:
devise_for :users
resources :users do
resources :socials
end
get '/users/:id/:firstname', controller: 'users', action: 'show'
users_controller.rb
def show
#user = User.find(params[:id], params[:firstname])
end
If you are trying to achieve 'friendly urls' then I suggest using this:
You don't have to create a special route:
get '/users/:id', controller: 'users', action: 'show'
Instead you have your model overwrite the to_param method:
class User
...
def to_param
"#{id}-#{firstname.try(:parameterize)}"
end
...
end
The url helper calls to_param to build the urls. If you overwrite it this way, you will receive a url like this:
http://localhost:3000/users/1-artloe
The rails find method calls .to_i on the params[:id] which, thankfully, interprets strings as number until it arrives at a character that can't become a number.
Examples:
'123abcde'.to_i # 123
'123-asdf'.to_i # 123
'asdf-123'.to_i # 0
So except for overwriting to_param, you don't have to do anything.
Try replacing this
def show
#user = User.find_by_id_and_firstname(params[:id], params[:firstname])
end
If what you are trying accomplish is "friendly urls" you would do it by:
# GET /users/1
# GET /users/joe
def show
#user = User.find_by!('id = :x OR firstname = :x', x: params[:id])
end
However you must ensure that property you are using in URLs is URL safe and unique. Usually a separate username or slug field is used.
Nothing special is needed in terms of routes.
These gems provide "friendly urls":
stringex
friendly_id
I"m trying to create a rails app that functions as a url shortener. I'm having trouble configuring the routes. How can I allow for the user visit my site and be redirected to the site base on the url they enter. I.E. mysite.com/any_random_url.
routes.rb
Rails.application.routes.draw do
get 'home/index'
get 'home/about'
get 'home/:id' => 'home#show'
root 'home#show/:id'
..
home_controller.rb
class HomeController < ApplicationController
def index
end
def about
end
def show
url = params[:id]
#url = ShortUrl.where(["url = ?", url]).first
if #url.nil?
return redirect_to action: 'index', status: 307
else
return redirect_to #url
end
end
If you want to be able to have multiple slashes you'll need something like:
get '*id', to: 'home#show'
If you only want a single subpath (i.e. /23af1) it's probably better to use:
get ':id', to: 'home#show'
You can find more info in the Rails Guide
I have this controller.
class SessionsController < Devise::SessionsController
# GET /resource/sign_in
def new
self.resource = build_resource(nil, :unsafe => true)
clean_up_passwords(resource)
respond_with(resource, serialize_options(resource))
end
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in, :username => resource.username) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
and this step-defenitions for cucumber:
Given /^a user "(.*?)" exists$/ do |user_name|
#user = User.create!(:username => user_name , :password => "tamara")
end
When /^he logs in$/ do
visit("/users/sign_in")
fill_in('Username', :with => "roelof")
fill_in('Password', :with => "tamara")
click_button('Sign in')
end
Then /^he should see "(.*?)"$/ do |message|
page.should have_content(message)
end
Everything works fine only after a successfull login I get redirect to the homepage and not to the login succceed page. So I don't see the flash message.
Roelof
Edit 1 : I checked the controller and resource_name and resource seems to have the right values.
the standard DeviseHelper for after_sign_in_path_for is the signed_in_root_path
# The scope root url to be used when he's signed in. By default, it first
# tries to find a resource_root_path, otherwise it uses the root_path.
You can check your routes and scope , and even debug by duplicating the Devise signed_in_root_path into your ApplicationController with a debugger line
By default, Devise's internal after_sign_in_path_for(resource) method first tries to find a valid {resource}_return_to key in the session, then it fallbacks to {resource}_root_path, otherwise it uses your root path route.
For instance, setting user_root_path would set direct path in the users scope after successful sign in (probably what you want):
# config/routes.rb
...
devise_for :users do
get 'users', :to => 'users#show', :as => :user_root
end
Or even:
# config/routes.rb
...
match 'user_root' => 'users#show'
Another way to override the redirect on successful login by overriding after_sign_in_path_for(resource):
# app/controllers/application_controller.rb
...
def after_sign_in_path_for(resource)
# Put some path, like:
current_user_path
end
Links for you to read:
How to: redirect to a specific page on successful sign in out
Method: Devise::Controllers::Helpers#after_sign_in_path_for
How To: Customize the redirect after a user edits their profile
How To: Change the redirect path after destroying a session i.e. signing out
I have three roles: Instuctor, Student, Admin and each have controllers with a "home" view.
so this works fine,
get "instructor/home", :to => "instructor#home"
get "student/home", :to => "student#home"
get "admin/home", :to => "admin#home"
I want to write a vanity url like below which will route based on the role of the user_id to the correct home page.
get "/:user_id/home", :to => "instructor#home" or "student#home" or "admin#home"
How do I accomplish this?
I'm providing an alternate approach as this SO question comes up near the top when searching for role based routing in Rails.
I recently needed to implement something similar but wanted to avoid having a large number of conditionals in the controller - this was compounded by the fact that each of my user roles required completely different data to be loaded and presented. I opted to move the deciding logic to the routing layer by using a Routing Constraint.
# app/constraints/role_route_constraint.rb
class RoleRouteConstraint
def initialize(&block)
#block = block || lambda { |user| true }
end
def matches?(request)
user = current_user(request)
user.present? && #block.call(user)
end
def current_user(request)
User.find_by_id(request.session[:user_id])
end
end
The most important part of the above code is the matches? method which will determine whether or not the route will match. The method is passed the request object which contains various information about the request being made. In my case, I'm looking up the :user_id stored in the session cookie and using that to find the user making the request.
You can then use this constraint when defining your routes.
# config/routes.rb
Rails.application.routes.draw do
get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }
end
With the above in place, an administrator making a request to /home would be routed the home action of the AdministratorsController, an instructor making a request to /home would be routed to the home action of the InstructorsController, and a student making a request to /home would be routed to the home action of the StudentsController.
More Information
If you're looking for more information, I recently wrote about this approach on my blog.
You can't do this with routes because the routing system does not have the information required to make this decision. All Rails knows at this point of the request is what the parameters are and does not have access to anything in the database.
What you need is a controller method that can load whatever data is required, presumably the user record, and redirects accordingly using redirect_to.
This is a fairly standard thing to do.
Update:
To perform all of this within a single controller action you will need to split up your logic according to role. An example is:
class HomeController < ApplicationController
def home
case
when #user.student?
student_home
when #user.admin?
admin_home
when #user.instructor
instructor_home
else
# Unknown user type? Render error or use a default.
end
end
protected
def instructor_home
# ...
render(:template => 'instructor_home')
end
def student_home
# ...
render(:template => 'student_home')
end
def admin_home
# ...
render(:template => 'admin_home')
end
end