[#43193] Remove OAuth cookie after successful authorization against Nextcloud

https://community.openproject.org/work_packages/43193
bug/43193-remove-oauth-cookie-after-successful-authorization-against-nextcloud
Andreas Pfohl 2 years ago
parent 062d92927c
commit bd3fd0c32c
No known key found for this signature in database
GPG Key ID: FF58F3B771328EB4
  1. 2
      Gemfile
  2. 4
      Gemfile.lock
  3. 20
      app/controllers/oauth_clients_controller.rb
  4. 37
      app/services/oauth_clients/redirect_uri_from_state_service.rb

@ -207,6 +207,8 @@ gem "sentry-ruby", '~> 5.3.0'
# Appsignal integration # Appsignal integration
gem "appsignal", "~> 3.0", require: false gem "appsignal", "~> 3.0", require: false
gem 'dry-monads', '~> 1.4'
group :test do group :test do
gem 'launchy', '~> 2.5.0' gem 'launchy', '~> 2.5.0'
gem 'rack-test', '~> 2.0.0' gem 'rack-test', '~> 2.0.0'

@ -439,6 +439,9 @@ GEM
dry-logic (1.2.0) dry-logic (1.2.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
dry-core (~> 0.5, >= 0.5) dry-core (~> 0.5, >= 0.5)
dry-monads (1.4.0)
concurrent-ruby (~> 1.0)
dry-core (~> 0.7)
dry-types (1.5.1) dry-types (1.5.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
dry-container (~> 0.3) dry-container (~> 0.3)
@ -1052,6 +1055,7 @@ DEPENDENCIES
disposable (~> 0.6.2) disposable (~> 0.6.2)
doorkeeper (~> 5.5.0) doorkeeper (~> 5.5.0)
dotenv-rails dotenv-rails
dry-monads (~> 1.4)
email_validator (~> 2.2.3) email_validator (~> 2.2.3)
equivalent-xml (~> 0.6) equivalent-xml (~> 0.6)
escape_utils (~> 1.3) escape_utils (~> 1.3)

@ -29,20 +29,20 @@
# This controller handles OAuth2 Authorization Code Grant redirects from a Authorization Server to # This controller handles OAuth2 Authorization Code Grant redirects from a Authorization Server to
# "callback" endpoint. # "callback" endpoint.
class OAuthClientsController < ApplicationController class OAuthClientsController < ApplicationController
before_action :set_oauth_state
before_action :find_oauth_client before_action :find_oauth_client
before_action :set_redirect_uri before_action :set_redirect_uri
before_action :set_code before_action :set_code
before_action :set_connection_manager before_action :set_connection_manager
after_action :clear_oauth_state_cookie
# Provide the OAuth2 "callback" endpoint. # Provide the OAuth2 "callback" endpoint.
# The Authorization Server redirects # The Authorization Server redirects
# here after successful authentication and authorization. # here after successful authentication and authorization.
# This endpoint gets a "code" parameter that cryptographically # This endpoint gets a "code" parameter that cryptographically
# contains a grant. # contains a grant.
# We get here by a URL like this:
# http://localhost:4200/oauth_clients/asdf12341234qsdfasdfasdf/callback?
# state=http%3A%2F%2Flocalhost%3A4200%2Fprojects%2Fdemo-project%2Foauth2_example&
# code=MQoOnUTJGFdAo5jBGD1SqnDH0PV6yioG7NoYM2zZZlK3g6LuKrGUmOxjIS1bIy7fHEfZy2WrgYcx
def callback def callback
# Exchange the code with a token using a HTTP call to the Authorization Server # Exchange the code with a token using a HTTP call to the Authorization Server
service_result = @connection_manager.code_to_token(@code) service_result = @connection_manager.code_to_token(@code)
@ -65,6 +65,14 @@ class OAuthClientsController < ApplicationController
private private
def set_oauth_state
@oauth_state = params[:state]
end
def clear_oauth_state_cookie
cookies.delete("oauth_state_#{@oauth_state}") unless @oauth_state.nil?
end
def set_oauth_errors(service_result) def set_oauth_errors(service_result)
flash[:error] = ["#{t(:'oauth_client.errors.oauth_authorization_code_grant_had_errors')}:"] flash[:error] = ["#{t(:'oauth_client.errors.oauth_authorization_code_grant_had_errors')}:"]
service_result.errors.each do |error| service_result.errors.each do |error|
@ -94,7 +102,7 @@ class OAuthClientsController < ApplicationController
# redirect_uri is used by OpenProject to redirect to # redirect_uri is used by OpenProject to redirect to
# after receiving an OAuth2 access token. So it should not be blank. # after receiving an OAuth2 access token. So it should not be blank.
service_result = ::OAuthClients::RedirectUriFromStateService service_result = ::OAuthClients::RedirectUriFromStateService
.new(state: params[:state], cookies:) .new(state: @oauth_state, cookies:)
.call .call
if service_result.success? if service_result.success?
@ -153,7 +161,7 @@ class OAuthClientsController < ApplicationController
def get_redirect_uri def get_redirect_uri
::OAuthClients::RedirectUriFromStateService ::OAuthClients::RedirectUriFromStateService
.new(state: params[:state], cookies:) .new(state: @oauth_state, cookies:)
.call .call
.result .result
end end

@ -28,30 +28,45 @@
require "rack/oauth2" require "rack/oauth2"
require "uri/http" require "uri/http"
require 'dry/monads'
require 'dry/monads/do'
module OAuthClients module OAuthClients
class RedirectUriFromStateService class RedirectUriFromStateService
include Dry::Monads[:maybe]
include Dry::Monads::Do.for(:process)
def initialize(state:, cookies:) def initialize(state:, cookies:)
@state = state @state = Maybe(state)
@cookies = cookies @cookies = cookies
end end
def call def call
redirect_uri = oauth_state_cookie process(@cookies, @state)
.fmap { |uri| ServiceResult.success(result: uri) }
if redirect_uri.present? && ::API::V3::Utilities::PathHelper::ApiV3Path::same_origin?(redirect_uri) .value_or(ServiceResult.failure)
ServiceResult.success(result: redirect_uri)
else
ServiceResult.failure
end
end end
private private
def oauth_state_cookie def process(cookies, state)
return nil if @state.blank? state_key = yield state
uri = yield callback_uri_from_cookies(cookies, "oauth_state_#{state_key}")
callback_uri = yield validate_callback_uri(uri)
Some(callback_uri)
end
def callback_uri_from_cookies(cookies, name)
Maybe(cookies[name])
end
@cookies["oauth_state_#{@state}"] def validate_callback_uri(uri)
if ::API::V3::Utilities::PathHelper::ApiV3Path::same_origin?(uri)
Some(uri)
else
None()
end
end end
end end
end end

Loading…
Cancel
Save