Phoenix simple auth testing logged in user - authentication

From this Simple authentication tutorial
I am looking to test routes of the app inside the :login_required pipeline (which simply checks if the client has called Guardian.Plug.sign_in(conn, user))
As the user_path show action needs to have been piped through the :login_required pipeline, I would have thought that to test this route I would simply need to write the following:
Auth.login_by_email_and_pass(conn, user.email, #password)
and then pipe the conn that comes out of that into:
get conn, user_path(conn, :show, user.id)
and check that I get a 200 status code.
But I can't get past the Auth.login_by_email_and_pass(conn, email, password) line and get the error:
session not fetched, call fetch_session/2
Where should I fetch the session?
I have tried bypass_through(conn, [:browser, :with_session])
I would have thought that the :browser pipeline invokes fetch_session which would have solved this issue.
I have also tried as calling fetch_session before and after, but still get the same error

You can use Plug.Test.init_test_session/2 to initialize the session. Since you don't need to put any data, you can pass an empty map as the second argument.
conn = conn
|> init_test_session(%{})
|> Auth.login_by_email_and_pass(user.email, #password)

Related

How to stop further routing in a plug?

I'm using Phoenix, and in router.ex, I have defined a function that verifies the user's JWT token, before continuing on to routing through the api, in order to protect the routes. However, I am having the issue that if, say, I return a status of 403 in the plug, it continues routing and I end up getting an error as I am both accessing the route and disallowing access.
My code looks something like this:
def validate(conn, _opts) do
if !Map.has_key?(conn.cookies, "jwt") do
conn
|> send_resp(403, "Forbidden")
# I would like to have something here that stops further
# routing - i.e. a way to say 'already sent a response; don't keep going'
end
end
Here is an excerpt from Plug.Conn.send_resp/3
Note that this function does not halt the connection, so if subsequent plugs try to send another response, it will error out. Use halt/1 after this function if you want to halt the plug pipeline.

Flask - How to require login only for post requests

I'm working in Flask and I want to allow users to enter information into a form without logging in but be required to login if they submit the form. After logging in, it should be as though a user just submitted the form (they shouldn't have to re-enter any information).
To store their information, I've used sessions like this. It works well:
if request.method == "POST":
if "arg1" not in session.keys() and "arg2" not in session.keys():
session["arg1"] = request.form.get('arg1')
session["arg2"] = request.form.get('arg2')
However, I'm having trouble with the login required part. I know I can use #login_required on the whole route but I just want #login_required to apply if the request is a post method. I've tried simply adding #login_required after checking if the method is a post request but it doesn't work.
My login route looks like this:
#app.route("/login", methods = ["POST", "GET"])
def login():
#log user in
return redirect(request.args.get("next") or url_for('index'))
It seems as though I need two things.
1: To apply #login_required solely to a post request.
2: To have request.args.get("next") call a post request, not get request
How could I go about doing these two things and achieve my goal?
Thank you!
Break out your routes. 1 for GET and 1 for POST
#app.route("/login", methods = ["GET"])
def get_login():
return stuff
#app.route("/login", methods = ["POST"])
#login_required
def post_login():
return stuff
There are a couple patterns that could be used here but this one is the most straight forward.

Phoenix Framework: current_user not available in view despite successfully fetching from database

Using a module plug, I fetch the current users information from the database and store it in Plug.Conn.assigns as current_user. By inspecting the connection I see the current users details in the assigns but I'm unable to access any details.
For example, I would like to display the current user's name, as in, "you are logged in as #conn.assigns.current_user.name " but Phoenix is showing the current user is nil.
Please view the accompanying screenshot.
There is certainly a current_user object in the Plug.Conn.assigns. I should be able to access the name, "cooldude77#aol.com". So, why is Phoenix telling me the current_user is nil?
EDIT:
Adding the current user plug:
def call(conn, _params) do
if conn.assigns[:current_user] do
conn
else
user_id = get_session(conn, :user_id)
cond do
user = Repo.get_by(User, auth_id: user_id) ->
conn
|> assign(:current_user, user)
|> assign(:user_signed_in?, true)
true ->
conn
|> assign(:current_user, nil)
|> assign(:user_signed_in?, false)
end
end
end
So, here I'm using Auth0 for authentication. The user logs in at Auth0 and is redirected back to app with 3 attributes that I track, a unique auth_id, a name, and an avatar. Upon logging in I check if this is a new user by checking if their auth_id is saved in my database, if it is then I know they are a returning user and I assign the current_user object to the user. If they are a new user I add them to my database (where they are given some additional attributes) and assign the current_user object to the user.
Still experiencing this problem where I have a current_user in the connection but I cannot access the user's attributes - they are showing as nil
You should be able to just use #current_user.name in the template to get the user.
Also, since the value of current_user is set to nil in the plug when nobody is logged in, you can just do !!#current_user instead of #conn.assigns[:user_signed_in?] up above.
The assumptions of #Badu lead me to discover the issue. In my controller code I originally passed the current_user from the session to the render function like so:
def index(conn, _params) do
render(conn, "index.html", current_user: get_session(conn, :current_user))
end
As the application grew and I added more pages I had to keep grabbing the current user out of the session so I decided to replace this behavior with a plug, the code of which I posted above. However, I forgot to delete the current_user object I was storing in the connection from the controller. So, while a current_user object did exist in my connection, my view_template was accessing an older deprecated version of current_user, which did not have the same attributes, hence the nil values.

My Salesforce password changed and now my Ruby on Rails app is broken

I have a simple demo app set up to be able to access Salesforce.com from a Ruby on Rails app. My code is extremely simple:
def sign_in_salesforce
client = OAuth2::Client.new(ENV['SALESFORCE_CONSUMER_KEY'], ENV['SALESFORCE_CONSUMER_SECRET'], :site => 'https://login.salesforce.com/', :authorize_url => 'services/oauth2/authorize', :token_url => 'services/oauth2/token')
auth_url = client.auth_code.authorize_url(:redirect_uri => 'https://99.44.242.76:3000/users/oauth_callback')
redirect_to auth_url
end
I then have a method to take care of the callback.
def oauth_callback
db_client = Databasedotcom::Client.new
db_client.authenticate(:token => params[:code])
puts db_client.inspect
end
The error in the console is:
ArgumentError (ArgumentError):
app/controllers/users_controller.rb:60:in `oauth_callback'
The line that is causing the error is:
db_client.authenticate(:token => params[:code])
like the token that I am getting is invalid or something.
It worked fine until I changed my Salesforce password (which they required me to do). What am I missing? Thanks for the help.
If the response you receive is that your refresh token is no longer valid then you need to restart the OAuth process from scratch to obtain a new refresh token; you can then use to get new session tokens in subsequent uses of the app as you have been up until now.
Essentially, start the process as you would for the very first time the app is launched.

speedy_c2dm: NoMethodError (undefined method `gsub' for nil:NilClass)

I set up speedy_c2dm to send "push" messages to android devices.
The gem was working fine, but now I get this NoMethodError message when I call
SpeedyC2DM::API.send_notification(options)
the options parameter is good, I have verified this.
From the ruby-doc I got the following code from the gem:
def get_auth_token(email, password)
data = "accountType=HOSTED_OR_GOOGLE&Email=#{email}&Passwd=#{password}&service=ac2dm"
headers = { "Content-type" => "application/x-www-form-urlencoded",
"Content-length" => "#{data.length}"}
uri = URI.parse(AUTH_URL)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
response, body = http.post(uri.path, data, headers)
return body.split("\n")[2].gsub("Auth=", "")
end
You can see that the last line uses gsub, so I believe the problem is in the authentication method.
I have changed the password of the account since I created this, I updated the file with the password, initializers/speedy_c2dm.rb:
C2DM_API_EMAIL = "myemail#gmail.com"
C2DM_API_PASSWORD = "mynewpassword"
SpeedyC2DM::API.set_account(C2DM_API_EMAIL, C2DM_API_PASSWORD)
Can this be causing the error? That I changed the password even though I updated this file?
(Google doesn't let me to go back to the old password, I have to create a new one different from the old ones if I change it again)
Its the only thing I can think of since I didn't modify the gem's code.
How can I fix it? C2DM is deprecated now, but its supposed to keep working for old users. I don't want to migrate to GCM if I don't need to, everything is set up to work with C2DM
Any other ideas to fix it are welcome.
The problem was fixed after I removed the "two step verification" for logging in to my email.
This change can be made in the account configuration of gmail.