File: authorization_code_request.rb

package info (click to toggle)
ruby-doorkeeper 5.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 992 kB
  • sloc: ruby: 4,644; makefile: 4
file content (124 lines) | stat: -rw-r--r-- 3,606 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# frozen_string_literal: true

module Doorkeeper
  module OAuth
    class AuthorizationCodeRequest < BaseRequest
      validate :params,       error: Errors::InvalidRequest
      validate :client,       error: Errors::InvalidClient
      validate :grant,        error: Errors::InvalidGrant
      # @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
      validate :redirect_uri, error: Errors::InvalidGrant
      validate :code_verifier, error: Errors::InvalidGrant

      attr_reader :grant, :client, :redirect_uri, :access_token, :code_verifier,
                  :invalid_request_reason, :missing_param

      def initialize(server, grant, client, parameters = {})
        @server = server
        @client = client
        @grant  = grant
        @grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE
        @redirect_uri = parameters[:redirect_uri]
        @code_verifier = parameters[:code_verifier]
      end

      private

      def before_successful_response
        grant.transaction do
          grant.lock!
          raise Errors::InvalidGrantReuse if grant.revoked?

          if Doorkeeper.config.revoke_previous_authorization_code_token?
            revoke_previous_tokens(grant.application, resource_owner)
          end

          grant.revoke

          find_or_create_access_token(
            client,
            resource_owner,
            grant.scopes,
            custom_token_attributes_with_data,
            server,
          )
        end

        super
      end

      def resource_owner
        if Doorkeeper.config.polymorphic_resource_owner?
          grant.resource_owner
        else
          grant.resource_owner_id
        end
      end

      def pkce_supported?
        Doorkeeper.config.access_grant_model.pkce_supported?
      end

      def validate_params
        @missing_param =
          if grant&.uses_pkce? && code_verifier.blank?
            :code_verifier
          elsif client && !client.confidential && Doorkeeper.config.force_pkce? && code_verifier.blank?
            :code_verifier
          elsif redirect_uri.blank?
            :redirect_uri
          end

        @missing_param.nil?
      end

      def validate_client
        client.present?
      end

      def validate_grant
        return false unless grant && grant.application_id == client.id

        grant.accessible?
      end

      def validate_redirect_uri
        Helpers::URIChecker.valid_for_authorization?(
          redirect_uri,
          grant.redirect_uri,
        )
      end

      # if either side (server or client) request PKCE, check the verifier
      # against the DB - if PKCE is supported
      def validate_code_verifier
        return true unless pkce_supported?
        return grant.code_challenge.blank? if code_verifier.blank?

        if grant.code_challenge_method == "S256"
          grant.code_challenge == generate_code_challenge(code_verifier)
        elsif grant.code_challenge_method == "plain"
          grant.code_challenge == code_verifier
        else
          false
        end
      end

      def generate_code_challenge(code_verifier)
        Doorkeeper.config.access_grant_model.generate_code_challenge(code_verifier)
      end

      def custom_token_attributes_with_data
        grant
          .attributes
          .with_indifferent_access
          .slice(*Doorkeeper.config.custom_access_token_attributes)
          .symbolize_keys
      end

      def revoke_previous_tokens(application, resource_owner)
        Doorkeeper.config.access_token_model.revoke_all_for(application.id, resource_owner)
      end
    end
  end
end