1

This is partly a problem-solving question, partly a "I'm trying to understand what's going on" question. I hope that's allowed. Basically, I'm trying to get Warden user authentication to work with Ruby/Sinatra and Postgres on Heroku.

I got a lot of help from this handy (but oldish) tutorial.

From some Rails experience I am a bit familiar with Active Record. The tutorial didn't mention anything about creating a migration for the User class. I went ahead and made my own migration, with my own properties ("name", "email", "password"), only to discover later that, lo and behold, the properties I put in that migration weren't being used by (and in fact were rejected by) the actual model in use. When I examined the object instances in the database, I found that they had only the properties Warden provided for me ("username" and "password").

I'm just trying to understand what happened here. I migrated down my (apparently unnecessary and ignored) Users migration, and nothing happened. I mean that I was able to create User instances and log in using them just as before.

Then it occurred to me that this old Warden tutorial (from 2012) uses something called DataMapper, which does what Active Record would do today. Is that right? They are both "ORMs"? I'm still confused about why Sinatra completely ignored the User migration I did. Maybe it's just using a different database--I did notice wht might be a new db.sqlite database in my main file. Pretty sure the one I created for Active Record was db/madlibs.sqlite3.

Although it works on my local machine, I'm pretty sure it won't work on Heroku, since they don't support sqlite (pretty sure). That then means I'll have to go back to the Warden documentation and figure out how to get it to work with my Postgres database...right? Any pointers on how to get started with that? Since this will be my first project using any authentication library like Warden, it's pretty intimidating.

Here's what I have so far (repo):

app.rb:

require 'sinatra'
require 'sinatra/activerecord'
require 'sinatra/base'
require './config/environment'
require 'bundler'
Bundler.require
require './model'
enable :sessions

class Madlib < ActiveRecord::Base
end

class SinatraWardenExample < Sinatra::Base
  register Sinatra::Flash
end

use Warden::Manager do |config|
  config.serialize_into_session{|user| user.id }
  config.serialize_from_session{|id| User.get(id) }
  config.scope_defaults :default,
                        strategies: [:password],
                        action: 'auth/unauthenticated'
  config.failure_app = self
end

Warden::Manager.before_failure do |env,opts|
  env['REQUEST_METHOD'] = 'POST'
end

Warden::Strategies.add(:password) do
  def valid?
    params['user']['username'] && params['user']['password']
  end

  def authenticate!
    user = User.first(username: params['user']['username'])

    if user.nil?
      fail!("The username you entered does not exist.")
    elsif user.authenticate(params['user']['password'])
      success!(user)
    else
      fail!("Could not log in")
    end
  end
end

...non authentication routes...

post '/auth/login' do
  env['warden'].authenticate!

  flash[:success] = env['warden'].message

  if session[:return_to].nil?
    redirect '/'
  else
    redirect session[:return_to]
  end
end

get '/auth/logout' do
  env['warden'].raw_session.inspect
  env['warden'].logout
  flash[:success] = 'Successfully logged out'
  redirect '/'
end

post '/auth/unauthenticated' do
  session[:return_to] = env['warden.options'][:attempted_path]
  puts env['warden.options'][:attempted_path]
  flash[:error] = env['warden'].message || "You must log in"
  redirect '/auth/login'
end

get '/protected' do
  env['warden'].authenticate!
  @current_user = env['warden'].user
  erb :protected
end

model.rb (just the User model):

require 'rubygems'
require 'data_mapper'
require 'dm-sqlite-adapter'
require 'bcrypt'

DataMapper.setup(:default, "sqlite://#{Dir.pwd}/db.sqlite")

class User
  include DataMapper::Resource
  include BCrypt

  property :id, Serial, :key => true
  property :username, String, :length => 3..50
  property :password, BCryptHash

  def authenticate(attempted_password)
    if self.password == attempted_password
      true
    else
      false
    end
  end

end

DataMapper.finalize
DataMapper.auto_upgrade!

It seems like this repo might have solved the problems I'm facing now. Should I study that? The Warden documentation itself is pretty forbidding for a relative beginner. For example, it says "Warden must be downstream of some kind of session middleware. It must have a failure application declared, and you should declare which strategies to use by default." I don't understand that. And then it gives some code...which I also don't quite understand. Advice?? (Should I be working with a teacher/mentor, maybe?)

globewalldesk
  • 544
  • 1
  • 3
  • 18

0 Answers0