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

Feat: Implement oauth authorization server metadata #1737

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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: 12 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,23 @@ ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8

ENV BUNDLER_VERSION=2.5.11
RUN gem install bundler -v ${BUNDLER_VERSION} -i /usr/local/lib/ruby/gems/$(ls /usr/local/lib/ruby/gems) --force

WORKDIR /srv

COPY Gemfile doorkeeper.gemspec /srv/
COPY lib/doorkeeper/version.rb /srv/lib/doorkeeper/version.rb

RUN bundle install

COPY . /srv/

RUN chown -R doorkeeper:doorkeeper /srv/coverage
RUN chown -R doorkeeper:doorkeeper /srv

# Set the running user for resulting container
USER doorkeeper

CMD ["rake"]
RUN mkdir -p /srv/.local/gem/share
ENV GEM_HOME=/srv/.local/gem/share

ENV BUNDLER_VERSION=2.5.11
RUN gem install bundler -v ${BUNDLER_VERSION}

# This is a fix for sqlite alpine issues
RUN bundle config force_ruby_platform true
RUN bundle install

CMD ["bundle", "exec", "rake"]
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ gem "sqlite3", "~> 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]

gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw]
gem "timecop"

gem 'irb', '~> 1.8'

# Interactive Debugging tools
gem 'debug', '~> 1.8'
9 changes: 9 additions & 0 deletions app/controllers/doorkeeper/discovery_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Doorkeeper
class DiscoveryController < Doorkeeper::ApplicationMetalController
def show
render json: {}, status: 200
end
end
end
10 changes: 2 additions & 8 deletions bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@
require "bundler/setup"
require "rails/all"
require "active_support/all"
require "irb"
require "debug"
require "doorkeeper"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start

Rails.logger = Logger.new(STDOUT)

Rails.logger.info("Doorkeeper version: #{Doorkeeper::VERSION::STRING}")
Expand All @@ -32,5 +27,4 @@ ActiveRecord::Base.establish_connection(
# Load database schema
load File.expand_path("../spec/dummy/db/schema.rb", __dir__)

require "irb"
IRB.start(__FILE__)
7 changes: 7 additions & 0 deletions lib/doorkeeper/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ def configure_secrets_for(type, using:, fallback:)
option :orm, default: :active_record
option :native_redirect_uri, default: "urn:ietf:wg:oauth:2.0:oob", deprecated: true
option :grant_flows, default: %w[authorization_code client_credentials]
option :pkce_code_challenge_methods, default: %w[plain S256]
option :handle_auth_errors, default: :render
option :token_lookup_batch_size, default: 10_000
# Sets the token_reuse_limit
Expand Down Expand Up @@ -554,6 +555,12 @@ def scopes_by_grant_type
@scopes_by_grant_type ||= {}
end

def pkce_code_challenge_methods_supported
return [] unless access_grant_model.pkce_supported?

pkce_code_challenge_methods
end

def client_credentials_methods
@client_credentials_methods ||= %i[from_basic from_params]
end
Expand Down
12 changes: 12 additions & 0 deletions lib/doorkeeper/config/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def validate!
validate_reuse_access_token_value
validate_token_reuse_limit
validate_secret_strategies
validate_pkce_code_challenge_methods
end

private
Expand Down Expand Up @@ -48,6 +49,17 @@ def validate_token_reuse_limit
)
@token_reuse_limit = 100
end

def validate_pkce_code_challenge_methods
return if pkce_code_challenge_methods.all? {|method| method =~ /^plain$|^S256$/ }

::Rails.logger.warn(
"[DOORKEEPER] You have configured an invalid value for pkce_code_challenge_methods option. " \
"It will be set to default ['plain', 'S256']",
)

@pkce_code_challenge_methods = ['plain', 'S256']
end
end
end
end
2 changes: 1 addition & 1 deletion lib/doorkeeper/oauth/pre_authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def validate_code_challenge_method
return true unless Doorkeeper.config.access_grant_model.pkce_supported?

code_challenge.blank? ||
(code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
(code_challenge_method.present? && Doorkeeper.config.pkce_code_challenge_methods_supported.include?(code_challenge_method))
end

def response_on_fragment?
Expand Down
6 changes: 6 additions & 0 deletions lib/doorkeeper/rails/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ def generate_routes!(options)
map_route(:authorized_applications, :authorized_applications_routes)
map_route(:token_info, :token_info_routes)
end

map_route(:discovery, :discovery_routes)
end

private

def discovery_routes(mapping)
routes.get ".well-known/oauth-authorization-server", controller: mapping[:controllers], action: :show
end

def authorization_routes(mapping)
routes.resource(
:authorization,
Expand Down
1 change: 1 addition & 0 deletions lib/doorkeeper/rails/routes/mapping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def initialize
authorizations: "doorkeeper/authorizations",
applications: "doorkeeper/applications",
authorized_applications: "doorkeeper/authorized_applications",
discovery: "doorkeeper/discovery",
tokens: "doorkeeper/tokens",
token_info: "doorkeeper/token_info",
}
Expand Down
7 changes: 7 additions & 0 deletions spec/dummy/app/controllers/custom_discovery_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class CustomDiscoveryController < ::ApplicationController
def show
render nothing: true
end
end
22 changes: 22 additions & 0 deletions spec/lib/oauth/authorization_code_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,28 @@

expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)
end

context "when PKCE code challenge methods is set to only S256" do
before do
Doorkeeper.configure do
pkce_code_challenge_methods ["S256"]
end
end

it "validates when code_verifier is S256" do
params[:code_verifier] = grant.code_challenge = "S256"
request.validate

expect(request.error).to eq(nil)
end

it "invalidates when code_verifier is plain" do
params[:code_verifier] = "plain"
request.validate

expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant)
end
end
end

context "when PKCE is not supported" do
Expand Down
22 changes: 22 additions & 0 deletions spec/lib/oauth/pre_authorization_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,28 @@

expect(pre_auth).not_to be_authorizable
end

context "when pkce_code_challenge_methods is set to only S256" do
before do
Doorkeeper.configure do
pkce_code_challenge_methods ["S256"]
end
end

it "accepts S256 as a code_challenge_method" do
attributes[:code_challenge] = "a45a9fea-0676-477e-95b1-a40f72ac3cfb"
attributes[:code_challenge_method] = "S256"

expect(pre_auth).to be_authorizable
end

it "rejects plain as a code_challenge_method" do
attributes[:code_challenge] = "a45a9fea-0676-477e-95b1-a40f72ac3cfb"
attributes[:code_challenge_method] = "plain"

expect(pre_auth).to_not be_authorizable
end
end
end

context "when PKCE is not supported" do
Expand Down
14 changes: 14 additions & 0 deletions spec/requests/endpoints/discovery_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe "Discovery endpoint" do
it "returns json" do
get "/.well-known/oauth-authorization-server"

response_status_should_be(200)

# WIP: currently empty
expect(json_response).to eq({})
end
end
22 changes: 19 additions & 3 deletions spec/routing/custom_controller_routes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
controllers authorizations: "custom_authorizations",
tokens: "custom_authorizations",
applications: "custom_authorizations",
token_info: "custom_authorizations"
token_info: "custom_authorizations",
discovery: "custom_discovery"

as authorizations: "custom_auth",
tokens: "custom_token",
Expand All @@ -29,7 +30,8 @@
controllers authorizations: "custom_authorizations",
tokens: "custom_authorizations",
applications: "custom_authorizations",
token_info: "custom_authorizations"
token_info: "custom_authorizations",
discovery: "custom_discovery"

as authorizations: "custom_auth",
tokens: "custom_token",
Expand All @@ -41,7 +43,8 @@
use_doorkeeper do
controllers authorizations: "custom_authorizations",
tokens: "custom_authorizations",
token_info: "custom_authorizations"
token_info: "custom_authorizations",
discovery: "custom_discovery"

as authorizations: "custom_auth",
tokens: "custom_token",
Expand Down Expand Up @@ -83,6 +86,11 @@
expect(get("/inner_space/scope/token/info")).to route_to("custom_authorizations#show")
end

it "GET /inner_space/.well-known/oauth-authorization-server route to show Discovery controller" do
expect(get("/space/.well-known/oauth-authorization-server")).to route_to("custom_discovery#show")
end


it "GET /space/oauth/authorize routes to custom authorizations controller" do
expect(get("/space/oauth/authorize")).to route_to("custom_authorizations#new")
end
Expand Down Expand Up @@ -115,6 +123,10 @@
expect(get("/space/oauth/token/info")).to route_to("custom_authorizations#show")
end

it "GET /space/.well-known/oauth-authorization-server route to show Discovery controller" do
expect(get("/space/.well-known/oauth-authorization-server")).to route_to("custom_discovery#show")
end

it "POST /outer_space/oauth/token is not be routable" do
expect(post("/outer_space/oauth/token")).not_to be_routable
end
Expand All @@ -130,4 +142,8 @@
it "GET /outer_space/oauth/token_info is not routable" do
expect(get("/outer_space/oauth/token/info")).not_to be_routable
end

it "GET /outer_space/.well-known/oauth-authorization-server route to show Discovery controller" do
expect(get("/outer_space/.well-known/oauth-authorization-server")).to route_to("custom_discovery#show")
end
end
4 changes: 4 additions & 0 deletions spec/routing/default_routes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@
it "GET /oauth/token/info route to authorized TokenInfo controller" do
expect(get("/oauth/token/info")).to route_to("doorkeeper/token_info#show")
end

it "GET /.well-known/oauth-authorization-server route to show Discovery controller" do
expect(get("/.well-known/oauth-authorization-server")).to route_to("doorkeeper/discovery#show")
end
end
4 changes: 4 additions & 0 deletions spec/routing/scoped_routes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@
it "POST /scope/introspect routes not to exist" do
expect(post("/scope/introspect")).not_to be_routable
end

it "GET /.well-known/oauth-authorization-server route to show Discovery controller" do
expect(get("/.well-known/oauth-authorization-server")).to route_to("doorkeeper/discovery#show")
end
end