create_session doesn't set SET-COOKIE header - session-cookies

i was under the assumption that create_session if the endpoint.ex was configured to use the cookie store, would set the SET-COOKIE response header
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
plug Plug.Session,
log: :debug,
store: :cookie,
key: "some_key",
signing_salt: "some_salt"
this is my authentication controller ( just a part of it)
def callback(%{ assigns: %{ ueberauth_auth: auth } } = conn, params) do
params = build_params(auth)
user = find_or_create_user params
conn = put_session(conn, :current_user, user)
IO.inspect conn.resp_headers
IO.inspect get_session(conn, :current_user)
render conn, "index.html"
#Helpers.redirect!(conn, "/")
end
def build_params(auth) do
%{email: auth.info.email, github_token: auth.credentials.token, github_user: auth.info.nickname}
end
def find_or_create_user(params) do
case DBRepo.get_by(User, email: params.email) do
nil ->
User.changeset(%User{}, params)
|> DBRepo.insert
results ->
results
end
end
IO.inspect conn.resp_headers
returns
[{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "vh8l2deodne1k2iloa4c3e4qdpmh857n"}, {"x-frame-options", "SAMEORIGIN"}, {"x-xss-protection", "1; mode=block"}, {"x-content-type-options", "nosniff"}]
IO.inspect get_session(conn, :current_user)
returns the user as expected

You don't see the session cookie in resp_headers because Plug.Session sets that cookie just before the response is actually sent, using Plug.Conn.register_before_send. If you make a request using any HTTP client (browser, curl, etc), you'll see the Set-Cookie header.
defmodule MyApp.PageController do
use MyApp.Web, :controller
def index(conn, _params) do
conn
|> put_session(:foo, :bar)
|> text("")
end
end
$ curl -I localhost:4000
HTTP/1.1 200 OK
server: Cowboy
date: Mon, 20 Feb 2017 08:57:36 GMT
content-length: 0
set-cookie: _my_app_key=SFMyNTY.g3QAAAABbQAAAANmb29kAANiYXI.F0G6lsgPxsYjq97tonLy1gRkOBUVcfwqKZdozgGRG-c; path=/; HttpOnly
content-type: text/plain; charset=utf-8
cache-control: max-age=0, private, must-revalidate
x-request-id: uoplksup9ndakf5sdr5shpjsjhvu849v
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff

Related

Rails controllers not recieving POST parameters sent from Next.js

I am sending the request to register a user from my Next.js frontend like this:
const res = await fetch("http://localhost:3000/create", {
method: "POST",
headers: {
"Contet-Type": "application/json",
},
body: JSON.stringify({ user: {values}),
});
const data = await res.json();
where the values are username, email, pw, pw confirmation...the usual.
Here are my headers from the request, which confirm that the payload is being sent as expected:
General:
Request URL: http://localhost:3000/create
Request Method: POST
Status Code: 204 No Content
Remote Address: 127.0.0.1:3000
Referrer Policy: strict-origin-when-cross-origin
Response headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin: http://localhost:3001
Access-Control-Max-Age: 1728000
Cache-Control: no-cache
Vary: Origin
X-Request-Id: a044ad4b-dbb3-41ed-80bd-ca933871b924
X-Runtime: 115.547357
Request headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en,nl;q=0.9,hr;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: keep-alive
Content-Length: 27
Content-Type: text/plain;charset=UTF-8
Contet-Type: application/json
Host: localhost:3000
Origin: http://localhost:3001
Referer: http://localhost:3001/
sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Request payload:
{user:{email:"ggggg#gmail.com", username:"myusername", password:"123123",password_confirmation:"123123"}}
But what happens is the parameters from the frontend request never show up in my controllers.
So naturally, my first step was to byebug my rails controller in order to figure out what is going on, and when I do that all I see from params is:
<ActionController::Parameters {"controller"=>"user", "action"=>"create"} permitted: false>
My user params are nowhere to be seen. What is going on here?
I even started another rails project through a template:
https://github.com/rchuasing/rails-api-boilerplate
and the same thing keeps happening again, I am recieving no parameters in any controller.
Here is my controller that I've stripped down to bare minimum in order to just test the params:
class UserController < ApplicationController
def create
# this is where I look for params
byebug
end
def user_params
params.require(:user).permit(:email, :username, :password,
:password_confirmation)
end
end
Model:
class User < ApplicationRecord
include Devise::JWT::RevocationStrategies::JTIMatcher
devise :database_authenticatable, :registerable, :jwt_authenticatable,
jwt_revocation_strategy: self
end
I have tried everything I could think of, that includes making a new app and trying all kinds of various headers, but the params never get to the backend.

Axios not sending headers to rails controllers

I am on Rails 5.1.7 with a react frontend with react-rails gem
My problem looks like what is explained here just that my react components are server rendered so I can not access data from the client from the component.
So I have created the following file:
import axios from 'axios'
export default function() {
const csrfToken = document.querySelector("meta[name=csrf-token]").content;
axios.defaults.headers.common['X-CSRF-Token'] = csrfTokens;
}
and I import in in application.js as:
import setupCSRFToken from '../setupCSRFToken'
and then calling the exported function.
The problem is that checking the headers the controller does not receive anything:
(byebug) request.headers['X-CSRF-Token']
nil
so the problem must be related with how axios is configurated. I did install it with:
yarn add axios
in the developer console I get:
xhr.js:179 POST http://localhost:3000/es/roomlist_reorder 422 (Unprocessable Entity)
dispatchXhrRequest # xhr.js:179
xhrAdapter # xhr.js:20
dispatchRequest # dispatchRequest.js:40
Promise.then (async)
request # Axios.js:64
Axios.<computed> # Axios.js:88
wrap # bind.js:11
RoomList._this.getOrder # roomlist.jsx:33
RoomList._this.filterRooms # roomlist.jsx:47
RoomList._this.handleOrderBy # roomlist.jsx:51
callCallback # react-dom.development.js:191
invokeGuardedCallbackDev # react-dom.development.js:240
invokeGuardedCallback # react-dom.development.js:293
invokeGuardedCallbackAndCatchFirstError # react-dom.development.js:308
executeDispatch # react-dom.development.js:393
executeDispatchesInOrder # react-dom.development.js:418
executeDispatchesAndRelease # react-dom.development.js:3303
executeDispatchesAndReleaseTopLevel # react-dom.development.js:3312
forEachAccumulated # react-dom.development.js:3284
runEventsInBatch # react-dom.development.js:3329
runExtractedPluginEventsInBatch # react-dom.development.js:3539
handleTopLevel # react-dom.development.js:3583
batchedEventUpdates$1 # react-dom.development.js:21731
batchedEventUpdates # react-dom.development.js:800
dispatchEventForLegacyPluginEventSystem # react-dom.development.js:3593
attemptToDispatchEvent # react-dom.development.js:4313
dispatchEvent # react-dom.development.js:4234
unstable_runWithPriority # scheduler.development.js:661
runWithPriority$1 # react-dom.development.js:11079
discreteUpdates$1 # react-dom.development.js:21748
discreteUpdates # react-dom.development.js:813
dispatchDiscreteEvent # react-dom.development.js:4213
roomlist.jsx:40 Error: Request failed with status code 422
at createError (createError.js:17)
at settle (settle.js:19)
at XMLHttpRequest.handleLoad (xhr.js:69)
headers:
Request URL: http://localhost:3000/es/roomlist_reorder
Request Method: POST
Status Code: 422 Unprocessable Entity
Remote Address: [::1]:3000
Referrer Policy: strict-origin-when-cross-origin
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
X-Request-Id: 2de02308-5eff-4e68-8a07-f39dfdf
X-Runtime: 0.145234
X-Web-Console-Mount-Point: /__web_console
X-Web-Console-Session-Id: 57baf1f81457b91c49fa8
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 24
Content-Type: application/json;charset=UTF-8
....
request headers:
:authority: m.stripe.com
:method: POST
:path: /6
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
content-length: 656
content-type: text/plain;charset=UTF-8
cookie: private_machine_identifier=tKQmcfYNS9%BJ86cfKvXcvBodWTYUWwUkE%3D; m=306ba65-c03-4b-8257-2b6d9bde3d44;
session=sess_I4LBSZAr2S0DM8i
origin: https://m.stripe.network
referer: https://m.stripe.network/
sec-ch-ua: "\\Not;A\"Brand";v="99", "Google Chrome";v="85", "Chromium";v="85"
sec-ch-ua-mobile: ?0
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site

Ruby Rest-client keeps correcting the uri

I'm using rest-client to make a post request to a certain address. Unfortunately rest-client keeps correcting the web adres from 'https' to 'http'. Which then gives back an error.
I already looked at the documentation whether this maybe was a setting for the gem. Anyone knows how I can 'force' rest-client to access the desired address.
Thx in advance!
used code is:
uri = 'https://sandbox.api.online.unit4.nl/V19/OAuth/Token'
payload = {
code: params[:code],
client_id: '#{#client_id}',
client_secret: '#{#client_secret}',
redirect_uri: '#{#client_redirect_url}',
grant_type: 'authorization_code'
}
response = RestClient::Request.execute(method: :post, url: uri, headers: {params: payload})
curl:
> GET /V19/OAuth/Token HTTP/1.1
> Host: sandbox.api.online.unit4.nl
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< content-length: 27
< content-type: application/json; charset=utf-8
< date: Wed, 06 Dec 2017 15:21:44 GMT
< p3p: CP="NON CUR OTPi OUR NOR UNI"
< server: Microsoft-IIS/7.5
< x-aspnetmvc-version: 5.2
< cache-control: no-cache, no-store, max-age=0, must-revalidate
< access-control-allow-origin: *
< x-powered-by: ASP.NET
< x-aspnet-version: 4.0.30319
< pragma: no-cache
< Set-Cookie: PD_STATEFUL_88eb7504-dfe9-11e2-8624-005056af4a32=sandbox.api.online.unit4.nl; Path=/
< Set-Cookie: LB_online_unit4=1493375404.20480.0000; path=/
Could be that your variables aren't getting interpolated. With single quotes '#{#variable}' doesn't work. Try switching the single quotes to double quotes ('' => ""). What is happening is that instead of sending the actual client id and secret, you are sending the string #{#client_id}.
Try
uri = 'https://sandbox.api.online.unit4.nl/V19/OAuth/Token'
payload = {
code: params[:code],
client_id: #client_id,
client_secret: #client_secret,
redirect_uri: #client_redirect_url,
grant_type: 'authorization_code'
}
response = RestClient::Request.execute(method: :post, url: uri, headers: {params: payload})

Why am I unable to sign out using devise_token_auth and curl?

I'm using rails-api with devise_token_auth for authentication. I am able to sign in without issue via:
curl -i http://localhost:3000/api/v1/auth/sign_in -F email="user#nowhere.org" -F password="password"
However, sign out fails with a 404 and the error "User was not found or was not logged in." via:
curl -X DELETE -i http://localhost:3000/api/v1/auth/sign_out
I have tried variations of this command with multiple combinations of parameters and header values, such as:
curl -X DELETE -i http://localhost:3000/api/v1/auth/sign_out -F email="user#nowhere.org" -H Access-Token="Ae1yaTYLkSAgdhz3LtPAZg" -H Client="9AmYF6NS8tP6EOD5nPSuxw" -H Expiry="1443073493" -H Uid="user#nowhere.org" -H Token-Type="Bearer"
To no avail. A similarly constructed RSpec test also fails with the same response and error, and the logs indicate the request was processed via DeviseTokenAuth::SessionsController#destroy as JSON.
Of course, I'm not actually using curl for authentication; just verification of the request structure before writing the relevant code.
Answering my own question, I wasn't returning certain sign_in response header values correctly with the sign_out request.
I found a related post which pointed out some of the critical headers. The trick is to capture the access-token, client, and uid header values from the sign_in response, then include them as parameters in the sign_out request:
curl -i -X DELETE http://localhost:3000/api/v1/auth/sign_out -F access-token="Ae1yaTYLkSAgdhz3LtPAZg" -F client="9AmYF6NS8tP6EOD5nPSuxw" -F uid="user#nowhere.org"
Here's an RSpec test to illustrate and verify:
require 'rails_helper'
RSpec.describe "Authentication", type: :request do
it "logs a user out" do
user = User.create!(
name: "Some Dude",
email: "user#nowhere.org",
password: "password",
confirmed_at: Date.today
)
# initial sign in to generate a token and response
post api_v1_user_session_path, {
email: user.email,
password: user.password
}
expect(user.reload.tokens.count).to eq 1
# sign out request using header values from sign in response
delete destroy_api_v1_user_session_path, {
"access-token": response.header["access-token"],
client: response.header["client"],
uid: response.header["uid"]
}
response_body = JSON.load(response.body)
expect(response_body["errors"]).to be_blank
expect(response.status).to eq 200
# user token should be deleted following sign out
expect(user.reload.tokens.count).to eq 0
end
end
In details, do the following steps:
Step 1:
curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST http://localhost:3000/api/v1/auth/sign_in -d "{\"email\":\"user#example.com\",\"password\":\"password\"}"
You'll get a response like this:
* Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> POST /api/v1/auth/sign_in HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.43.0
> Content-Type: application/json
> Accept: application/json
> Content-Length: 50
>
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 200 OK
< X-Frame-Options: SAMEORIGIN
< X-Xss-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< Access-Token: BqXcWQi0-9faLyxP1LnUKw
< Token-Type: Bearer
< Client: dYSqVgM9VT6fV9Y5MFWpJQ
< Expiry: 1465679853
< Uid: user#example.com
< Content-Type: application/json; charset=utf-8
< Etag: W/"9ad6a23f014a744a7ec83b4e0e9d27aa"
< Cache-Control: max-age=0, private, must-revalidate
< X-Request-Id: 6566bd38-1ad7-491a-a1ab-e41458b9b704
< X-Runtime: 0.184807
< Server: WEBrick/1.3.1 (Ruby/2.3.0/2015-12-25)
< Date: Sat, 28 May 2016 21:17:33 GMT
< Content-Length: 135
< Connection: Keep-Alive
<
* Connection #0 to host localhost left intact
{"data":{"id":6,"provider":"email","uid":"user#example.com","name":"testuser","nickname":null,"image":null,"email":"user#example.com"}}%
Step 2:
Now, you want to sign the user out.
curl -i -X DELETE http://localhost:3000/api/v1/auth/sign_out -F access-token="BqXcWQi0-9faLyxP1LnUKw" -F client="dYSqVgM9VT6fV9Y5MFWpJQ" -F uid="user#example.com"
You'll get a response like this:
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: W/"7363e85fe9edee6f053a4b319588c086"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 8f7a297a-6a72-4c9d-a210-48c29fb4bfe0
X-Runtime: 0.095060
Server: WEBrick/1.3.1 (Ruby/2.3.0/2015-12-25)
Date: Sat, 28 May 2016 21:19:18 GMT
Content-Length: 16
Connection: Keep-Alive
{"success":true}%
In step 2, I've added the access-token and client as per the response that we received in step 1 (Access-Token: BqXcWQi0-9faLyxP1LnUKw and Client: dYSqVgM9VT6fV9Y5MFWpJQ).
That's it! :)
I took the help of #user3006381's answer! All credits to him.

can HTTP multipart/mixed xml part be converted to a Hash when mixed with other parts

The question is just above the last code snippet. Thank you.
(environment details are the end )
posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.new(params[:post])
respond_to do |format|
format.xml { render :xml => #post.to_xml(:include => [ :assets])}
end
end
posts.rb
class Post < ActiveRecord::Base
has_many :assets, :as => :attachable, :dependent => :destroy
end
asset.rb
class Asset < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
has_attached_file :data,
:url => "/assets/:id",
:path =>":rails_root/assets/:id_partition/:style/:basename.:extension"
def name
data_file_name
end
def content_type
data_content_type
end
def file_size
data_file_size
end
end
now when we post this information
POST /posts.xml HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: application/xml
Content-Type: application/xml
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8080
Content-Length: 60
<post><body>postbody</body><title>post_title</title></post>
a post entry gets created
and when I post this
POST /posts.xml HTTP/1.1
Content-type: multipart/mixed; boundary=---------------------------7d226f700d0
Accept: application/xml,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_21
Host: 192.168.0.105:8080
Connection: keep-alive
Content-Length: 1710
-----------------------------7d226f700d0
content-disposition: form-data; name="post[title]"
Content-Length: 10
post_title
-----------------------------7d226f700d0
content-disposition: form-data; name="post[body]"
Content-Length: 8
postbody
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/files/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/Pictures/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0
a new post gets created with 2 file attachments.
now the question is, I want to get the following HTTP post ( please notice the xml part before the file attachments) to also create a post with 2 attachments, with no additional changes ( to posts_controller or routes.rb). is that possible?
POST /posts.xml HTTP/1.1
Content-type: multipart/mixed; boundary=---------------------------7d226f700d0
Accept: application/xml,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_21
Host: 192.168.0.105:8080
Connection: keep-alive
Content-Length: 1710
-----------------------------7d226f700d0
Content-type: application/xml; charset=UTF-8
Content-Length: 59
<post><body>postbody</body><title>post_title</title></post>
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/files/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0
content-disposition: form-data; name="post[assets_attributes][0][data]"; filename="C:/Users/mv288/Pictures/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------7d226f700d0Blockquotetest
using jruby 1.5.2/jdk1.6, rails 2.3.4, paperclip-2.3.3
on windows 2007 - 64 bit
At least rails 2.3.4 does not do this automatically. One needs to write a multipart/related parser and register it, inside initializers/mime_types.rb
NOTE : Feel free to update the hard coded values( like main and attachment part prefix etc.,) in your copy.
This same strategy can be used for multipart/related content as well. We are still debating whether to use multipart/related or multipart/mixed in this example below.
Microsoft's notes on this subject.
http://msdn.microsoft.com/en-us/library/ms527355(EXCHG.10).aspx
Mime::Type.register "multipart/mixed", :mixed
class ActionController::Request
def initialize(env)
Thread.current[:request]=self
super
end
end
class MultiPartParamsParser
def main_part_name
"main"
end
def attachment_part_prefix
"my_company_attachment"
end
def content_type(main_part)
# TODO ----
:xml_simple
end
def content(main_part)
# TODO implement this
if main_part.is_a?(String)
main_part.gsub!("Content-Type: application/xml",'') # remove content type if it exists
main_part.strip! # to remove any trailing or leading whitespaces
else
main_part[:tempfile].read
end
end
def request
Thread.current[:request]
end
def parse_formatted_parameters(data)
multi_parts = Rack::Utils::Multipart.parse_multipart(request.try(:env))
main_part = multi_parts[main_part_name]
data = content(main_part)
# TODO return an error if data is not found
params = case content_type(main_part)
when :xml_simple, :xml_node
data.blank? ? {} : Hash.from_xml(data).with_indifferent_access
when :yaml
YAML.load(data)
when :json
if data.blank?
{}
else
ret = ActiveSupport::JSON.decode(data)
ret = {:_json => data} unless data.is_a?(Hash)
ret.with_indifferent_access
end
else
{}
end
process_attachments(params, multi_parts)
params
end
def process_attachments(data, multi_parts)
data.each do |key, value|
value ||= key # when array value is nil
if value.is_a?(Hash) or value.is_a?(Array)
process_attachments(value, multi_parts)
elsif value.respond_to?(:match) and value.match("^#{attachment_part_prefix}") and (attachment=multi_parts[value]) # there could Time,Numbers etc.., but we match only string.
data[key] = create_uploaded_file(attachment) # TODO handle the scenarios for short strings
end
end
end
def create_uploaded_file (attachment)
upload = attachment[:tempfile]
upload.extend(ActionController::UploadedFile)
upload.original_path = attachment[:filename]
upload.content_type = attachment[:type]
upload
end
end
proc = Proc.new do |data|
MultiPartParamsParser.new.parse_formatted_parameters(data)
end
ActionController::Base.param_parsers[Mime::Type.lookup('multipart/mixed')] = proc
then, you can post your message like this. Nesting gets automatically taken with no further changes to the models or controllers.
POST /posts.xml HTTP/1.1
Content-type: multipart/mixed; boundary=---------------------------###987612345###
Accept: application/xml,text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Cache-Control: no-cache
Pragma: no-cache
Connection: keep-alive
Content-Length: ##
-----------------------------###987612345###
content-disposition: name="main"
Content-Length: ##
<post><title>post_title</title><body>post_body</body>
<assets_attributes type="array">
<asset><data>my_company_attachment_0</data> </asset>
<asset><data>my_company_attachment_1</data> </asset>
</assets_attributes>
</post>
-----------------------------###987612345###
content-disposition: name="my_company_attachment_0"; filename="C:/Users/mv288/files/1.txt"
content-type: application/octet-stream
ÿþ
sample file content
-----------------------------###987612345###
content-disposition: name="my_company_attachment_1"; filename="C:/Users/mv288/Pictures/1.png"
content-type: image/png
ÿþ
sample file content
-----------------------------###987612345###

Resources