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 ..
Related
I want to send clients that did not complete a checkout an email with a magic link that will log them in before hitting an update action in a controller.
I'm sending the following link in the email body:
<%= link_to(
"Continue to checkout",
"#{checkout_url(host: #account.complete_url, id: #user.current_subscription_cart)}?msgver=#{#user.create_message_verifier}",
method: :patch,
subscription_cart: { item_id: #item_id },
) %>
My checkouts_controller has an update action:
def update
# update cart with item_id param and continue
end
And my routes look like this:
resources :checkouts, only: [:create, :update]
which gives the following update route:
checkout_path PATCH /checkouts/:id(.:format) checkouts#update
The link_to in the email body produces a link with a data-method="patch" property
<a data-method="patch" href="https://demo.test.io/checkouts/67?msgver=TOKEN">Continue to checkout</a>
=> https://demo.test.io/checkouts/67?msgver=TOKEN
but when I click on it I get the following error:
No route matches [GET] "/checkouts/67"
Why is it attempting a GET request when I'm specifying method: :patch ?
As pointed out by #AbM you need to send out a link to a route that responds to GET requests. Emails clients are unlikely to let you run JS or include forms in the email body - so you shouldn't assume that you'll be able to send anything but GET.
If you want an example of how this can be done you don't have to look further then the Devise::Confirmable module that solves pretty much the exact same problem:
Prefix Verb Method URI Pattern Description
new_user_confirmation GET /users/confirmation/new Form for resending the confirmation email
user_confirmation GET /users/confirmation The link thats mailed out with a token added - actually confirms the user
POST /users/confirmation Resends the confirmation email
The beauty of this design is that users confirmation are modeled as a RESTful resource even if no separate model exists.
In your case the implementation could look something like:
resources :checkouts do
resource :confirmation, only: [:new, :create, :show]
end
# Handles email confirmations of checkouts
class ConfirmationsController < ApplicationController
before_action :set_checkout
# GET /checkouts/1/confirmation/new
# Form for resending the confirmation email
def new
end
# POST /checkouts/1/confirmation
# Resends the confirmation email - because shit happens.
def create
#todo generate new token and resend the confirmation email
end
# GET /checkouts/1/confirmation&token=jdW0rSaYXWI7Ck_rOeSL-A
# Confirms the checkout by verifying that a valid token is passed
def show
if #checkout.valid_token?(params[:token])
#checkout.confirm!
redirect_to '/whatever_the_next_step_is'
else
flash.now('Invalid token')
render :new, status: :unauthorized
end
end
private
def set_checkout
#checkout = Checkout.find(params[:checkout_id])
end
end
Here we are taking a slight liberty with the rule that that GET requests should always be idempotent as clicking the link actually updates the checkout. Its a fair tradeoff though as it requires one less click from the user.
If you want to maintain that idempotency you could send a link to the edit action which contains a form to update the confirmation. Devise::Invitable does this.
Below is the relevant code (do let me know if I'm missing anything!).
routes.rb:
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
devise_scope :user do
match '/sessions/user', to: 'devise/sessions#create', via: :post
get '/join' => 'users/registrations#new'
end
resources :users do
resources :video_lessons do
resources :likes
end
end
resources :users, only: [:show, :index]
devise.rb:
if Rails.env.production?
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID_PROD'[, ENV['GOOGLE_CLIENT_SECRET_PROD'], {}
else
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID_DEV'], ENV['GOOGLE_CLIENT_SECRET_DEV'], {}
end
omniauth_callbacks_controller.rb
def google_oauth2
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env['omniauth.auth'])
if #user.persisted?
flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google'
sign_in_and_redirect #user, event: :authentication
else
session['devise.google_data'] = request.env['omniauth.auth'].except(:extra) # Removing extra as it can overflow some session stores
redirect_to new_user_registration_url, alert: #user.errors.full_messages.join("\n")
end
end
devise/registrations/new.html.erb
<%= link_to "Sign up with Google", user_google_oauth2_omniauth_authorize_path %>
The authorized redirect URIs in my Developer's Console are:
http://localhost:3000/users/auth/google_oauth2 and
https://localhost:3000/users/auth/google_oauth2
I've followed the docs (https://github.com/zquestz/omniauth-google-oauth2) exactly, to no avail.
Thank you for your help!
I recently got this working through this guide.
A more descriptive resource, and the source of the guide, is this official wiki example.
This commit shows all the files I changed to get google and github set up on my rails login page using the guide I mentioned first.
Just make sure when you use either of the guides, that you remove extra part of the data as it can overflow some session stores. The guides do not include this important piece of information. So in this line of omniauth_callbacks_controller.rb, your current code is correct:
...
else
session['devise.google_data'] = request.env['omniauth.auth'].except(:extra) # Removing extra as it can overflow some session stores
....
Good luck!
<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
The request should be 'post'
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.
I'm trying add a new button(Resend Email) to an existing(Send Email) method/route on Active Admin rails. I have done these kind of tasks on a normal rails with MVC architecture. Where I used to add the required route path in a html.erb file. I'm finding it quiet difficult to implement the same on Active Admin since it doesn't have that MVC look.
On routes.rb file I have
routes.rb
Rails.application.routes.draw do
mount Bootsy::Engine => '/bootsy', as: 'bootsy'
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
devise_for :users,controllers: {registrations: 'users/registrations',sessions: 'users/sessions',confirmations: 'users/confirmations'}
end
Here confirmations: 'users/confirmations' is used to send Email Verfication mail to the users. That's working fine. Some users forget to look into their email. So I'm trying to create a "Resend Email" button on the ActiveAdmin file, which on submitting sends the verification email mail again.
What path should I set inorder to send the mail again. I'm new to active-admin rails. Your help would be much appreciated.
User.rb
ActiveAdmin.register User, :as => 'User' do
action_item only: :show do
link_to('Resend Email', )
end
end
user_mailer.rb
def confirmation_instructions(record, token, opts={})
#system_email = SystemEmail.find_by(title: 'Email Verification')
#subject = #system_email.try(:subject).to_s
#subject = "Email Verification" if #subject.blank?
opts = {subject: #subject}
#token = token
devise_mail(record, :confirmation_instructions, opts)
end
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