Rails prevent url from being changed - ruby-on-rails

I'm trying to figure out a way to prevent someone from changing parts of the url. I've setup my website so that no one can sign up or create a account so anyone visiting is a guest.
For example, I've set the url in this part of the website to look for the value of 1a from /pathfinder/a/quest1/1a/q1sub1/ in order to display a certain part of the index page of q1sub1.
If someone were to change the value of 1a to 1n I would like to test if the url has changed and give some sort of error message.
Not sure what code excerpts to share in this case so let me know if you need more info.
I'm open to any ideas and appreciate any input. Thank you!

The, URL by design, can't be controlled by your application. But if you want to track where the user was previously, in all controller you have have access to request.referrer which should tell you where the request came from. You might add some conditional logic to your controller and redirect the user if the request.referrer is something you want to restrict. So in your controller action something like this:
def show # for example
if request.referrer != "http://example.com/myformpage"
redirect_to root_path, notice: "Invalid access"
else
# do stuff
end
end
However this is still not secure as headers can be spoofed (thanks to #Holgar Just). If you really want security in your application you should read the documentation and if you need more granular control over users permissions, maybe checkout CanCanCan gem

A way to do what you describe is to fingerprint the URLs before sending them to your client, using a hash function that includes a secret pepper. The idea is that if the client tampers with and URL the fingerprint won't match anymore, and since the fingerprint is generated with a server-side secret, the client won't be able to generate a new one that matches the new URL.
Here is an example.
The user will access /foo and pass a custom data parameter. The application will use this param to build a customized URL with format /my/secret/:one/path/:two, and redirect the client to it. This is just to keep things simple, and the same approach could be applied if the generated URL were to be used as a <a href="..."> in a page.
The generated URL contains a fingerprint, and if the client tampers with either the URL or the fingerprint, or if the fingerprint is missing, the application will respond with a 403.
Let's look at the code. The routes:
Rails.application.routes.draw do
get :foo, to: 'example#foo'
get '/my/secret/:one/path/:two', to: 'example#bar', as: 'bar'
end
And the controller:
class ExampleController < ApplicationController
# GET /foo?data=xx foo_path
#
def foo
user_data = request[:data]
go_to_path = bar_path(one: user_data, two: "foobar")
go_to_path += "?check=#{url_fingerprint(go_to_path)}"
redirect_to go_to_path
end
# GET /my/secret/:one/path/:two bar_path
#
def bar
unless valid_request?
render plain: "invalid request!", status: 403
return
end
render plain: "this is a good request", status: 200
end
private
SECRET = ENV.fetch("URL_FINGERPRINT_SECRET", "default secret")
# Calculate the fingerprint of a URL path to detect
# manual tampering.
#
# If you want to restrict this to a single client, add
# some unique identifier stored in a cookie.
#
def url_fingerprint(path)
Digest::SHA2.hexdigest(path + SECRET)
end
def valid_request?
return false unless params[:check].present?
params[:check] == url_fingerprint(request.path)
end
end
With this, the client would start with:
$ curl -i -s http://localhost:3000/foo?data=hello | grep Location
Location: http://localhost:3000/my/secret/hello/path/foobar?check=da343dd84accb4c0f5f7ff1d6e68152ac124ca1a39ce4746623bcb7b9043cab3
And then:
curl -i http://localhost:3000/my/secret/hello/path/foobar?check=da343dd84accb4c0f5f7ff1d6e68152ac124ca1a39ce4746623bcb7b9043cab3
HTTP/1.1 200 OK
this is a good request
But if the URL gets modified:
curl -i http://localhost:3000/my/secret/IWASCHANGED/path/foobar?check=da343dd84accb4c0f5f7ff1d6e68152ac124ca1a39ce4746623bcb7b9043cab3
HTTP/1.1 403 Forbidden
invalid request!
The same would happen if the fingerprint itself was modifed or if it was missing.

Related

How can I implement Whatsapp life QR code authentication

How can I create a dynamic QR code on a rails app such that the moment it is scanned and successfully processed, the open page bearing the QR code can then just redirect to the success page.
This is similar to the whatsapp web implementation where the moment the android app scans the QR code, the page loads the messages.
Am more interested in is the management of the sessions. When the QR is scanned am able to reload the page where it was displayed and then redirect to another page. any idea?
You could update the User model to be able to store an unique token value to use in you QR Codes; e.g.
$ rails generate migration add_token_to_user token:string
Or a separate related model
$ rails generate model Token value:string user:belongs_to
Then generate unique Token value that can be used within an URL and encode it
into a QRCode
# Gemfile
gem "rqrcode"
# app/models/token.rb
require "securerandom"
class User < ActiveRecord::Base
def generate_token
begin
self.token = SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
end while self.class.exists?(token: token)
end
def qr_code
RQRCode::QRCode.new(
Rails.application.routes.url_helpers.url_for(
controller: "session",
action: "create",
email: email,
token: token
)
)
end
end
Then display this QRCode somewhere in your application
# app/views/somewhere.html.erb
<%= #token.qr_code.as_html %>
Then wire up your application's routes and controllers to process that generated
and encoded QRCode URL
# config/routes.rb
Rails.application.routes.draw do
# ...
get "/login", to: "sessions#new"
end
# app/controller/sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email], token: params[:token])
if user
session[:user_id] = user.id # login user
user.update(token: nil) # nullify token, so it cannot be reused
redirect_to user
else
redirect_to root_path
end
end
end
References:
whomwah/rqrcode: A Ruby library that encodes QR Codes
Module: SecureRandom (Ruby 2_2_1)
#352 Securing an API - RailsCasts
I am adding a new answer for two reasons:
1. Acacia repharse the question with an emphasis on What's App redirection of
the page with the QR Code being view, which I did not address in my initial
solution due a misunderstanding of the problem, and
2. Some people have found the first answer helpful and this new answer would
change it significantly that whilst similar, but no longer the same
When the QR is scanned am able to reload the page where it was displayed and
then redirect to another page
-- Acacia
In order to achieve this there requires to some kind of open connection on the
page that is displaying the QRCode that something interpretting said QRCode can
use to effect it. However, because of the application you trying to mimic
requires that only that one User viewing the page is effected, whilst not
actually being logged in yet, would require something in the page to be unique.
For the solution to this problem you will need a couple of things:
An unique token to identify the not logged-in User can use to be contacted /
influenced by an external browser
A way of logging in using JavaScript, in order to update the viewed page to
be logged after previous step's event
Some kind of authentication Token that can be exchange between the
application and the external QRCode scanner application, in order to
authentication themselves as a specific User
The following solution stubs out the above 3rd step since this is to
demonstrate the idea and is primarily focused on the server-side of the
application. That being said, the solution to the 3rd step should be as simple
as passing the know User authentication token by appending it to the URL within
the QRCode as an additional paramater (and submitting it as a POST request,
rather than as a GET request in this demonstration).
You will need some random Tokens to use to authentication the User with and
exchange via URL embedded within the QCcode; e.g.
$ rails generate model Token type:string value:string user:belongs_to
type is a reserverd keyword within Rails, used for Single Table Inheritance.
It will be used to specific different kinds of / specialized Tokens within this
application.
To generate unique Token value that can be used within an URL and encode it
into a QRCode, use something like the following model(s) and code:
# Gemfile
gem "rqrcode" # QRCode generation
# app/models/token.rb
require "securerandom" # used for random token value generation
class Token < ApplicationRecord
before_create :generate_token_value
belongs_to :user
def generate_token_value
begin
self.value = SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
end while self.class.exists?(value: value)
end
def qr_code(room_id)
RQRCode::QRCode.new(consume_url(room_id))
end
def consume_url(room_id)
Rails.application.routes.url_helpers.url_for(
host: "localhost:3000",
controller: "tokens",
action: "consume",
user_token: value,
room_id: room_id
)
end
end
# app/models/external_token.rb
class ExternalToken < Token; end
# app/models/internal_token.rb
class InternalToken < Token; end
InternalTokens will be only used within the application itself, and are
short-lived
ExternalTokens will be only used to interact with the application from
outside; like your purposed mobile QRCode scanner application; where the User
has either previously registered themselves or has logged in to allow for
this authentication token to be generated and stored within the external app
Then display this QRCode somewhere in your application
# e.g. app/views/tokens/show.html.erb
<%= #external_token.qr_code(#room_id).as_html.html_safe %>
I also hide the current #room_id within the <head> tags of the application
using the following:
# e.g. app/views/tokens/show.html.erb
<%= content_for :head, #room_id.html_safe %>
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>QrcodeApp</title>
<!-- ... -->
<%= tag("meta", name: "room-id", content: content_for(:head)) %>
<!-- ... -->
</head>
<body>
<%= yield %>
</body>
</html>
Then wire up your application's routes and controllers to process that generated
and encoded QRCode URL.
For Routes we need:
Route to present the QRCode tokens; "token#show"
Route to consume / process the QRCode tokens; "token#consume"
Route to log the User in with, over AJAX; "sessions#create"
We will also need some way of opening a connection within the display Token page
that can be interacted with to force it to login, for that we will need:
mount ActionCable.server => "/cable"
This will require Rails 5 and ActionCable to implment, otherwise another
Pub/Sub solution; like Faye; will need to be used instead with older versions.
All together the routes look kind of like this:
# config/routes.rb
Rails.application.routes.draw do
# ...
# Serve websocket cable requests in-process
mount ActionCable.server => "/cable"
get "/token-login", to: "tokens#consume"
post "/login", to: "sessions#create"
get "/logout", to: "sessions#destroy"
get "welcome", to: "welcome#show"
root "tokens#show"
end
Then Controllers for those actions are as follows:
# app/controller/tokens_controller.rb
class TokensController < ApplicationController
def show
# Ignore this, its just randomly, grabbing an User for their Token. You
# would handle this in the mobile application the User is logged into
session[:user_id] = User.all.sample.id
#user = User.find(session[:user_id])
# #user_token = Token.create(type: "ExternalToken", user: #user)
#user_token = ExternalToken.create(user: #user)
# keep this line
#room_id = SecureRandom.urlsafe_base64
end
def consume
room_id = params[:room_id]
user_token = params[:user_token] # This will come from the Mobile App
if user_token && room_id
# user = Token.find_by(type: "ExternalToken", value: user_token).user
# password_token = Token.create(type: "InternalToken", user_id: user.id)
user = ExternalToken.find_by(value: user_token).user
password_token = InternalToken.create(user: user)
# The `user.password_token` is another random token that only the
# application knows about and will be re-submitted back to the application
# to confirm the login for that user in the open room session
ActionCable.server.broadcast("token_logins_#{room_id}",
user_email: user.email,
user_password_token: password_token.value)
head :ok
else
redirect_to "tokens#show"
end
end
end
The Tokens Controller show action primarily generates the #room_id value for
reuse in the view templates. The rest of the code in the show is just used to
demonstrate this kind of application.
The Tokens Controller consume action requires a room_id and user_token to
proceed, otherwise redirects the User back to QRCode sign in page. When they are
provided it then generates an InternalToken that is associated with the User
of the ExternalToken that it will then use to push a notification / event to
all rooms with said room_id (where there is only one that is unique to the
User viewing the QRCode page that generate this URL) whilst providing the
necessary authentication information for a User (or in this case our
application) to log into the application without a password, by quickly
generating an InternalToken to use instead.
You could also pass in the User e-mail as param if the external application
knows about it, rather than assuming its correct in this demonstration example.
For the Sessions Controller, as follows:
# app/controller/sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:user_email])
internal_token = InternalToken.find_by(value: params[:user_password_token])
# Token.find_by(type: "InternalToken", value: params[:user_password_token])
if internal_token.user == user
session[:user_id] = user.id # login user
# nullify token, so it cannot be reused
internal_token.destroy
# reset User internal application password (maybe)
# user.update(password_token: SecureRandom.urlsafe_base64)
respond_to do |format|
format.json { render json: { success: true, url: welcome_url } }
format.html { redirect_to welcome_url }
end
else
redirect_to root_path
end
end
def destroy
session.delete(:user_id)
session[:user_id] = nil
#current_user = nil
redirect_to root_path
end
end
This Sessions Controller takes in the user_email and user_password_token to
make sure that these two match the same User internally before proceeding to
login. Then creates the user session with session[:user_id] and destroys the
internal_token, since it was a one time use only and is only used internally
within the application for this kind of authentication.
As well as, some kind of Welcome Controller for the Sessions create action to
redirect to after logging in
# app/controller/welcome_controller.rb
class WelcomeController < ApplicationController
def show
#user = current_user
redirect_to root_path unless current_user
end
private
def current_user
#current_user ||= User.find(session[:user_id])
end
end
Since this aplication uses
ActionCable, we
have already mounted the /cable path, now we need to setup a Channel that is
unique to a given User. However, since the User is not logged in yet, we use the
room_id value that was previously generated by the Tokens Controller show
action since its random and unique.
# app/channels/tokens_channel.rb
# Subscribe to `"tokens"` channel
class TokensChannel < ApplicationCable::Channel
def subscribed
stream_from "token_logins_#{params[:room_id]}" if params[:room_id]
end
end
That room_id was also embedded within the <head> (although it could a hidden
<div> element or the id attribtue of the QRCode, its up to you), which means
it can be pulled out to use in our JavaScript for receiving incoming boardcasts
to that room/QRCode; e.g.
// app/assets/javascripts/channels/tokens.js
var el = document.querySelectorAll('meta[name="room-id"]')[0];
var roomID = el.getAttribute('content');
App.tokens = App.cable.subscriptions.create(
{ channel: 'TokensChannel', room_id: roomID }, {
received: function(data) {
this.loginUser(data);
},
loginUser: function(data) {
var userEmail = data.user_email;
var userPasswordToken = data.user_password_token; // Mobile App's User token
var userData = {
user_email: userEmail,
user_password_token: userPasswordToken
};
// `csrf_meta_tags` value
var el = document.querySelectorAll('meta[name="csrf-token"]')[0];
var csrfToken = el.getAttribute('content');
var xmlhttp = new XMLHttpRequest();
// Handle POST response on `onreadystatechange` callback
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE ) {
if (xmlhttp.status == 200) {
var response = JSON.parse(xmlhttp.response)
App.cable.subscriptions.remove({ channel: "TokensChannel",
room_id: roomID });
window.location.replace(response.url); // Redirect the current view
}
else if (xmlhttp.status == 400) {
alert('There was an error 400');
}
else {
alert('something else other than 200 was returned');
}
}
};
// Make User login POST request
xmlhttp.open(
"POST",
"<%= Rails.application.routes.url_helpers.url_for(
host: "localhost:3000", controller: "sessions", action: "create"
) %>",
true
);
// Add necessary headers (like `csrf_meta_tags`) before sending POST request
xmlhttp.setRequestHeader('X-CSRF-Token', csrfToken);
xmlhttp.setRequestHeader("Content-Type", "application/json");
xmlhttp.send(JSON.stringify(userData));
}
});
Really there is only two actions in this ActionCable subscription;
received required by ActionCable to handle incoming requests/events, and
loginUser our custom function
loginUser does the following:
Handles incoming data to build a new data object userData to POST back to
our application, which contains User information; user_email &
user_password_token; required to login over AJAX using an authentication
Token as the password (since its somewhat insecure, and passwords are usually
hashed; meaning that they unknown since they cannot be reversed)
Creates a new XMLHttpRequest() object to POST without jQuery, that sends a
POST request at the JSON login URL with the userData as login information,
whilst also appending the current HTML page CSRF token; e.g.
Otherwise the JSON request would fail without it
The xmlhttp.onreadystatechange callback function that is executed on a
response back from the xmlhttp.send(...) function call. It will unsubscribe
the User from the current room, since it is no longer needed, and redirect the
current page to the "Welcomw page" it received back in its response. Otherwise
it alerts the User something failed or went wrong
This will produce the following kind of application
You can access a copy of the project I worked on at the following URL:
Sonna/remote-url_qrcode-signin: ruby on rails - How can I implement Whatsapp life QR code authentication - Stack Overflow
The only this solution does not address is the rolling room token generation,
which would either require either a JavaScript library to generate/regenerate
the URL with the Room Token or a Controller Action that return a regenerated
QRCode as either image or HTML that can be immediately displayed within the
page. Either method still requires you to have some JavaScript that closes the
current connection and opens a new one with a new room/session token that can
used so that only it can receive mesages from, after a certain amount of time.
References:
Action Cable Overview — Ruby on Rails Guides
whomwah/rqrcode: A Ruby library that encodes QR Codes
Module: SecureRandom (Ruby 2_2_1)
#352 Securing an API - RailsCasts

rails 4-- accessing api data

I am building a sample rails 4 app and I'm unclear about something. I want to access an external API to pull data on sports news via an ajax call.
So for example if you have a list of teams in the teams#index view, when you click on one team a widget will get populated with the latest results / scores for that team-- the results info is provided by an external API service, not the local database.
Do I need to create a controller for this service to allow the rails ajax request to have a local endpoint? Should the actual request mechanism happen in this controller? or would it be better to build a helper for the data request and call that from the controller?
On the other hand it's possible to do it all via javascript in the browser.
Thanks-- I realize there's a dozen ways to do things in rails, I'm just unclear on the "right" way to handle this type of situation.
I tend to do this with a helper module that you can unit test independently. To give you a similar, trivial example, here's a module that you could use to wrap the Gravatar API:
# /lib/gravatar.rb
module Gravatar
def self.exists email
url = self.image_url email
url = url + '?d=404'
response = HTTParty.get url
return response.code != 404
end
def self.image_url email, size=nil
gravatar_id = self.gravatar_id email
size_url = size ? '?s=' + size.to_s : ''
"http://gravatar.com/avatar/#{gravatar_id}.png" + size_url
end
def self.gravatar_id email
Digest::MD5::hexdigest(email.downcase)
end
end
Then, you can make a call to Gravatar::image_url as necessary. If you wanted to be able to access a Gravatar image via an ajax call, you could simply wrap it in a controller:
# /app/controllers/api/users_controller.rb
class Api::UsersController < Api::BaseController
def gravatar_for_user_id
user = User.find_by_id(params[:id])
render plain: Gravatar::image_url user.email, :status => 200
end
end
This model can be applied to whatever external APIs you need to hit, and modularizing your interface will always make unit testing more straightforward.

Authlogic - return user to intended page after login

How can you adapt a standard Authlogic app so instead of the home page every time, it directs a user to whatever link they were trying to get to?
Example 1 - standard log in works as expected
User goes to app.com
Goes through 'login' process
Redirected to home_url
Example 2 - user attempts to visit a specific page
User goes to app.com/specific_link
Redirected to login_url for authentication
Expects to be sent back to /special_link; instead sent to home_url
How can I return the user to the link they want in Example 2?
Save the page when you go to login, then used that to return the user to that page after login, e.g.
# redirect to the login page. Call this in the login action, when successful.
def redirect_away(*params)
session[:original_uri] = request.request_uri
redirect_to(*params)
end
# returns to the original url from a redirect_away or to a default url
def redirect_back(*params)
uri = session[:original_uri]
session[:original_uri] = nil
if uri
redirect_to uri
else
redirect_to(*params)
end
end
Michael was close, but there is no "request_uri" method for the request object.
Murifox was also close, but #referrer does not give you the information you want. You probably want #url.
Try this (assumes "authenticated" returns your logged in status):
before_filter :check_authentication
.
.
.
private
def check_authentication
unless authenticated
session[:intented_page] = request.url
redirect_to login_page
end
end
Then in the controller that handles logins, after a successful login in you just do:
redirect_to session[:intended_page]
Incidentally: the #referrer method was originally mis-spelled as #referer. Now there is an alias so both spellings work, but "referrer" is proper according to the dictionary.
(Edited later): Actually #referrer might be what you want. If you are IN the controller for the target url when you check your authentication (as in the code here) then you want #url. If you are in a controller where you've been redirected from the target controller, then you want the #referrer.
I think request.referer holds this information for you. So you check for the existence of this attribute on the request object, and if it exists, you redirect to it, instead of the home_url.
if request.referer
redirect_to request.referer
else
redirect_to home_url
end
Something like this.
While the above suggestions will work use of session is not the best option cookies have a maximum size of 4,096 bytes. This is not much and if your mixing what could be a rather long URL could cause your session store to fail.
You should use the cookies method instead. This will create a separate unencrypted(by default) cookie keeping your session data separate.
cookies[:original_uri] = { value: request.request_uri, expires: 10.minutes.from_now }
Then to read the cookie you use
cookies[:original_uri]

Rails controller needs to accept cross-domain post

I'm new to Rails and indeed to web development. I'm trying to do a cross-domain post (I think) and have no clue how to do it.
I have a rails app running on webrick, let's call this 'myapp'.
I have written a bookmarklet which when selected should grab the URL from whatever website the user is on and post it to 'myapp' to be saved for that user (who will need to give his email address). How would I write a controller to deal with this?
It's hard to be specific with the amount of info you've provided, but general, you'll need to set up a route to handle the request, and define a controller action to do what you want with it.
Assuming the requests look something like POST http://myapp.com/bookmarks/create with parameters for the user's email and the url they're on, that means doing something like this:
in routes.rb:
resources :bookmarks
in bookmarks_controller.rb:
def create
if params[:email]
#user = User.find_by_email(params[:email])
if #user
#user.bookmarks.create!(:url => params[:url]
end
end
end

Zendesk Single Sign-on gem for rails 3

Does anyone know of a maintained gem that handles user authentication for the Zendesk API through an existing Rails 3 application?
I asked Zendesk IT and got sent to https://github.com/tobias/zendesk_remote_auth, but it does not look rails 3 compatible and has not been updated since 2009.
I think the article in our docs gives the impression that Zendesk SSO is difficult when in fact it is pretty easy (http://www.zendesk.com/api/remote-authentication).
# reference http://www.zendesk.com/api/remote-authentication
# you need to be a Zendesk account admin to enable remote auth (if you have not already)
# go to Settings > Security, click "Enabled" next to Single Sign-On
# three important things to pay attention to:
# Remote Login URL, Remote Logout URL, and shared secret token
# for testing on a Rails 3 application running on localhost, fill in the Remote Login URL to map
# to http://localhost:3000/zendesk/login (we will need to make sure routes for that exist)
# fill in Remote Logout URL to http://localhost:3000/zendesk/logout
# copy the secret token, you'll need it later
# first, let's create those routes in config/routes.rb
namespace :zendesk do
match "/login" => "zendesk#login" # will match /zendesk/login
match "/logout" => "zendesk#logout" # will match /zendesk/logout
end
# Above I've mapped those requests to a controller named "zendesk" but it can be named anything
# next we want to add our secret token to the application, I added this in an initializer
# config/initializers/zendesk_auth.rb
ZENDESK_REMOTE_AUTH_TOKEN = "< your token >"
ZENDESK_REMOTE_AUTH_URL = "http://yourcompany.zendesk.com/access/remote/"
# Assuming we have a controller called zendesk, in zendesk_controller.rb
require "digest/md5"
class ZendeskController < ApplicationController
def index
#zendesk_remote_auth_url = ZENDESK_REMOTE_AUTH_URL
end
def login
timestamp = params[:timestamp] || Time.now.utc.to_i
# hard coded for example purposes
# really you would want to do something like current_user.name and current_user.email
# and you'd probably want this in a helper to hide all this implementation from the controller
string = "First Last" + "first.last#gmail.com" + ZENDESK_REMOTE_AUTH_TOKEN + timestamp.to_s
hash = Digest::MD5.hexdigest(string)
#zendesk_remote_auth_url = "http://yourcompany.zendesk.com/access/remote/?name=First%20Last&email=first.last#gmail.com&timestamp=#{timestamp}&hash=#{hash}"
redirect_to #zendesk_remote_auth_url
end
def logout
flash[:notice] = params[:message]
end
end
# Note that the above index action defines an instance variable #zendesk_remote_auth_url
# in my example I simple put a link on the corresponding view that hits ZENDESK_REMOTE_AUTH_URL, doing so
# will cause Zendesk to hit your applications Remote Login URL (you defined in your Zendesk SSO settings) and pass a timestamp back in the URL parameters
# BUT, it is entirely possible to avoid this extra step if you just want to go to /zendesk/login in your app
# notice I am either using a params[:timestamp] if one exists or creating a new timestamp with Time.now
This example is quite simplistic but I just want to illustrate the basic mechanics of Zendesk SSO. Note that I'm not touching the more complicated issue of creating new users or editing existing ones, just logging in users who have an existing Zendesk account.
There is an updated example code from zendesk
# Using JWT from Ruby is straight forward. The below example expects you to have `jwt`
# in your Gemfile, you can read more about that gem at https://github.com/progrium/ruby-jwt.
# Assuming that you've set your shared secret and Zendesk subdomain in the environment, you
# can use Zendesk SSO from your controller like this example.
class ZendeskSessionController < ApplicationController
# Configuration
ZENDESK_SHARED_SECRET = ENV["ZENDESK_SHARED_SECRET"]
ZENDESK_SUBDOMAIN = ENV["ZENDESK_SUBDOMAIN"]
def create
if user = User.authenticate(params[:login], params[:password])
# If the submitted credentials pass, then log user into Zendesk
sign_into_zendesk(user)
else
render :new, :notice => "Invalid credentials"
end
end
private
def sign_into_zendesk(user)
# This is the meat of the business, set up the parameters you wish
# to forward to Zendesk. All parameters are documented in this page.
iat = Time.now.to_i
jti = "#{iat}/#{rand(36**64).to_s(36)}"
payload = JWT.encode({
:iat => iat, # Seconds since epoch, determine when this token is stale
:jti => jti, # Unique token id, helps prevent replay attacks
:name => user.name,
:email => user.email,
}, ZENDESK_SHARED_SECRET)
redirect_to zendesk_sso_url(payload)
end
def zendesk_sso_url(payload)
"https://#{ZENDESK_SUBDOMAIN}.zendesk.com/access/jwt?jwt=#{payload}"
end
end

Resources