GithubHelp home page GithubHelp logo

knock's People

Contributors

adonespitogo avatar albertobajo avatar aldesantis avatar anderslemke avatar artygus avatar bmcdaniel11 avatar duboff avatar esquith avatar eturino avatar gustavovnicius avatar kalashnikovisme avatar lassebunk avatar nicktomlin avatar nsarno avatar oreshinya avatar ristovskiv avatar sachse avatar tappleby avatar toby-1-kenobi avatar wkurosawa avatar zudochkin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

knock's Issues

How to skip authenticate! some specific actions?

Hello I'm trying skip athenticate method this way:

skip_before_filter :authenticate, only: :half_hourly
and
skip_before_filter :authenticate!, only: :half_hourly

but this is not working.

Can you help me?

Document change in header-passed token representation in CHANGELOG

Commit 18b2cfb now requires prefixing a header-passed token with a Bearer string. The previous release supported both forms, with and without the prefix. This breaks compatibility with all users who did not use prefixed tokens.

Please document this change in the CHANGELOG and / or consider changing the regular expression to accommodate the old behaviour.

Add an action to check if token is valid

I think it would be nice to have an additional action alongside AuthToken#create (maybe AuthToken#validate) that simply returns success if the provided token is valid.

It can be useful when the client just wants to check if the token is valid and hasn't expired without having to decode it.

`current_user` name collision

Using Knock I added token-based authentication to a rails app that already had session-based authentication. There was already a current_user method that was included in ApplicationController which looked in the session for the logged in user. This method prevented Knock authentication working because Knock checks if it can get a result from current_user to see if the user is authenticated, but it would call my current_user method instead of it's own.

To work around this I had to change the name of my current_user method to something else.

This sort of collision should be prevented using namespaces or something like that. At the very least it should be clearly documented that you can't have your own current_user method in controllers using Knock. It would have saved me a lot of debugging time.

Does this support automatic refresh tokens?

I'm new to JWT, but it looks better than just using the same API token forever.

I know the token expires after a day (by default), but does that mean the user will have to re-login again?

  • this could lead to some un-desired behavior if someone's token is about to expire when they are in the middle of work.

How does auth work between multiple devices? (esp in the case of a refresh token)

permanent tokens?

Is there a recommended method for generating permanent tokens?

I ask because I'm making both a mobile app and the Rails API it will use, and I only want the user to login once, after they install the app.

I noticed the default expiration period is config.token_lifetime = 1.day with no obvious way to set this to infinity/nil. And after a cursory search I didn't see any mention of refresh tokens.

For now I've set it to an arbitrary long-term future date, 10.years. And will store the token locally in the app, but is that a bad idea? Thanks

Namespacing models

Hi there.

I'm trying to implement Knock in a Rails 5 API, where I have everything namespaced (e.g. V1::User, V1::UsersController, etc..).

When mounting the knock authentication endpoint inside the namespace, like this, it works fine for exchanging username and password for a JWT:

  constraints format: :json, defaults: { format: :json } do
    namespace :v1 do
      post :user_token, to: 'user_token#create'
      resources :rooms
    end
  end

It doesn't work to go to the rooms endpoint afterwords, using the new JWT token. I get this exception: NameError: uninitialized constant User. This is because I have this in my V1::BaseController:

before_action :authenticate_user

Here this is being constantized into the User model, so it's clear that a namespace is missing.

I then tried adding this to my controller instead:

before_action :authenticate_v1_user

But this gets transformed into a V1User class, not as expected a V1::User.

Is there a way that I'm not seeing for easily letting Knock know that I'm trying to get at a namespaced user?

Thanks in advance,
Emil

Passing token from iOS app returns 401

After several days of debugging and even nuking my repo and starting over, I still can't seem to get any authorization with a JWT token I'm passing from an iOS app authenticated with Auth0 Lock. After updating to Knock 1.5, I've narrowed it down to self.from_token_payload not creating a new user. Any assistance would be greatly appreciated!
Here's my repo: https://github.com/brandonmanson/happening-api

Multi-tenant solution

I'm looking at providing an API that will have multiple token_audience/token_secret_signature_key values depending on the particular account that a user is associated with. Right now, it looks like the only way I could deal with this scenario is to set those config values manually for each request. However, I'm concerned that if I'm changing things at a global module level, I might run into an issue where multiple app requests running in parallel are overwriting the config values and stomping on each other (maybe only if I have a multi-threaded app server?). If I could, I would be happy to pass those values directly to the Knock::AuthToken model, but that's not how it works currently.

Anyway, just curious if you have an idea of how to go about this. Thanks!

Authentication for a scoped user?

I would like to use Knock for an API that allows the reuse of an email address against multiple different accounts. Just like Slack, the account would be determined by the subdomain that the user logs in from.

Is it possible to scope the current_user to be found via an associated Account object?

e.g. Users.where(account_id: $account_id).find_by! Knock.handle_attr => handle

Setting @current_user in authenticable.rb

In authenticable.rb the @current_user is retrieved like this

@current_user = User.find(payload['user_id'])

but since there is no User model in the lib it will force whoever who uses knock to have a model named User and expect a claim named user_id to be present in the JWT.
What I'd like to have is a way to define how the user is retrieved from my DB, e.g. in my app's config/knock.rb I could have:

config.current_user_from_token = -> (claims) { User.find(claims['sub']) }

and then in authenticable.rb

@current_user = Knock.current_user_from_token.call(payload)

This will allow to fetch the user from any model using any claim in the JWT

Overlap with Warden

Hi,

I've noticed that this gem does not have warden as a dependency. I presume this is intentional? Is this gem reimplementing part of warden functionality?

I'm wondering what would it take to integrate knock as an authentication strategy into warden

undefined method `length' for nil:NilClass while running specs

Hello,
I try to authenticate user in my tests. So far my spec looks like:

require 'rails_helper'

RSpec.describe 'GET /posts' do
  subject { response }

  let(:user) { create(:user) }

  it 'authenticate user' do
    authenticate
    get '/posts'
    expect(response.status).to eq 200
  end
end

My User model has auth_id column which stores auth0 user id. My code method is below.

def authenticate
  token = Knock::AuthToken.new(payload: { sub: user.auth_id }).token
  request.env['HTTP_AUTHORIZATION'] = "Bearer #{token}"
end

Every time I run spec I receive error about undefined method length.

Failure/Error: authenticate
  NoMethodError:
    undefined method `length' for nil:NilClass

I will really appreciate it if someone can suggest me what's wrong with my code.

Thanks!

Typo in generators

lib/generators/templates/knock.rb:51

Believe you mean identify but wrote indentify

Verifying that authenticate is called.

It would be nice if there was built in support for verifying that authentication is enforced to lock down an application much like verify_authorized in Pundit.

Comments? Is this something you would like to see in a PR?

I'm thinking of something like:

module Knock::Authenticable
  def current_user
    @current_user ||= begin
      token = params[:token] || request.headers['Authorization'].split.last
      Knock::AuthToken.new(token: token).current_user
    rescue
      nil
    end
  end

  def authenticate
    @_authentication_performed = true
    head :unauthorized unless current_user
  end

  def authentication_performed?
    !!@_authentication_performed
  end

  def verify_authentication
    raise Knock::AuthenticationNotPerformedError unless authentication_performed?
  end
end

Usage:

class ApplicationController < ActionController::API
  include Knock::Authenticable 
  after_action :verify_authentication, except: [:show, :index]
end

404 response when credentials are bad

When a client tries to authenticate with bad credentials the server response is 404. The client then wont know if the credentials are bad or the sever is offline. Perhaps a 401 or 403 response would be better.

A [GET] on /users respond with 'NameError (uninitialized constant Knock)'

I use rails 5.0.0.beta3 and rbenv and used this guide
With rbenv i created a new rails api project rbenv exec rails _5.0.0.beta3_ new levented --api
After that i scaffold my user bin/rails g scaffold user name:string family_name:string nick_name:string email:string

routes.rb

Rails.application.routes.draw do
  mount Knock::Engine => '/knock'
  resources :users
end

application_controller.rb

class ApplicationController < ActionController::API
  include Knock::Authenticable
end

users_controller.rb

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :update, :destroy]

  # GET /users
  def index
    @users = User.all

    render json: @users
  end
end

A [GET] to /users prints the following error

ActionController::RoutingError (uninitialized constant ApplicationController::Knock):

app/controllers/application_controller.rb:2:in `<class:ApplicationController>'
app/controllers/application_controller.rb:1:in `<top (required)>'
app/controllers/users_controller.rb:1:in `<top (required)>'
Started GET "/users" for 127.0.0.1 at 2016-03-15 20:57:53 +0100

What did i wrong?

Syntax error during 'rails generate knock:install'

Hi,

executing :rails generate knock:install on my dev environment (Jruby 1.7.10 on Windows 7)

I got:

SyntaxError: C:/Ruby/jruby-1.7.10/lib/ruby/gems/shared/gems/knock-1.3.0/lib/knock.rb:12: syntax error,
 unexpected tLPAREN_ARG self.current_user_from_token = -> (claims) { User.find claims['sub'] }

I'm opening a pull request for removing the spaces that cause problem.
Let me know if is useful.

Unable to autoload constant Engine

Getting the following error (I followed the Auth0 manual)

LoadError (Unable to autoload constant Engine, expected /Users/mikhaildubov/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/knock-1.3.0/lib/knock/engine.rb to define it):

Any idea why this is happening? Appreciate any pointers.

Authorization header works with any random JWT prefix

Awesome gem.

I followed Adam's JWT tutorial in the past: http://adamalbrecht.com/2015/07/20/authentication-using-json-web-tokens-using-rails-and-react/ which builds a JWT from scratch without using gems.

Today, I tried this library, works like a gem (pun intended).

I have one issue though, hopefully it hasn't been asked before.

When I use cURL or Postman to make the request:

screen shot 2016-05-22 at 5 34 00 pm

It doesn't matter whether I put in the HTTP Headers:

"Authorization: JWT <the_real_JWT_here>"

Or

"Authorization: Blah <the_real_JWT_here>"

The Rails server still lets it through....is this how it's suppose to work?

For example, this Github README uses Bearer <JWT_token>. I would have thought a real JWT should enforce the Authorization header to have the word "JWT" instead of letting anything there.

Any ideas?

Support for Rails 5 RCs

Any recommendation on using Knock with the Rails 5 release candidates? Right now, the dependency is based on official releases (per PR #30). In the meantime, I've forked this repo and adjusted the dependency accordingly but wanted to know if you're planning an official release of Knock that supports the release candidates or not.

Readme inconsistent with 1.4.0?

I updated to 1.4.0 and everything stopped working. I assume there are some changes I need to make but on the readme the setup is exactly the same. Any chance for explaining what has changed?

Adding private claims to the token

Hi there, just wondering if you thoughts on adding additional claims to the token payload. I'd like to add some additional user information to it but it looks like I'd have to subclass the AuthTokenController (which will be easier once #47 is fixed).

Thanks and cheers

Revoking a token

I need to know how to handle the logout operation, because the token will be valid up to the time mentioned in the expiry claim, but if a user log out from the application before the expiry time, the token should become invalid.
I was searching the way to revoke a jwt, i found some of the solutions here where someone said to use blacklisting mechanism and use of refresh token, i need to know whether these features are implemented in the knock or any other alternatives for invalidating a JWT.

404 on login response format

by defualt knock returns 'json' in response format heade. I would like to return "application/vnd.api+json

I tried to costumiz eit with an after_action:

response.content_type = "application/vnd.api+json"

but it doesn't work.

I creaed y own contrller for this, but will be great to add it as an option.

Thanks!

Allow passing auth_params as a whole to authenticate (v2)

This would give the implementer the freedom to implement authenticate how they choose. I may want to pass additional information to authenticate or provide a password-less authentication mechanism.

Granted, it looks like you're trying to adhere to the interface that has_secure_password provides, but there are many use cases where this doesn't work. So making it a configurable option might make sense.

initializer broken by ruby-jwt api changes

I found config.token_secret_signature_key was broken by ruby-jwt API updates ...

# config.token_secret_signature_key = -> { JWT.base64url_decode Rails.application.secrets.auth0_client_secret }

Latest ruby-jwt(v1.5.3) completely removes base64url_decode method from JWT module(at this PR)

BTW, We may use these work around as a temporary solution :)
(we may have better solution for this problem...)

require 'base64'

# extracted from original [method](http://www.rubydoc.info/github/jwt/ruby-jwt/JWT.base64url_decode)
config.token_secret_signature_key = -> {
    secret = Rails.application.secrets.auth0_client_secret
    secret += '=' * (4 - secret.length.modulo(4))
    Base64.decode64(secret.tr('-_', '+/'))
  }

Time of token expiration?

Hi, how long the token is valid?
Is there a way to configure this time?

Thanks!!

Congratulations for the job with this gem!!!

Update documentation for testing with Rails 5

Rails 5 has now been released and ActionController::TestCase is deprecated so the documentation on testing is outdated. As far as I can tell the request.env object is no longer accessible. Instead, what I've been doing is something like this:

test_helper.rb

def authentication_token
  Knock::AuthToken.new(payload: { sub: users(:one).id }).token
end

def authenticated_headers
  {
    'Authorization': "Bearer #{authentication_token}"
  }
end

controller_test.rb

test "should respond successfully" do
    get model_url(@model), headers: authenticated_headers
end

Maybe there's a better way to do this? Once a format has been decided on I can make a pull request, if you'd like.

URL authentication

Would be nice be able to pass the token via query string. Something like

token = request.headers['Authorization'].split(' ').last || params[:token]

Interested?

No method error: User doesn't have 'authenticate'

Knock looks like a great solution but I'm having issues. I know Knock is in there. The routes are registered and when I hit a secured page with no headers supplied, I get a 401. However, when I do supply the correct headers, I get this no method error. Any ideas what's up?

https://github.com/brandonpittman/knock_test/blob/master/app/controllers/users_controller.rb

Has anyone done a tutorial using Knock that you know of? I'm not having a lot of luck getting it up and running.

uninitialized constant Knock

I changed my development machine. The same code that works fine on the old machine, now on the new machine gives this error:

uninitialized constant Knock

from this line in routes.rb
mount Knock::Engine => '/knock'

When I comment out that line I get

uninitialized constant ApplicationController::Knock

from this line in application_controller.rb
include Knock::Authenticable

The only difference I can think of from old machine to new is that this time I used rbenv instead of RVM, and also my ruby version is 2.3.0 instead of 2.2.3

Devise Integration Example

Anyone try integrating this with Devise yet? I want Devise for it's user and password management, but need JWT since I have an API.

Using Knock for SSO with multiple servers

I'm still wrapping my mind around how JWTs work and hoping for some guidance on how I might use Knock to help me in my situation.

I have an Authentication Server (Rails), a Client (Angular2), and a Data API (Rails). From the client I need to be able to authenticate with the auth server which would then give me permission to access the data api. I'm a little confused how to use the received JWT from the auth server with the data api. How does the data api decode the request that was signed by the auth server? Do I need to have the same 'secret' on both servers? I'm going for an SSO solution here so I'd like this to work with any *.mydomain.com. I assume Knock would be running on the Auth server since it has User accounts and I would need to implement something with straight JWT decoding on the Data API server.

I really appreciate any advice. Thanks!

Knock::AuthTokenController extending

Hello. In some cases AuthTokenController may need custom behaviour, but all the methods inside except create action are private. What about making them protected? This will allow users add some custom logic. For example, adding expiration date or user identifier to response and etc

[Question] How do I authenticate an Auth0 user in my request specs?

I'm trying to write some request specs for my API but the following code results in a 401 status:

# spec/support/auth0.rb

def authenticate(user)
  # user.auth0_id contains the actual user id from Auth0
  Knock::AuthToken.new(payload: { sub: user.auth0_id }).token
end
# spec/requests/dashboard_spec.rb

require "rails_helper"

RSpec.describe 'GET /dashboard' do
  let(:user) { create(:user) }
  let(:auth_token) { authenticate(user) }

  context 'when a user is logged in' do
    it "returns a 200 response" do
      get '/dashboard', headers: { 'Authorization' => "Bearer #{auth_token}" }
      expect(response.status).to eq 200
    end
  end
end

Any idea on how to resolve this? By the way, i'm using the v2 branch with my updates found here

Support for Rails 4.0.x ?

When I run bundle update I get:
In Gemfile:
rails (= 4.0.13)
knock was resolved to 1.0.0, which depends on
rails (~> 4.2)
I tried to force a lower version of knock, but this was "not found."

Going to the next version of Rails has many code-breaking changes within this app.
Is there a way to run knock on Rails 4.0.x ?

Unauthorized response in specs

When testing the response is always unauthorized with response.body equaling f.

deliveries_controller_spec.rb

require 'rails_helper'

module V1
  RSpec.describe DeliveriesController, type: :controller do
    let(:auth_token) { authenticate }
    context 'index' do
      before do
        @deliveries = [DateTime.now, (DateTime.now - 1), DateTime.now, (DateTime.now + 1), DateTime.now].each_with_object([]) do |time, stash|
          stash << create(:delivery, eta: time)
        end

        @deliveries.each_with_index do |delivery, index|
          delivery.locations << create(:location, name: "Location #{index}")
        end
      end
      it 'give all deliveries that have an eta for the current day' do
        get :index, headers: { 'Authorization' => "Bearer #{auth_token}" }
        expected_count = 3
        full_count = @deliveries.size
        expect(json_data.size).to_not be(full_count)
        expect(json_data.size).to be(expected_count)
      end
      context 'by location' do
        it 'give all deliveries for a location for that day' do
          location = @deliveries.last.locations.first
          get :index, location_id: location.id, headers: { 'Authorization' => "Bearer #{auth_token}" }
          expected_count = 1
          full_count = @deliveries.size
          puts response.body
          expect(json_data.size).to_not be(full_count)
          expect(json_data.size).to be(expected_count)
        end
      end
    end
    context 'CRUD' do
      it 'show' do
        delivery = create(:delivery)
        get :show, id: delivery, headers: { 'Authorization' => "Bearer #{auth_token}" }
        expect(response).to be_successful
        expect(json_data).to include(:id, :type, :attributes)
      end
      it 'create' do
        post :create, json_api_format { attributes_for(:delivery) }
        expect(response).to be_successful
        expect(json_data).to include(:id, :type, :attributes)
      end

      it 'update' do
        delivery = create(:delivery)
        old_cargo = delivery.cargo
        expected_cargo = 'Updated Cargo'
        delivery.cargo = expected_cargo
        patch :update, json_api_format(delivery.id) { delivery.attributes }, headers: { 'Authorization' => "Bearer #{auth_token}" }
        delivery.reload
        expect(delivery.cargo).to_not eq(old_cargo)
        expect(delivery.cargo).to eq(expected_cargo)
      end
    end
  end
end

deliveries_controller.rb

module V1
  class DeliveriesController < ApplicationController
    before_action :authenticate
    def index
      if location
        render json: location.deliveries.today, status: :ok
      else
        render json: Delivery.today, include: '*', status: :ok
      end
    end

    def show
      delivery = Delivery.find delivery_id
      render json: delivery, include: '*', status: :ok
    end

    def create
      delivery = Delivery.create delivery_params
      render json: delivery, include: '*', status: :created
    end

    def update
      delivery = Delivery.find delivery_id
      delivery.update delivery_params
      render json: delivery, status: :ok
    end

    private

    def location
      @location ||= Location.find_by(id: location_params[:location_id])
    end

    def location_params
      params.permit(:location_id)
    end

    def delivery_id
      params[:id]
    end

    def delivery_params
      ActiveModelSerializers::Deserialization
        .jsonapi_parse(params, only: [:cargo, :cargo_quantity, :notes, :arrive_at, :escort,
                                      :priority, :processing_time, :eta, :icon_url, :escort_badge_number])
    end
  end
end

spec support

def authenticate
   token = Knock::AuthToken.new(payload: { sub: create(:driver).id }).token
   request.env['HTTP_AUTHORIZATION'] = "Bearer #{token}"
 end

config/initializers/knock.rb

  config.current_user_from_handle = -> (handle) { Driver.find_by! Knock.handle_attr => handle }

  config.current_user_from_token = -> (claims) { Driver.find claims['sub'] }

Rails cannot recognise the engine version

Versions of rails and rails-api:

gem 'rails-api', '0.4.0'
gem 'rails', '~> 4.2.3'

Specs written with rspec get in some kind of conflict and throws the error that it cannot find the version of the engine version.
The path provided in the error is the same as the version file but somehow it cannot be loaded.

Adding custom message when RecordNotFound exceptions occurs

Hey, I see you specify for us to raise an ActiveRecord::RecordNotFound for the authentication process but I am having trouble doing so as I do not know where to do it since the controller for generating tokens based on user credentials is the one you provide with your gem.

What I would like to do is send back with the status a response with a custom message like "Wrong Credentials" for example when such exception occurs. I imagine I should use a rescue but I do not know how or where, thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.