422 error, devise/backbone - backbone.js

I'm trying to set up a Rails app that uses Backbone with Devise for registration.
The response text in the error callback in the Chrome console says
responseText: "{"errors":{"email":["can't be blank"],"password":["can't be blank"]}}"
However, the log in the server says unprocessable entity
Parameters: {"email"=>"pp#rr.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "registration"=>{"email"=>"pp#rr.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
(0.1ms) begin transaction
(0.1ms) rollback transaction
Completed 422 Unprocessable Entity in 4ms (Views: 0.3ms | ActiveRecord: 0.1ms)
I have a Backbone user model that sets the url for the save
UserRegistration = Backbone.Model.extend({
url: '/users.json',
paramRoot: 'user',
defaults: {
"email": "",
"password": "",
"password_confirmation": ""
}
});
In the associated view, I get the attributes from the registration form, set them in the model, and then save the model
var email = $('#email').val();
var password_confirmation = $('#password_confirmation').val();
var password = $('#password').val();
this.model.set({email : email, password_confirmation: password_confirmation, password: password})
this.model.save(this.model.attributes, {
success: function(userSession, response) {
console.log("success");
console.log(userSession);
console.log(response);
console.log(this.model.url);
},
error: function(userSession, response) {
console.log("error");
console.log(userSession);
console.log(response);
}
});
}
After setting the model attributes (before saving) i did a console.log(this.model.attributes), and they are set
Object {email: "oo#gmail.com", password: "snowy", password_confirmation: "snowy"}
My User model looks like this
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
end
Can anyone make any suggestions?
There were some recent issues with a recent Devise release only responding to html, so I installed Devise 2.1.2 to make it respond with json to make it compatible with Backbone. That is not the issue here.

paramRoot isn't part of Backbone core. In order to fix this problem, I had to include the sync library https://raw.github.com/codebrew/backbone-rails/master/vendor/assets/javascripts/backbone_rails_sync.js from the Backbone-Rails gem to make user part of the param root
url: '/users.json',
paramRoot: 'user',

Related

Dotnet API requires auth both for application and React

I must be really stupid, But I have been struggling for weeks to try solve this issue, and all the digging I have done (in Stack overflow and MS Documentation) has yielded no results (or I'm too stupid to implement auth correctly)
I have a dotnet service which needs to act as an API - both for an application to post data to (an exe which logs exception data), and for a UI (react app) to get the posted exceptions
the exe can successfully send data to the dotnet app after first getting a token from login.microsoftonline.com and then sending the token (and secret) in the http request.
A sample postman pre-request script of the auth used (I've set all the secret stuff as environment variables):
pm.sendRequest({
url: 'https://login.microsoftonline.com/' + pm.environment.get("tenantId") + '/oauth2/v2.0/token',
method: 'POST',
header: 'Content-Type: application/x-www-form-urlencoded',
body: {
mode: 'urlencoded',
urlencoded: [
{key: "grant_type", value: "client_credentials", disabled: false},
{key: "client_id", value: pm.environment.get("clientId"), disabled: false},
{key: "client_secret", value: pm.environment.get("clientSecret"), disabled: false}, //if I don't configure a secret, and omit this, the requests fail (Azure Integration Assistant recommends that you do not configure credentials/secrets, but does not provide clear documentation as to why, or how to use a daemon api without it)
{key: "scope", value: pm.environment.get("scope"), disabled: false}
]
}
}, function (err, res) {
const token = 'Bearer ' + res.json().access_token;
pm.request.headers.add(token, "Authorization");
});
Now in React, I am using MSAL(#azure/msal-browser) in order to login a user, get their token, and pass the token to one of the dotnet endpoints using axios as my http wrapper, but no matter what I do, it returns http status 401 with WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid".
A simplified code flow to login user and request data from the API:
import {publicClientApplication} from "../../components/Auth/Microsoft";//a preconfigured instance of PublicClientApplication from #azure/msal-browser
const data = await publicClientApplication.loginPopup();
// ... some data validation
publicClientApplication.setActiveAccount(data.account);
// .. some time and other processes may happen here so we don't access token directly from loginPopup()
const activeAccout = publicClientApplication.getActiveAccount();
const token = publicClientApplication.acquireTokenSilent(activeAccount).accessToken;
const endpointData = await api()/*an instance of Axios.create() with some pre-configuration*/.get(
'/endpoint',
{ headers: {'Authorization': `bearer ${token}`} }); // returns status 401
The dotnet service has the following configurations
public void ConfigureServices(IServiceCollection services){
...
var authScheme = services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme);
authScheme.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
...
}
namespace Controllers{
public class EndpointController : ControllerBase{
...
[Authorize]
[HttpGet]
public IActionResult GetEndpoint(){
return Ok("you finally got through");
}
}
}
I've literally tried so many things that I've lost track of what I've done...
I've even cried myself to sleep over this - but that yielded no results
i can confirm that running the request in postman, with the pre request script, it is possible to get the response from the endpoint
So....
After much digging and A-B Testing I was able to solve this issue.
I discovered that I was not sending the API scope to the OAuth token endpoint. To do this I needed to change the input for acquireTokenSilent.
The updated code flow to login user and request data from the API:
import {publicClientApplication} from "../../components/Auth/Microsoft";//a preconfigured instance of PublicClientApplication from #azure/msal-browser
const data = await publicClientApplication.loginPopup();
// ... some data validation
publicClientApplication.setActiveAccount(data.account);
// .. some time and other processes may happen here so we don't access token directly from loginPopup()
const activeAccout = publicClientApplication.getActiveAccount();
const token = publicClientApplication.acquireTokenSilent({scopes:["api://XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/.default"],account:activeAccount}).accessToken;//here scopes is an array of strings, Where I used the api URI , but you could directly use a scope name like User.Read if you had it configured
const endpointData = await api()/*an instance of Axios.create() with some pre-configuration*/.get(
'/endpoint',
{ headers: {'Authorization': `bearer ${token}`} }); // returns status 401

How to use Active Storage to add profile picture for User

Recently, I tried using active storage in Ruby on Rails to store profile picture for logged in user. I been following this documentation closely but, it didn't work out https://edgeguides.rubyonrails.org/active_storage_overview.html.
What I want is to allow users to register with only first name, last name, username, password, and email. Then, users can login and they can upload their own profile picture from profile page. After uploading, I want users to see their avatar in their profile.
My User.rb Model is looks like this:
class User < ApplicationRecord
has_secure_password
has_many :games, dependent: :destroy
validates :first_name, presence: true
validates :last_name, presence: true
validates :email, presence: true
validates :password, presence: true
has_one_attached :avatar
end
I added "has_one_attached :avatar" like from the rails documentation and I'm using "set_avatar" method to attach avatar to an existing user.
My users_controller is looks like this:
class UsersController < ApplicationController
skip_before_action :authorized
wrap_parameters format: []
def index
users = User.all
render json: users
end
def find_user
user = User.find_by(id: params[:id])
if user
render json: user, status: :not_found
else
render json: { error: "Not found" }, status: :not_found
end
end
def show
user = User.find_by(id: session[:user_id])
if user
render json: user
else
render json: { error: "Not authorized" }, status: :unauthorized
end
end
def create
user = User.create(user_params)
if user.valid?
session[:user_id] ||= user.id
render json: user, status: :created
else
render json: {error: user.errors.full_messages }, status: :unprocessable_entity
end
end
def update
currentUser = User.find_by(id: params[:id])
if currentUser
currentUser.update(user_update_params)
render json: currentUser, status: :accepted
else
render json: {error: currentUser.errors.full_messages }, status: :unprocessable_entity
end
end
def destroy
user = User.find_by(id: params[:id])
if user
user.destroy
head :no_content
else
render json: {error: "user not found"}, status: :not_found
end
end
def set_avatar
user = User.find_by(id: params[:id])
if user
user.avatar.attach(params[:avatar])
else
render json: {error: "Profile image upload failed"}
end
end
private
def user_params
params.permit(:first_name, :last_name, :email, :username, :password, :avatar)
end
def user_update_params
params.permit(:first_name, :last_name, :email, :username, :password)
end
end
My routes:
Rails.application.routes.draw do
# resources :game_memos
# resources :memos
# resources :games
# resources :users
resources :sessions
#--------------USER------------------------------
# Create Session
get '/login', to: "sessions#create"
# Show all users
get '/users', to: "users#index"
# Register new user
post '/users', to: "users#create"
# Login User
post "/login", to: "sessions#create"
# Logout user
delete '/logout', to: "sessions#destroy"
# Update User profile
patch '/users/:id', to: "users#update"
# Keep user logged in
get '/me', to: "users#show"
# Get request for find user based on User id
get '/users/:id', to: "users#find_user"
# Save avatar
post '/users/:id', to: "users#set_avatar"
# -------------GAMES Routes----------------------
get '/games', to: 'games#index'
# Show all the games that belongs to logged in user
get '/users/:id/games', to: "games#show"
# Create a new wishlist
post '/games', to: "games#create"
# Delete a game from wishlist
delete '/users/:id/games/:id', to: "games#destroy"
#-------------Memo Routes------------------------
# Show memos that belongs to a game
get '/games/:id/memos', to: "memos#show"
# Post memos to a game
post '/games/:id/memos', to: "memos#create"
end
I'm uploading my avatar from this component:
import React, { useState } from "react";
// I'm getting default image from this location just for now
import IMAGES from '../images/Image';
export default function ProfilePicture({currentUser}){
const[profileAvatar, setAvatar] = useState([])
const fileTypes = [
"image/apng",
"image/bmp",
"image/gif",
"image/jpeg",
"image/pjpeg",
"image/png",
"image/svg+xml",
"image/tiff",
"image/webp",
"image/x-icon"
];
function handleSubmit(e){
// fetch(`/users/${currentUser.id}` ,{
// method: "POST",
// headers: {"Content-Type": "application/json"},
// body: JSON.stringify({avatar:profileAvatar})
// })
// .then((r) => r.json())
// .then(data => {
// if (data.errors) {
// alert(data.errors)
// }
// else {
// setAvatar(data)
// }
// })
}
return(
<div>
<img src={IMAGES.defaultProfile} alt="default_profile_image" className="profile_avatar"/>
<form onSubmit={handleSubmit}>
<input type="file" id="avatar" name="avatar" accept={fileTypes} onChange={(e)=>setAvatar(e.target.value)}/>
<button type="submit">Submit</button>
</form>
</div>
)
}
Lastly, my Users migration file:
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.string :username
t.string :password_digest
t.string :avatar
t.timestamps
end
end
end
I wasn't sure where to ask this kind of question. I never used Active Storage before and this is my first time using it. Please help.
Firstly you don't need t.string :avatar. ActiveStorage attachments are not columns of owner table but records in associated tables
ActiveStorage provides Direct Upload if you use JS frameworks like React
From docs:
If you want to use the Direct Upload feature from a JavaScript framework, or you want to integrate custom drag and drop solutions, you can use the DirectUpload class for this purpose. Upon receiving a file from your library of choice, instantiate a DirectUpload and call its create method. Create takes a callback to invoke when the upload completes.
import { DirectUpload } from "#rails/activestorage"
const input = document.querySelector('input[type=file]')
// Bind to file drop - use the ondrop on a parent element or use a
// library like Dropzone
const onDrop = (event) => {
event.preventDefault()
const files = event.dataTransfer.files;
Array.from(files).forEach(file => uploadFile(file))
}
// Bind to normal file selection
input.addEventListener('change', (event) => {
Array.from(input.files).forEach(file => uploadFile(file))
// you might clear the selected files from the input
input.value = null
})
const uploadFile = (file) => {
// your form needs the file_field direct_upload: true, which
// provides data-direct-upload-url
const url = input.dataset.directUploadUrl
const upload = new DirectUpload(file, url)
upload.create((error, blob) => {
if (error) {
// Handle the error
} else {
// Add an appropriately-named hidden input to the form with a
// value of blob.signed_id so that the blob ids will be
// transmitted in the normal upload flow
const hiddenField = document.createElement('input')
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("value", blob.signed_id);
hiddenField.name = input.name
document.querySelector('form').appendChild(hiddenField)
}
})
}
Pay attention to const url = input.dataset.directUploadUrl. You need to set data-direct-upload-url attribute as your upload url, by default it is /rails/active_storage/direct_uploads
hiddenField.setAttribute("value", blob.signed_id) from this example is a Rails magic
When the file is uploaded, Rails return blob with signed_id to frontend. Using it, you can attach file to the record
In your case
fetch(`/users/${currentUser.id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({avatar: blob.signed_id}) // blob.signed_id we get after file uploading
})
On backend
def set_avatar
user = User.find(params[:id])
user.update(avatar: params[:avatar])
render json: { avatar: rails_blob_path(user.avatar) }
end
So pass blob.signed_id and avatar will be attached automatically
Probably this tutorial will be helpful for you
Like Mechnicov's answer you don't need t.string :avatar
in the schema the
has_one_attached :avatar is already taking care of that.
If you want to attach the photo through the backend and not direct upload try using this
user.avatar.attach(io: file, filename: 'photo.png', content_type: 'image/png')
and make sure that the client is sending the photo through formdata and not through the body
Step 1: Create a FormData instance
let formData = new FormData();
Step 2: Append the data in it
formData.append('file_to_upload', fileRef.files[0]); // fileRef is the input file reference

How to write nested axios post request in rails project?

I have been trying to convert the views of a project written initially in Ruby on Rails and I have pretty much succeeded in it. The next step is to write React APIs that will request and send data to the rails models. Here is where I am stuck.
So Initially I had to write the APIs for user registration, login and logout which I have succeeded in but now I need to write APIs for the nested resources and I am not able to solve the issue.
So the scenario in the application is that each user can have many projects and each project will have one project manager (from user model) and can have multiple developers and QAs (also from user table). The project manager of each project is being managed through a foreign key named "manager_id" in the projects table while the developers and QAs of the projects are being managed through a has_and_belongs_to_many association and hence through a join table as well.
My model of user and project is following
Project.rb:
class Project < ApplicationRecord
belongs_to :project_manager, class_name: 'User', foreign_key: :manager_id
has_and_belongs_to_many :users
end
User.rb:
class User < ApplicationRecord
has_many :projects_as_project_manager, class_name: 'Project', foreign_key: :manager_id
has_and_belongs_to_many :projects
end
After running through the rake routes command I get to see that the URI for projects#create is /users/:user_id/projects(.:format)
For project creation, I have created a form and on submit I am sending the post request in the following way
const saveProject = (e) => {
const API_URL = "http://localhost:3000/users/" + user_details.id + "/projects";
e.preventDefault();
const temp_project = {title: title, deadline: deadline, status: status, manager_id: user_details.id};
axios.post(API_URL, temp_project).then((promise) => {
console.log("Response in promise is: ", promise);
}).catch((error) => {
console.log("error in catch block is: ", error);
})
}
For clarification purposes let me state that project users(developers and qas) can be null as well so I am just sending manager id in addition to the project form field.
But I am getting the following error. Kindly help me out here
The code above is a javascript code. You should render Ruby variables in erb in order to view the ruby variables. for instance <%= user_details.id %> Check the code beneath.
If the source of the javascript code is in a js.erb file then you should make the variables ~> instance variables. i.e #user_details so that you can access them in the js file like this: <%= #user_details.id %>.
const saveProject = (e) => {
const API_URL = "http://localhost:3000/users/" + <%= user_details.id %> + "/projects";
e.preventDefault();
const temp_project = {title: <%= title %>, deadline: <%= deadline %>, status: <%= status %>, manager_id: <%= user_details.id %>};
axios.post(API_URL, temp_project).then((promise) => {
console.log("Response in promise is: ", promise);
}).catch((error) => {
console.log("error in catch block is: ", error);
})
}
Finally I was able to find out the mistake which infact was a really stupid one. I had not generated the controller for projects model and that's why I was getting the bad request error as the request being generated from axios was not being submitted anywhere.

How do i hookup a React button to authenticate with Devise Token Auth + OmniAuth

I'm having trouble connecting my Devise Token Auth with a token I get back from google in react.
I'm using this package for the button:
https://www.npmjs.com/package/react-google-login
This is the auth I'm trying to set up:
https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/omniauth.md
I'm getting a response from google with the react button but I have no idea how that information has to translate to go back to the devise auth.
Information online is severely lacking between these 2 technologies. What it comes down to is how to translate this ruby tag into react:
<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
I know this is old but here are my 2 cents.
I have used this gem OmniAuth Google OAuth2. The information is pretty clear. In my project, I manage my token using JWT while still storing the access and refresh tokens from Google.
Backend
# config/initializers/devise.rb
config.omniauth :google_oauth2,
ENV['GOOGLE_CLIENT_ID'],
ENV['GOOGLE_CLIENT_SECRET'],
{ scope: "userinfo.email, userinfo.profile",
prompt: 'select_account',
image_aspect_ratio: 'square',
provider_ignores_state: true,
}
# controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
#user = User.from_omniauth(request.env['omniauth.auth'])
if #user.persisted?
# My personal token
token = issue_token(#user)
render json: { success: true,
user: #user,
token: token,
google_token: #user.access_token,
message: "Logged in successfully." }
else
render json: { success: false,
error: #user.errors.full_messages.join("\n"),
message: 'Google sign in unsuccessful.' }
end
end
def failure
set_flash_message! :alert, :failure, kind: OmniAuth::Utils.camelize(failed_strategy.name), reason: failure_message
render json: { success: false, message: 'Google authentication failed.', reason: failure_message, kind: OmniAuth::Utils.camelize(failed_strategy.name) }
end
private
end
# In User.rb
def self.from_omniauth(auth)
user = User.where(email: auth.info.email).first
if user
user = User.update(id: user.id,
refresh_token: auth.credentials.refresh_token,
access_token: auth.credentials.token,
uid: auth.uid,
)
else
# Create a username from names, append incremental if username already exists
username ||= auth.info.first_name + auth.info.last_name
username = username.delete('^a-zA-Z0-9_').downcase
num = 1
until User.find_by(username: username).nil?
username = "#{username}#{num}"
num += 1
end
user = User.create(email: auth.info.email,
uid: auth.uid,
refresh_token: auth.credentials.refresh_token,
access_token: auth.credentials.token,
provider: auth.provider,
password: Devise.friendly_token[0, 20],
firstname: auth.info.first_name,
lastname: auth.info.last_name,
username: username,
)
end
user
end
# routes.rb
# User Routes: Devise
devise_for :users,
path_names: {
sign_in: 'login',
sign_out: 'logout',
# sign_up: 'register'
},
controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations',
omniauth_callbacks: 'users/omniauth_callbacks'
}
Above routes translations
user_google_oauth2_omniauth_authorize_path GET|POST /api/users/auth/google_oauth2(.:format)
users/omniauth_callbacks#passthru
user_google_oauth2_omniauth_callback_path GET|POST /api/users/auth/google_oauth2/callback(.:format)
users/omniauth_callbacks#google_oauth2
Here is the front end
<!-- index.html -->
<head>
<script src="https://apis.google.com/js/platform.js?onload=init" async defer></script>
</head>
Do not worry about defining the gapi function, it is loaded script in the above
// RegisterContent.js
const RegisterContent = function RegisterContent() {
function handleResponse(response) {
// Save user to redux store and all the tokens to cookies
}
// callback
function signInCallback(authResult) {
if (authResult.code) {
const params = { code: authResult.code }
const path = "localhost:3000/api/users/auth/google_oauth2/callback";
// This just handdles posting with axios
postResource(path, params, handleResponse);
}
}
// This will prompt opening the google window and returns a callback upon success
const googleHandler = () => {
googleAuth.grantOfflineAccess().then(signInCallback);
};
useEffect(() => {
// Initialize the GoogleAuth object
gapi.load("auth2", function foo() {
const auth = gapi.auth2.init({
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
scope: "email profile",
});
setGoogleAuth(auth);
console.log("Init");
});
}, []);
return (
<Button onclick={googleHandler}>
Continue with Google
</Button>
);
}
A few resources to help Google Sign-In JavaScript client reference,
How to integrate Google API into your React app and that's it.
It's in the end just a post request to that endpoint but I have encountered the same problems as you are.
What you need to do is to create a form like this:
<form action="<%=user_google_oauth2_omniauth_authorize_path %>" method="post">
<input type="hidden" name="authenticity_token" value="XX">
<button type="submit">Connect Google</button>
</form>
My trials failed when I haven't passed the auth token or added a "skip_before_action :verify_autneticity_token" to the callback controller. You need to fill the correct authenticity token, then it works.
Authenticity token information can be added to your html page's head section, via <%= csrf_meta_tags %>. Then you will need to parse the dom for the meta fields to fill them correctly.

How To Setup Minimalist Authentication In Rails with React?

I am trying to set up a minimal layer of authentication between my Rails backend and my React front end, but I am running into some problems.
I cannot seem to find the cookie key value that the server passes down to my client. In the network tab, I see it in the response: Set-Cookie:_skillcoop_session=...., but when I use js-cookie to look for the above cookie, _skillcoop_session, I only see one called identity-token=... and its value is different from _skillcoop_session. How do I access _skillcoop_session in the browser?
What header key do I pass up to the server to signal to my backend to use 'this' header key to match up with the session it has stored off? In this post, Justin Weiss seems to suggest that I make the request to the server with a header like: Cookie: _skillcoop_session=....
Am I doing this all wrong? Would I be better off using a gem like devise?
Also in order to load the session in my other controllers, I have had to do something like session['init'] = true, and I learned to do this from this SO post. This seems hacky. Why do I have to manually reload the session in separate controller actions after I've set it previously in a different controller action in a different request?
I'm currently just stubbing out the user and the authentication -- all I want to do to get the plumping in place is set a session[:user_id] and be able to read that session data in other controller actions. For this I have two main files for consideration: UsersController and Transport.js. In UsersController I am just stubbing the session[:user_id] with the number 1 and in Transport.js I'd like to pass the cookie received from the server so that the backend can maintain a session between requests with a client.
Here is my controller:
class UsersController < ApplicationController
def create
session[:user_id] = 1
render json: user_stub, status: :ok
end
def show
puts "user id: #{session[:user_id]}"
# should return, 1, but is returning, nil...why?
render json: user_stub, status: :ok
end
private
def user_stub
{
id: 1,
email: params['email'] || 'fakeemail#gmail.com',
password: params['password'] || 'fake password'
}
end
end
Here is the main location of my app where I make my request to the server - it's in an abstraction I call Transport.js:
require('es6-promise').polyfill();
require('isomorphic-fetch');
var cookie = require('js-cookie');
const GET = 'GET';
const POST = 'POST';
function Transport() {
}
Transport.prototype.get = function(url, options = {}) {
return this.query(GET, url, null, options);
};
Transport.prototype.post = function(url, dataString, options = {}) {
return this.query(POST, url, dataString, options);
};
Transport.prototype.query = function(method, url, dataString, options = {}) {
var data;
if (dataString) {
data = JSON.parse(dataString);
}
switch(method) {
case GET:
return fetch(url, Object.assign({headers: {'Cookie': cookie.get('_skillcoop_session')}}, options, {
method: method
}));
case POST:
return fetch(url, Object.assign({
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}, options, {
method: method
}));
default:
throw new Error("This HTTP Method is not supported.");
}
};
module.exports = Transport;
According to this SO post, one cannot access the Set-Cookie header in JS. Thus, I suppose my attempts to handle Set-Cookie in the response headers was a fools effort.
According to the NPM package that I'm using to make HTTP requests, I need to pass {credentials: 'same-origin'} key value pair in the second argument to fetch, which will 'automatically send cookies for the current domain'. That did the trick -- the session object is available and contains the user_id that was set in the session in the previous request in a different action.
Yes. I changed up how I approached this problem. I leaned very heavily on this Reddit post. In short, I use ruby-jwt on the backend and store the token in localStorage on the front end. Each request out to the server will include the token in a header AUTHORIZATION.
In following steps 1 and 2, it looks like I no longer have to 'reload the session'.

Resources