Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add authenticate_or_request_with_http_token #85

Merged
merged 1 commit into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/rage/controller/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,28 @@ def authenticate_with_http_token
yield token
end

# Authenticate using an HTTP Bearer token, or otherwise render an HTTP header requesting the client to send a
# Bearer token. For the authentication to be considered successful, the block should return a non-nil value.
#
# @yield [token] token value extracted from the `Authorization` header
# @example
# before_action :authenticate
#
# def authenticate
# authenticate_or_request_with_http_token do |token|
# ApiToken.find_by(token: token)
# end
# end
def authenticate_or_request_with_http_token
authenticate_with_http_token { |token| yield(token) } || request_http_token_authentication
end

# Render an HTTP header requesting the client to send a Bearer token for authentication.
def request_http_token_authentication
headers["Www-Authenticate"] = "Token"
render plain: "HTTP Token: Access denied.", status: 401
end

if !defined?(::ActionController::Parameters)
# Get the request data. The keys inside the hash are symbols, so `params.keys` returns an array of `Symbol`.<br>
# You can also load Strong Params to have Rage automatically wrap `params` in an instance of `ActionController::Parameters`.<br>
Expand Down
142 changes: 92 additions & 50 deletions spec/controller/api/authenticate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,9 @@
RSpec.describe RageController::API do
subject { described_class.new(env, nil) }

context "with a Bearer token" do
let(:env) { { "HTTP_AUTHORIZATION" => "Bearer my_token" } }

it "extracts the token" do
subject.authenticate_with_http_token do |token|
expect(token).to eq("my_token")
end
end

it "returns the value of the login procedure" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to eq(:request_authenticated)
end

context "with a token prefix" do
let(:env) { { "HTTP_AUTHORIZATION" => "Bearer token=my_token" } }
context "#authenticate_with_http_token" do
context "with a Bearer token" do
let(:env) { { "HTTP_AUTHORIZATION" => "Bearer my_token" } }

it "extracts the token" do
subject.authenticate_with_http_token do |token|
Expand All @@ -30,25 +17,25 @@
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to eq(:request_authenticated)
end
end
end

context "with a Token token" do
let(:env) { { "HTTP_AUTHORIZATION" => "Token my_token" } }
context "with a token prefix" do
let(:env) { { "HTTP_AUTHORIZATION" => "Bearer token=my_token" } }

it "extracts the token" do
subject.authenticate_with_http_token do |token|
expect(token).to eq("my_token")
end
end
it "extracts the token" do
subject.authenticate_with_http_token do |token|
expect(token).to eq("my_token")
end
end

it "returns the value of the login procedure" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to eq(:request_authenticated)
it "returns the value of the login procedure" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to eq(:request_authenticated)
end
end
end

context "with a token prefix" do
let(:env) { { "HTTP_AUTHORIZATION" => "Token token=my_token" } }
context "with a Token token" do
let(:env) { { "HTTP_AUTHORIZATION" => "Token my_token" } }

it "extracts the token" do
subject.authenticate_with_http_token do |token|
Expand All @@ -61,45 +48,100 @@
expect(value).to eq(:request_authenticated)
end

context "with quotes" do
let(:env) { { "HTTP_AUTHORIZATION" => "Token token=\"my_token\"" } }
context "with a token prefix" do
let(:env) { { "HTTP_AUTHORIZATION" => "Token token=my_token" } }

it "extracts the token" do
subject.authenticate_with_http_token do |token|
expect(token).to eq("my_token")
end
end

it "returns the value of the login procedure" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to eq(:request_authenticated)
end

context "with quotes" do
let(:env) { { "HTTP_AUTHORIZATION" => "Token token=\"my_token\"" } }

it "extracts the token" do
subject.authenticate_with_http_token do |token|
expect(token).to eq("my_token")
end
end
end
end
end
end

context "with a Digest token" do
let(:env) { { "HTTP_AUTHORIZATION" => "Digest my_token" } }
context "with a Digest token" do
let(:env) { { "HTTP_AUTHORIZATION" => "Digest my_token" } }

it "doesn't extract the token" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to be_nil
it "doesn't extract the token" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to be_nil
end

it "doesn't call the login procedure" do
expect {
subject.authenticate_with_http_token { raise }
}.not_to raise_error
end
end

it "doesn't call the login procedure" do
expect {
subject.authenticate_with_http_token { raise }
}.not_to raise_error
context "with no token" do
let(:env) { {} }

it "returns nil" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to be_nil
end

it "doesn't call the login procedure" do
expect {
subject.authenticate_with_http_token { raise }
}.not_to raise_error
end
end
end

context "with no token" do
context "#authenticate_or_request_with_http_token" do
let(:env) { {} }

it "returns nil" do
value = subject.authenticate_with_http_token { :request_authenticated }
expect(value).to be_nil
before do
expect(subject).to receive(:authenticate_with_http_token).and_yield("my_test_token")
end

it "doesn't call the login procedure" do
expect {
subject.authenticate_with_http_token { raise }
}.not_to raise_error
it "extracts the token" do
subject.authenticate_or_request_with_http_token do |token|
expect(token).to eq("my_test_token")
end
end

it "returns the value of the login procedure" do
value = subject.authenticate_or_request_with_http_token { :request_authenticated }
expect(value).to eq(:request_authenticated)
end

it "doesn't request authentication if login procedure returns non nil" do
subject.authenticate_or_request_with_http_token { :request_authenticated }
expect(subject.response.headers).not_to have_key("Www-Authenticate")
end

it "doesn't request authentication if login procedure returns nil" do
subject.authenticate_or_request_with_http_token {}
expect(subject.response.headers["Www-Authenticate"]).to eq("Token")
end
end

context "#request_http_token_authentication" do
let(:env) { {} }

it "requests token authentication" do
subject.request_http_token_authentication {}

expect(subject.response.body).to eq("HTTP Token: Access denied.")
expect(subject.response.headers["Www-Authenticate"]).to eq("Token")
end
end
end
Loading