Skip to content

Commit b2f321d

Browse files
committed
Merge PR #131 from jessedoyle/pkce
2 parents 35bc27b + d414194 commit b2f321d

File tree

5 files changed

+94
-19
lines changed

5 files changed

+94
-19
lines changed

.rubocop.yml

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,34 @@
1+
AllCops:
2+
NewCops: enable
3+
4+
Gemspec/RequiredRubyVersion:
5+
Enabled: false
6+
17
Layout/AccessModifierIndentation:
28
EnforcedStyle: outdent
39

10+
Layout/LineLength:
11+
AllowURI: true
12+
Enabled: false
13+
414
Layout/SpaceInsideHashLiteralBraces:
515
EnforcedStyle: no_space
616

17+
Lint/MissingSuper:
18+
Enabled: false
19+
20+
Metrics/AbcSize:
21+
Max: 18
22+
23+
Metrics/BlockLength:
24+
Exclude:
25+
- spec/omniauth/strategies/oauth2_spec.rb
26+
727
Metrics/BlockNesting:
828
Max: 2
929

10-
Metrics/LineLength:
11-
AllowURI: true
12-
Enabled: false
30+
Metrics/ClassLength:
31+
Max: 110
1332

1433
Metrics/MethodLength:
1534
CountComments: false
@@ -19,6 +38,10 @@ Metrics/ParameterLists:
1938
Max: 4
2039
CountKeywordArgs: true
2140

41+
Naming/FileName:
42+
Exclude:
43+
- lib/omniauth-oauth2.rb
44+
2245
Style/CollectionMethods:
2346
PreferredMethods:
2447
map: 'collect'
@@ -35,6 +58,9 @@ Style/DoubleNegation:
3558
Style/ExpandPathArguments:
3659
Enabled: false
3760

61+
Style/FrozenStringLiteralComment:
62+
Enabled: false
63+
3864
Style/HashSyntax:
3965
EnforcedStyle: hash_rockets
4066

@@ -52,4 +78,3 @@ Style/TrailingCommaInHashLiteral:
5278

5379
Style/TrailingCommaInArrayLiteral:
5480
EnforcedStyleForMultiline: comma
55-

Rakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env rake
2+
23
require "bundler/gem_tasks"
34
require "rspec/core/rake_task"
45

lib/omniauth-oauth2.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
require "omniauth-oauth2/version" # rubocop:disable FileName
1+
require "omniauth-oauth2/version"
22
require "omniauth/strategies/oauth2"

lib/omniauth/strategies/oauth2.rb

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,22 @@ def self.inherited(subclass)
2424
option :client_secret, nil
2525
option :client_options, {}
2626
option :authorize_params, {}
27-
option :authorize_options, [:scope, :state]
27+
option :authorize_options, %i[scope state]
2828
option :token_params, {}
2929
option :token_options, []
3030
option :auth_token_params, {}
3131
option :provider_ignores_state, false
32+
option :pkce, false
33+
option :pkce_verifier, nil
34+
option :pkce_options, {
35+
:code_challenge => proc { |verifier|
36+
Base64.urlsafe_encode64(
37+
Digest::SHA2.digest(verifier),
38+
:padding => false,
39+
)
40+
},
41+
:code_challenge_method => "S256",
42+
}
3243

3344
attr_accessor :access_token
3445

@@ -48,22 +59,29 @@ def request_phase
4859
redirect client.auth_code.authorize_url({:redirect_uri => callback_url}.merge(authorize_params))
4960
end
5061

51-
def authorize_params
62+
def authorize_params# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
5263
options.authorize_params[:state] = SecureRandom.hex(24)
53-
params = options.authorize_params.merge(options_for("authorize"))
64+
5465
if OmniAuth.config.test_mode
5566
@env ||= {}
5667
@env["rack.session"] ||= {}
5768
end
69+
70+
params = options.authorize_params
71+
.merge(options_for("authorize"))
72+
.merge(pkce_authorize_params)
73+
74+
session["omniauth.pkce.verifier"] = options.pkce_verifier if options.pkce
5875
session["omniauth.state"] = params[:state]
76+
5977
params
6078
end
6179

6280
def token_params
63-
options.token_params.merge(options_for("token"))
81+
options.token_params.merge(options_for("token")).merge(pkce_token_params)
6482
end
6583

66-
def callback_phase # rubocop:disable AbcSize, CyclomaticComplexity, MethodLength, PerceivedComplexity
84+
def callback_phase # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
6785
error = request.params["error_reason"] || request.params["error"]
6886
if error
6987
fail!(error, CallbackError.new(request.params["error"], request.params["error_description"] || request.params["error_reason"], request.params["error_uri"]))
@@ -84,27 +102,44 @@ def callback_phase # rubocop:disable AbcSize, CyclomaticComplexity, MethodLength
84102

85103
protected
86104

105+
def pkce_authorize_params
106+
return {} unless options.pkce
107+
108+
options.pkce_verifier = SecureRandom.hex(64)
109+
110+
# NOTE: see https://tools.ietf.org/html/rfc7636#appendix-A
111+
{
112+
:code_challenge => options.pkce_options[:code_challenge]
113+
.call(options.pkce_verifier),
114+
:code_challenge_method => options.pkce_options[:code_challenge_method],
115+
}
116+
end
117+
118+
def pkce_token_params
119+
return {} unless options.pkce
120+
121+
{:code_verifier => session.delete("omniauth.pkce.verifier")}
122+
end
123+
87124
def build_access_token
88125
verifier = request.params["code"]
89126
client.auth_code.get_token(verifier, {:redirect_uri => callback_url}.merge(token_params.to_hash(:symbolize_keys => true)), deep_symbolize(options.auth_token_params))
90127
end
91128

92129
def deep_symbolize(options)
93-
hash = {}
94-
options.each do |key, value|
130+
options.each_with_object({}) do |(key, value), hash|
95131
hash[key.to_sym] = value.is_a?(Hash) ? deep_symbolize(value) : value
96132
end
97-
hash
98133
end
99134

100135
def options_for(option)
101136
hash = {}
102137
options.send(:"#{option}_options").select { |key| options[key] }.each do |key|
103138
hash[key.to_sym] = if options[key].respond_to?(:call)
104-
options[key].call(env)
105-
else
106-
options[key]
107-
end
139+
options[key].call(env)
140+
else
141+
options[key]
142+
end
108143
end
109144
hash
110145
end

spec/omniauth/strategies/oauth2_spec.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require "helper"
22

3-
describe OmniAuth::Strategies::OAuth2 do # rubocop:disable Metrics/BlockLength
3+
describe OmniAuth::Strategies::OAuth2 do
44
def app
55
lambda do |_env|
66
[200, {}, ["Hello."]]
@@ -62,10 +62,17 @@ def app
6262
end
6363

6464
it "includes custom state in the authorize params" do
65-
instance = subject.new("abc", "def", state: Proc.new { "qux" } )
65+
instance = subject.new("abc", "def", :state => proc { "qux" })
6666
expect(instance.authorize_params.keys).to eq(["state"])
6767
expect(instance.session["omniauth.state"]).to eq("qux")
6868
end
69+
70+
it "includes PKCE parameters if enabled" do
71+
instance = subject.new("abc", "def", :pkce => true)
72+
expect(instance.authorize_params[:code_challenge]).to be_a(String)
73+
expect(instance.authorize_params[:code_challenge_method]).to eq("S256")
74+
expect(instance.session["omniauth.pkce.verifier"]).to be_a(String)
75+
end
6976
end
7077

7178
describe "#token_params" do
@@ -80,6 +87,13 @@ def app
8087
instance = subject.new("abc", "def", :token_options => %i[scope foo], :scope => "bar", :foo => "baz")
8188
expect(instance.token_params).to eq("scope" => "bar", "foo" => "baz")
8289
end
90+
91+
it "includes the PKCE code_verifier if enabled" do
92+
instance = subject.new("abc", "def", :pkce => true)
93+
# setup session
94+
instance.authorize_params
95+
expect(instance.token_params[:code_verifier]).to be_a(String)
96+
end
8397
end
8498

8599
describe "#callback_phase" do

0 commit comments

Comments
 (0)