I am having an issue getting devise_token_auth logout working.
I am working based on these SO:
How to set header and options in axios?
Why am I unable to sign out using devise_token_auth and curl?
This is the devise token auth, destroy method. It does reach this method and stops at a breakpoint.
https://github.com/lynndylanhurley/devise_token_auth/blob/c92258038c05fcc8f6a0374ccce2e63b9f8d5312/app/controllers/devise_token_auth/sessions_controller.rb#L48
def destroy
# remove auth instance variables so that after_action does not run
user = remove_instance_variable(:#resource) if #resource
client = #token.client
#token.clear!
if user && client && user.tokens[client]
user.tokens.delete(client)
user.save!
yield user if block_given?
render_destroy_success
else
render_destroy_error
end
end
#token is set in another method and it doesn't appear to be called. I don't quite get how this method is supposed to clear tokens.
My #token is #<struct DeviseTokenAuth::TokenFactory::Token client=nil, token=nil, token_hash=nil, expiry=nil> and #resource is nil at my break point/top of the method.
Client Request (Vue):
methods: {
headers() {
const config = {
headers: {
"uid": localStorage.getItem("uid"),
"client": localStorage.getItem("client"),
"access-token": localStorage.getItem("access-token")
}
}
return config
},
async handleLogOut() {
// e.preventDefault();
const headers = this.headers()
localStorage.removeItem('access-token')
localStorage.removeItem('uid')
localStorage.removeItem('client')
this.logOut();
let response = await axios.get('api/v1/auth/sign_out', null, headers)
}
}
Routes:
destroy_api_user_session GET /api/v1/auth/sign_out(.:format) api/v1/sessions#destroy
What am I doing wrong?
How is the destroy method working?
Ok, I missed this before_action method:
https://github.com/lynndylanhurley/devise_token_auth/blob/c92258038c05fcc8f6a0374ccce2e63b9f8d5312/app/controllers/devise_token_auth/concerns/set_user_by_token.rb#L26
This is where it takes your headers, checks them and sets instance variables.
By finding this I realized I was not sending the headers that I thought I was sending. I changed my http request it works fine.
axios.get('api/v1/auth/sign_out', headers)
Side Note:
The logout action by default in devise is delete but this can be modified:
config.sign_out_via = :get
Related
Note: This feature works properly on my local site and in review apps AND passes in rspec locally, but seems to only fail when testing in CircleCI.
Below I have a filter class that I use to pass in a default scope (Object.where(state: :in_progress)), and uses a filter parameter to further filter the default scope. This class is called when hitting an endpoint ex: /api/example/endpoint?filter=current_user and while testing this endpoint I've found this bug. In my other specs I've validated the data of the objects being queried, the current_user, and the filter param and have been able to narrow down the issue to the call to apply: ->(scope, user). I have verified that scope remains valid even after passing it into apply, but when the where clause is appended scope becomes an empty ActiveRecord::Relation as if the data never existed at all. I suspect there is either a bug with rspec or something about rspec that I do not understand.
class WorkOrderPreviews::IndexFilter
FILTERS = {
current_user_filter: {
apply?: ->(params) {
params[:filter] == "current_user"
},
apply: ->(scope, user) {
scope.where(technician: user)
}
}.freeze,
}.freeze
def call(scope, params, current_user)
FILTERS.keys.inject(scope.dup) do |current_scope, filter_key|
filter = fetch_filter(filter_key)
filter[:apply].call(current_scope, current_user) if filter[:apply?].call(params)
end
end
def fetch_filter(filter_key)
FILTERS.fetch(filter_key) { raise ArgumentError, 'Unknown filter.' }
end
end
My spec:
let_once(:tenant) { create_new_tenant }
let!(:staff) { tenant.owner }
let!(:tech) { FactoryBot.create(:staff, :technician) }
let!(:assigned_work_order) { FactoryBot.create(:work_order, :in_progress, technician: tech) }
let!(:unassigned_work_order) { FactoryBot.create(:work_order, :in_progress) }
let!(:estimate) { FactoryBot.create(:work_order, :estimate) }
before do
host! tenant.hostname
#access_token, refresh_token = jwt_and_refresh_token(tech.user, "user")
end
context 'without user filter' do
it 'should respond with all work orders with stat=in_progress' do
get api_internal_work_order_previews_path,
headers: { "Authorization" => #access_token }
expect(assigns(:work_orders)).to eq([assigned_work_order, unassigned_work_order])
end
end
context 'with user filter' do
it 'should respond with the assigned work order when ?filter=current_user' do
scope = WorkOrder.where(state: 'in_progress')
filtered_results = WorkOrderPreviews::IndexFilter.new.call(scope, { filter: 'current_user' }, tech)
get api_internal_work_order_previews_path,
headers: { "Authorization" => #access_token },
params: { filter: "current_user" }
expect(assigns(:work_orders)).to eq(filtered_results)
end
end
# This is the reponse body for spec #1. This data (which excludes the filter) is correct.
(byebug) JSON.parse(response.body)
[{"id"=>4, "advisor"=>nil, "technician"=>"Firstname5 Lastname5", "due_in_time"=>nil, "due_out_time"=>nil, "total_ro_billed_hours"=>0, "label"=>nil, "vehicle_plate_number"=>"JC2931", "vehicle_fleet_number"=>"12345", "vehicle_year"=>"1996", "vehicle_make"=>"Toyota", "vehicle_model"=>"T100", "vehicle_color"=>"BLACK", "vehicle_engine"=>"V6, 3.4L; DOHC 24V", "service_titles"=>[]}, {"id"=>5, "advisor"=>nil, "technician"=>nil, "due_in_time"=>nil, "due_out_time"=>nil, "total_ro_billed_hours"=>0, "label"=>nil, "vehicle_plate_number"=>"JC2931", "vehicle_fleet_number"=>"12345", "vehicle_year"=>"1996", "vehicle_make"=>"Toyota", "vehicle_model"=>"T100", "vehicle_color"=>"BLACK", "vehicle_engine"=>"V6, 3.4L; DOHC 24V", "service_titles"=>[]}]
(byebug)
I found this bug while testing if response.body returned the correct collection, and to further prove the point of this post I'm including the spec above. Weirdly enough, filtered_results returns the correct data, but the same call in my controller returns the empty ActiveRecord::Relation. The only thing I can think of is that maybe the user session in CircleCI is instantiated in a different way than when running locally, but this seems unlikely. What am I missing?
I have implemented PayPal checkout API in my rails application by using the SmartButtons and by creating the order in the server-side.
I have used the payouts-ruby-sdk gem and my code is as follows:-
index.html.erb
<!-- Set up a container element for the button -->
<div id="paypal-button-container"></div>
<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=xyz¤cy=USD"></script>
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Call your server to set up the transaction
createOrder: function(data, actions) {
return fetch('/orders', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
return orderData.orderID;
});
},
// Call your server to finalize the transaction
onApprove: function(data, actions) {
return fetch('/orders/' + data.orderID + '/capture', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show a success / thank you message
// Your server defines the structure of 'orderData', which may differ
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
// Recoverable state, see: "Handle Funding Failures"
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
return actions.restart();
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
// Show a failure message
return alert(msg);
}
// Show a success message to the buyer
alert('Transaction completed');
});
}
}).render('#paypal-button-container');
</script>
orders_controller.rb
class OrdersController < ApplicationController
skip_before_action :verify_authenticity_token
def index
end
def create
# Creating Access Token for Sandbox
client_id = 'xyz'
client_secret = 'abc'
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
request.request_body({
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "10.00"
}
}
]
})
begin
# Call API with your client and get a response for your call
# debugger
response = client.execute(request)
puts response.result.id
render json: {success: true, orderID: response.result.id}
rescue PayPalHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
end
def execute_payment
client_id = 'xyz'
client_secret = 'abc'
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
request = PayPalCheckoutSdk::Orders::OrdersCaptureRequest::new(session[:orderID])
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
puts order
rescue PayPalHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
end
end
Now I want to implement the Paypal's Payouts API and I know that paypal-ruby-sdk is available for it but I am confused where to fit this code and how to integrate it with the front end. Any ideas? Thanks in advance :)
Your code above is Checkout code, for both front-end (JavaScript), and back-end (Ruby).
Payouts has nothing to do with Checkout, neither front-end Checkout nor back-end Checkout.
Payouts is strictly a backend API operation, where you send money from your account to another account.
Payouts does not connect to any front-end UI. You can build your own UI to trigger a payout, if you need one. Presumably you know who you want to send money from your account to, and what process should trigger this action.
thank you in advance for the help. I am currently attempting to decode a JWT token on my frontend react app sent from a ruby on rails restful API. I am creating the token on the back end like so:
def create
#user = User.find_by(email: session_params[:email])
if #user && #user.authenticate(session_params[:password])
token = JWT.encode(#user, ENV["JWT_SECRET"])
render json: {
email: #user.email,
name: #user.name,
discipline: #user.discipline,
id: #user.id,
token: token
}
else
render json:{
error: "User not Found",
details: #user.errors.full_messages
}, status: :unauthorized
end
end
I then handle saving it to local storage on the front end, and then attempting to decode the token like this:
export function checkLoginStatus() {
let token = window.localStorage.getItem("ST_APP");
if (token) {
let user = decode(token);
console.log(user);
return user; //returns user object
} else {
return false; //returns false if ST_APP token not found in local storage
}
}```
The problem is that console.log(user) prints #<User:0x00007f3870068ec0> and the attributes of the object are innaccessible. From all the documentation I have been looking at I would expect decode(token) to return a plain old javascript object that I could then access the attributes of. This is my first time working with JWT tokens so any advice is appreciated.
I'm using grails 2.3.7 with SpringSecurityCore 2.0 .. I have two separate signon screens tailored for specific devices with the appropriate one triggered by accessing a specific controller. To do this I customized the loginController ..
/**
* Show the login page.
*/
def auth() {
def config = SpringSecurityUtils.securityConfig
if (springSecurityService.isLoggedIn()) {
redirect uri: config.successHandler.defaultTargetUrl
return
}
String whereFrom = session.SPRING_SECURITY_SAVED_REQUEST.requestURI
def rdt = whereFrom.contains('RDT')
// Redirect for RDT as required ..
String view = rdt ? 'rauth' : 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
session.rdt = rdt
render view: view, model: [postUrl: postUrl,
rememberMeParameter: config.rememberMe.parameter]
}
which seems to work well .. On logout I want again to redirect to an appropriate screen .. I'm trying to use the session attribute I store on login along with a (admittedly old) link I found (http://grails.1312388.n4.nabble.com/Parameter-quot-logoutSuccessUrl-quot-in-spring-security-core-td2264147.html) to redirect back to an appropriate page ..
/**
* Index action. Redirects to the Spring security logout uri.
*/
def index() {
if (!request.post && SpringSecurityUtils.getSecurityConfig().logout.postOnly) {
response.sendError HttpServletResponse.SC_METHOD_NOT_ALLOWED // 405
return
}
// TODO put any pre-logout code here
def rdt = session.rdt
session.rdt = null
// redirect uri: "/j_spring_security_logout?spring-security-redirect=$logoutUrl"
if (rdt) {
def link = g.createLink(controller: "RDT")
def redirectUrl = "${SpringSecurityUtils.securityConfig.logout.filterProcessesUrl}?spring-security-redirect=${link}"
redirectStrategy.sendRedirect request, response, redirectUrl
} else {
redirectStrategy.sendRedirect request, response, SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
response.flushBuffer()
}
Both options return me to the 'default' auth login screen and not my alternate rauth one even with the addition of the extra parameter .. How can I route back to an appropriate screen ?? Thanks
In the end I manually set the session variables to null, invalidate the session and a standard redirect ... works ..
When I redirect to another action in the same controller the 'request' is null.
def updateEmployee() {
println "updateEmployee(): request =" + request.JSON
redirect(action: "createEmployee", params: params)
}
def createEmployee() {
def renderStatus = 500;
System.out.println "createEmployee() : request= " + request.JSON;
the updateEmployee prints all the request data, but creteEmployee prints it as null ([:])
How to redirect the 'request' (I mean the POST data ) ?
You cannot redirect POST request. Redirect mean a new GET request, so all previous data from previous request will be lost.
If you need to call another action without actual redirect use forward: http://grails.org/doc/latest/ref/Controllers/forward.html