omniauth-oauth2 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.md +1 -1
- data/README.md +8 -12
- data/SECURITY.md +4 -10
- data/lib/omniauth/strategies/oauth2.rb +113 -36
- data/lib/omniauth-oauth2/version.rb +1 -1
- data/sig/omniauth-oauth2.rbs +127 -0
- metadata +18 -39
- data/.github/FUNDING.yml +0 -2
- data/.github/workflows/main.yml +0 -47
- data/.gitignore +0 -18
- data/.rspec +0 -2
- data/.rubocop.yml +0 -80
- data/CHANGELOG.md +0 -9
- data/Gemfile +0 -20
- data/Rakefile +0 -19
- data/omniauth-oauth2.gemspec +0 -24
- data/spec/helper.rb +0 -35
- data/spec/omniauth/strategies/oauth2_spec.rb +0 -198
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 56ea3a5f282231d2b4dbfc7393ab753608e47be23a5759dbe2b0bb5865a04b30
|
|
4
|
+
data.tar.gz: e3ed45d971bafe000603a58ef6221be463d08b6649065c6a6bb1de908d87fb64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b326c3f87b4fdee92bcf1bd41c36378f91a3a9a6678461c16f9dcec95f995eaebd40808e1d9262d485ced7cfab49007476becdc3a94e49cb1b1411f884f6723
|
|
7
|
+
data.tar.gz: b847b133de1f64cf23bae72b92e5453c0fc9846248bdd8eee71b9197632cdbdbaaaab36bc6f7792a257d20132205206619227dfc4accdc0e869bf8a4edb16c07
|
data/LICENSE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (
|
|
1
|
+
Copyright (c) 2011-2026 Michael Bleigh, Erik Berlin and Intridea, Inc.
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
4
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# OmniAuth OAuth2
|
|
2
2
|
|
|
3
3
|
[][gem]
|
|
4
|
-
[][githubactions]
|
|
5
|
+
[][qlty]
|
|
6
|
+
[][coveralls]
|
|
7
7
|
|
|
8
8
|
[gem]: https://rubygems.org/gems/omniauth-oauth2
|
|
9
|
-
[
|
|
10
|
-
[
|
|
9
|
+
[githubactions]: https://github.com/omniauth/omniauth-oauth2/actions/workflows/main.yml
|
|
10
|
+
[qlty]: https://qlty.sh/gh/omniauth/projects/omniauth-oauth2
|
|
11
|
+
[coveralls]: https://coveralls.io/r/omniauth/omniauth-oauth2
|
|
11
12
|
|
|
12
13
|
This gem contains a generic OAuth2 strategy for OmniAuth. It is meant to serve
|
|
13
14
|
as a building block strategy for other strategies and not to be used
|
|
@@ -65,12 +66,7 @@ end
|
|
|
65
66
|
|
|
66
67
|
That's pretty much it!
|
|
67
68
|
|
|
68
|
-
## OmniAuth-OAuth2 for Enterprise
|
|
69
|
-
|
|
70
|
-
Available as part of the Tidelift Subscription.
|
|
71
|
-
|
|
72
|
-
The maintainers of OmniAuth-OAuth2 and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/rubygems-omniauth-oauth2?utm_source=undefined&utm_medium=referral&utm_campaign=enterprise)
|
|
73
|
-
|
|
74
69
|
## Supported Ruby Versions
|
|
75
70
|
|
|
76
|
-
OmniAuth is tested
|
|
71
|
+
OmniAuth OAuth2 is tested on Ruby 2.5 through 4.0, JRuby, and TruffleRuby,
|
|
72
|
+
on Linux and macOS.
|
data/SECURITY.md
CHANGED
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported Versions
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
currently being supported with security updates.
|
|
7
|
-
|
|
8
|
-
| Version | Supported |
|
|
9
|
-
| ------- | ------------------ |
|
|
10
|
-
| 1.7.x | :white_check_mark: |
|
|
11
|
-
| <= 1.6.x | :x: |
|
|
5
|
+
Only the most recent release of omniauth-oauth2 receives security updates.
|
|
12
6
|
|
|
13
7
|
## Security contact information
|
|
14
8
|
|
|
15
|
-
To report a security vulnerability, please use
|
|
16
|
-
[
|
|
17
|
-
|
|
9
|
+
To report a security vulnerability, please use GitHub's
|
|
10
|
+
[private vulnerability reporting](https://github.com/omniauth/omniauth-oauth2/security/advisories/new)
|
|
11
|
+
rather than opening a public issue.
|
|
@@ -15,9 +15,14 @@ module OmniAuth
|
|
|
15
15
|
include OmniAuth::Strategy
|
|
16
16
|
|
|
17
17
|
def self.inherited(subclass)
|
|
18
|
-
|
|
18
|
+
Strategy.included(subclass)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# How many sign-ins can be in progress at once, for example in several
|
|
22
|
+
# tabs. Starting another drops the oldest. Each one adds about 300
|
|
23
|
+
# bytes to the session, which may be a 4 KB cookie.
|
|
24
|
+
MAX_STORED_SIGN_INS = 5
|
|
25
|
+
|
|
21
26
|
args %i[client_id client_secret]
|
|
22
27
|
|
|
23
28
|
option :client_id, nil
|
|
@@ -30,7 +35,6 @@ module OmniAuth
|
|
|
30
35
|
option :auth_token_params, {}
|
|
31
36
|
option :provider_ignores_state, false
|
|
32
37
|
option :pkce, false
|
|
33
|
-
option :pkce_verifier, nil
|
|
34
38
|
option :pkce_options, {
|
|
35
39
|
:code_challenge => proc { |verifier|
|
|
36
40
|
Base64.urlsafe_encode64(
|
|
@@ -48,48 +52,67 @@ module OmniAuth
|
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
credentials do
|
|
51
|
-
hash = {"token" => access_token.token}
|
|
52
|
-
hash["refresh_token"] = access_token.refresh_token if access_token.
|
|
55
|
+
hash = {"token" => access_token.token} #: Hash[String, String | Integer | bool | nil]
|
|
56
|
+
hash["refresh_token"] = access_token.refresh_token if access_token.refresh_token
|
|
53
57
|
hash["expires_at"] = access_token.expires_at if access_token.expires?
|
|
54
58
|
hash["expires"] = access_token.expires?
|
|
55
59
|
hash
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
def request_phase
|
|
63
|
+
# The provider can't do anything with an authorize request that has no
|
|
64
|
+
# client ID, so fail here, where the cause is clear.
|
|
65
|
+
return fail!(:missing_client_id, Error.new("client_id is missing")) if client.id.to_s.empty?
|
|
66
|
+
|
|
59
67
|
redirect client.auth_code.authorize_url({:redirect_uri => callback_url}.merge(authorize_params))
|
|
60
68
|
end
|
|
61
69
|
|
|
62
|
-
def authorize_params
|
|
70
|
+
def authorize_params
|
|
63
71
|
options.authorize_params[:state] = SecureRandom.hex(24)
|
|
64
72
|
|
|
65
73
|
if OmniAuth.config.test_mode
|
|
66
|
-
@env ||= {}
|
|
67
|
-
|
|
74
|
+
test_env = (@env ||= {}) #: Rack::env
|
|
75
|
+
test_env["rack.session"] ||= {}
|
|
68
76
|
end
|
|
69
77
|
|
|
70
78
|
params = options.authorize_params
|
|
71
79
|
.merge(options_for("authorize"))
|
|
72
80
|
.merge(pkce_authorize_params)
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
state = params.fetch(:state)
|
|
83
|
+
# Reused in the token request, which must send the same redirect_uri.
|
|
84
|
+
# oauth2 gives a client_options redirect_uri precedence in the
|
|
85
|
+
# authorize URL, so it does here too.
|
|
86
|
+
redirect_uri = client.options[:redirect_uri] || params[:redirect_uri] || callback_url
|
|
87
|
+
session["omniauth.state"] = state
|
|
88
|
+
session["omniauth.redirect_uri"] = redirect_uri
|
|
89
|
+
store_sign_in(state.to_s, redirect_uri)
|
|
90
|
+
# Lets OmniAuth give the callback this sign-in's origin and params.
|
|
91
|
+
remember_sign_in(state.to_s)
|
|
76
92
|
|
|
77
93
|
params
|
|
78
94
|
end
|
|
79
95
|
|
|
96
|
+
# The state the provider sends back, which OmniAuth uses to find the
|
|
97
|
+
# origin and params that remember_sign_in stored.
|
|
98
|
+
def callback_sign_in_key
|
|
99
|
+
request.params["state"]
|
|
100
|
+
end
|
|
101
|
+
|
|
80
102
|
def token_params
|
|
81
103
|
options.token_params.merge(options_for("token")).merge(pkce_token_params)
|
|
82
104
|
end
|
|
83
105
|
|
|
84
|
-
def callback_phase
|
|
106
|
+
def callback_phase
|
|
107
|
+
# Take what the request phase stored, even if the callback fails.
|
|
108
|
+
stored_sign_in
|
|
85
109
|
error = request.params["error_reason"] || request.params["error"]
|
|
86
|
-
if
|
|
110
|
+
if csrf_detected?
|
|
87
111
|
fail!(:csrf_detected, CallbackError.new(:csrf_detected, "CSRF detected"))
|
|
88
112
|
elsif error
|
|
89
113
|
fail!(error, CallbackError.new(request.params["error"], request.params["error_description"] || request.params["error_reason"], request.params["error_uri"]))
|
|
90
114
|
else
|
|
91
|
-
self.access_token =
|
|
92
|
-
self.access_token = access_token.refresh! if access_token.expired?
|
|
115
|
+
self.access_token = fetch_access_token
|
|
93
116
|
super
|
|
94
117
|
end
|
|
95
118
|
rescue ::OAuth2::Error, CallbackError => e
|
|
@@ -102,57 +125,111 @@ module OmniAuth
|
|
|
102
125
|
|
|
103
126
|
protected
|
|
104
127
|
|
|
128
|
+
# Whether the callback's state is missing or doesn't match the one the
|
|
129
|
+
# request phase stored, unless the provider ignores state.
|
|
130
|
+
def csrf_detected?
|
|
131
|
+
return false if options.provider_ignores_state
|
|
132
|
+
|
|
133
|
+
state = request.params["state"].to_s
|
|
134
|
+
state.empty? || !secure_compare(state, stored_sign_in.fetch("state").to_s)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Stores the state, redirect URI, and PKCE verifier of a sign-in by its
|
|
138
|
+
# state, so several can be in progress at once.
|
|
139
|
+
def store_sign_in(state, redirect_uri)
|
|
140
|
+
return if state.empty?
|
|
141
|
+
|
|
142
|
+
sign_in = {"redirect_uri" => redirect_uri}
|
|
143
|
+
sign_in["pkce_verifier"] = session.fetch("omniauth.pkce.verifier") if options.pkce
|
|
144
|
+
sign_ins = stored_sign_ins
|
|
145
|
+
sign_ins.delete(state)
|
|
146
|
+
sign_ins[state] = sign_in
|
|
147
|
+
session["omniauth.oauth2"] = sign_ins.to_a.last(MAX_STORED_SIGN_INS).to_h
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# What the request phase stored for this callback's state, removed from
|
|
151
|
+
# the session. Without a stored sign-in for the state, it comes from the
|
|
152
|
+
# omniauth.state and omniauth.redirect_uri keys, which hold the latest
|
|
153
|
+
# sign-in and which provider gems and tests set directly.
|
|
154
|
+
def stored_sign_in
|
|
155
|
+
@stored_sign_in ||= begin
|
|
156
|
+
fallback = {"state" => session.delete("omniauth.state"), "redirect_uri" => session.delete("omniauth.redirect_uri")}
|
|
157
|
+
state = request.params["state"]
|
|
158
|
+
sign_ins = stored_sign_ins
|
|
159
|
+
key = sign_ins.keys.detect { |stored| secure_compare(stored, state) } if state.instance_of?(String)
|
|
160
|
+
sign_in = sign_ins.delete(key)
|
|
161
|
+
if sign_in
|
|
162
|
+
session["omniauth.oauth2"] = sign_ins
|
|
163
|
+
sign_in.merge("state" => key)
|
|
164
|
+
else
|
|
165
|
+
fallback
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# A copy of the stored sign-ins, which are changed by assigning the
|
|
171
|
+
# session key, not in place.
|
|
172
|
+
def stored_sign_ins
|
|
173
|
+
sign_ins = session["omniauth.oauth2"]
|
|
174
|
+
sign_ins.is_a?(Hash) ? sign_ins.dup : {}
|
|
175
|
+
end
|
|
176
|
+
|
|
105
177
|
def pkce_authorize_params
|
|
106
178
|
return {} unless options.pkce
|
|
107
179
|
|
|
108
|
-
|
|
180
|
+
# A fresh verifier for every authorization request, as RFC 7636
|
|
181
|
+
# requires: https://tools.ietf.org/html/rfc7636#section-4.1
|
|
182
|
+
verifier = SecureRandom.hex(64)
|
|
183
|
+
session["omniauth.pkce.verifier"] = verifier
|
|
109
184
|
|
|
110
185
|
# NOTE: see https://tools.ietf.org/html/rfc7636#appendix-A
|
|
111
186
|
{
|
|
112
|
-
:code_challenge => options.pkce_options
|
|
113
|
-
|
|
114
|
-
:code_challenge_method => options.pkce_options[:code_challenge_method],
|
|
187
|
+
:code_challenge => options.pkce_options.fetch(:code_challenge).call(verifier),
|
|
188
|
+
:code_challenge_method => options.pkce_options.fetch(:code_challenge_method),
|
|
115
189
|
}
|
|
116
190
|
end
|
|
117
191
|
|
|
118
192
|
def pkce_token_params
|
|
119
193
|
return {} unless options.pkce
|
|
120
194
|
|
|
121
|
-
|
|
195
|
+
verifier = session.delete("omniauth.pkce.verifier")
|
|
196
|
+
{:code_verifier => stored_sign_in.fetch("pkce_verifier", verifier)}
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# The access token from the callback's code, refreshed if it's already
|
|
200
|
+
# expired.
|
|
201
|
+
def fetch_access_token
|
|
202
|
+
token = build_access_token || raise(CallbackError.new(:invalid_credentials, "No access token"))
|
|
203
|
+
token.expired? ? token.refresh! : token
|
|
122
204
|
end
|
|
123
205
|
|
|
124
206
|
def build_access_token
|
|
125
207
|
verifier = request.params["code"]
|
|
126
|
-
|
|
208
|
+
redirect_uri = stored_sign_in.fetch("redirect_uri") || callback_url
|
|
209
|
+
client.auth_code.get_token(verifier, {:redirect_uri => redirect_uri}.merge(token_params.to_hash(:symbolize_keys => true)), deep_symbolize(options.auth_token_params))
|
|
127
210
|
end
|
|
128
211
|
|
|
129
212
|
def deep_symbolize(options)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
213
|
+
hash = {} #: Hash[Symbol, untyped]
|
|
214
|
+
options.each { |key, value| hash[key.to_sym] = value.is_a?(Hash) ? deep_symbolize(value) : value }
|
|
215
|
+
hash
|
|
133
216
|
end
|
|
134
217
|
|
|
135
218
|
def options_for(option)
|
|
136
|
-
hash = {}
|
|
137
|
-
options.
|
|
138
|
-
hash[key.to_sym] = if options
|
|
139
|
-
options
|
|
219
|
+
hash = {} #: Hash[Symbol, untyped]
|
|
220
|
+
options.public_send(:"#{option}_options").select { |key| options[key] }.each do |key|
|
|
221
|
+
hash[key.to_sym] = if options.fetch(key).respond_to?(:call)
|
|
222
|
+
options.fetch(key).call(env)
|
|
140
223
|
else
|
|
141
|
-
options
|
|
224
|
+
options.fetch(key)
|
|
142
225
|
end
|
|
143
226
|
end
|
|
144
227
|
hash
|
|
145
228
|
end
|
|
146
229
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
l = string_a.unpack "C#{string_a.bytesize}"
|
|
152
|
-
|
|
153
|
-
res = 0
|
|
154
|
-
string_b.each_byte { |byte| res |= byte ^ l.shift }
|
|
155
|
-
res.zero?
|
|
230
|
+
# constant-time comparison algorithm to prevent timing attacks
|
|
231
|
+
def secure_compare(string_a, string_b)
|
|
232
|
+
Rack::Utils.secure_compare(string_a, string_b)
|
|
156
233
|
end
|
|
157
234
|
|
|
158
235
|
# An error that is indicated in the OAuth 2.0 callback.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module OmniAuth
|
|
2
|
+
module OAuth2
|
|
3
|
+
VERSION: String
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
module Strategies
|
|
7
|
+
# An abstract OAuth 2.0 strategy. Provider strategies subclass it and
|
|
8
|
+
# set client_options (site, authorize_url, and token_url) for their
|
|
9
|
+
# provider.
|
|
10
|
+
class OAuth2
|
|
11
|
+
include Strategy
|
|
12
|
+
extend Strategy::ClassMethods
|
|
13
|
+
|
|
14
|
+
# The options this strategy adds to OmniAuth's. Strategy::Options
|
|
15
|
+
# answers them through Hashie::Mash's method_missing.
|
|
16
|
+
interface _Options
|
|
17
|
+
def client_id: () -> String?
|
|
18
|
+
def client_secret: () -> String?
|
|
19
|
+
def client_options: () -> Strategy::Options
|
|
20
|
+
def authorize_params: () -> Strategy::Options
|
|
21
|
+
# The options copied into the authorize params, when set
|
|
22
|
+
def authorize_options: () -> Array[Symbol | String]
|
|
23
|
+
def token_params: () -> Strategy::Options
|
|
24
|
+
# The options copied into the token params, when set
|
|
25
|
+
def token_options: () -> Array[Symbol | String]
|
|
26
|
+
def auth_token_params: () -> Strategy::Options
|
|
27
|
+
def provider_ignores_state: () -> bool
|
|
28
|
+
def pkce: () -> bool
|
|
29
|
+
# :code_challenge, a callable that takes the verifier, and
|
|
30
|
+
# :code_challenge_method
|
|
31
|
+
def pkce_options: () -> Strategy::Options
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Gives each subclass the class-level strategy DSL.
|
|
35
|
+
def self.inherited: (singleton(OAuth2) subclass) -> void
|
|
36
|
+
|
|
37
|
+
# The fetcher blocks run with the strategy instance as self.
|
|
38
|
+
def self.uid: () { () [self: instance] -> untyped } -> Proc
|
|
39
|
+
| () -> Proc?
|
|
40
|
+
|
|
41
|
+
def self.info: () { () [self: instance] -> Hash[untyped, untyped]? } -> Proc
|
|
42
|
+
| () -> Proc?
|
|
43
|
+
|
|
44
|
+
def self.extra: () { () [self: instance] -> Hash[untyped, untyped]? } -> Proc
|
|
45
|
+
| () -> Proc?
|
|
46
|
+
|
|
47
|
+
def self.credentials: () { () [self: instance] -> Hash[untyped, untyped]? } -> Proc
|
|
48
|
+
| () -> Proc?
|
|
49
|
+
|
|
50
|
+
def options: () -> (Strategy::Options & _Options)
|
|
51
|
+
|
|
52
|
+
# Set by the callback phase, which runs before the fetcher blocks that
|
|
53
|
+
# read it.
|
|
54
|
+
attr_accessor access_token: ::OAuth2::AccessToken
|
|
55
|
+
|
|
56
|
+
# How many sign-ins can be in progress at once. Starting another drops
|
|
57
|
+
# the oldest.
|
|
58
|
+
MAX_STORED_SIGN_INS: Integer
|
|
59
|
+
|
|
60
|
+
# What the request phase stored for a sign-in: "state",
|
|
61
|
+
# "redirect_uri", and, with PKCE, "pkce_verifier"
|
|
62
|
+
type sign_in = Hash[String, String?]
|
|
63
|
+
|
|
64
|
+
# The sign-in the callback phase took from the session.
|
|
65
|
+
@stored_sign_in: sign_in?
|
|
66
|
+
|
|
67
|
+
def client: () -> ::OAuth2::Client
|
|
68
|
+
|
|
69
|
+
def request_phase: () -> rack_response
|
|
70
|
+
|
|
71
|
+
def authorize_params: () -> Strategy::Options
|
|
72
|
+
|
|
73
|
+
# The state the provider sends back to the callback.
|
|
74
|
+
def callback_sign_in_key: () -> String?
|
|
75
|
+
|
|
76
|
+
def token_params: () -> Strategy::Options
|
|
77
|
+
|
|
78
|
+
def callback_phase: () -> rack_response
|
|
79
|
+
|
|
80
|
+
# These are protected in Ruby, which RBS has no way to say.
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
# Whether the callback's state is missing or doesn't match the one the
|
|
84
|
+
# request phase stored, unless the provider ignores state.
|
|
85
|
+
def csrf_detected?: () -> bool
|
|
86
|
+
|
|
87
|
+
# Stores the redirect URI and PKCE verifier of a sign-in by its state.
|
|
88
|
+
def store_sign_in: (String state, String redirect_uri) -> void
|
|
89
|
+
|
|
90
|
+
# What the request phase stored for the callback's state, removed from
|
|
91
|
+
# the session, or else the latest sign-in's state and redirect URI.
|
|
92
|
+
def stored_sign_in: () -> sign_in
|
|
93
|
+
|
|
94
|
+
# The sign-ins in progress, by state, oldest first.
|
|
95
|
+
def stored_sign_ins: () -> Hash[String, sign_in]
|
|
96
|
+
|
|
97
|
+
def pkce_authorize_params: () -> Hash[Symbol, untyped]
|
|
98
|
+
|
|
99
|
+
def pkce_token_params: () -> Hash[Symbol, String?]
|
|
100
|
+
|
|
101
|
+
def fetch_access_token: () -> ::OAuth2::AccessToken
|
|
102
|
+
|
|
103
|
+
# Nil when oauth2's raise_errors is off and the token response has no
|
|
104
|
+
# token.
|
|
105
|
+
def build_access_token: () -> ::OAuth2::AccessToken?
|
|
106
|
+
|
|
107
|
+
def deep_symbolize: (Hash[untyped, untyped] options) -> Hash[Symbol, untyped]
|
|
108
|
+
|
|
109
|
+
def options_for: (String option) -> Hash[Symbol, untyped]
|
|
110
|
+
|
|
111
|
+
# Compares two strings in constant time, to prevent timing attacks.
|
|
112
|
+
def secure_compare: (String string_a, String string_b) -> bool
|
|
113
|
+
|
|
114
|
+
# An error that is indicated in the OAuth 2.0 callback, such as a
|
|
115
|
+
# redirect_uri_mismatch.
|
|
116
|
+
class CallbackError < StandardError
|
|
117
|
+
attr_accessor error: String | Symbol
|
|
118
|
+
attr_accessor error_reason: String?
|
|
119
|
+
attr_accessor error_uri: String?
|
|
120
|
+
|
|
121
|
+
def initialize: (String | Symbol error, ?String? error_reason, ?String? error_uri) -> void
|
|
122
|
+
|
|
123
|
+
def message: () -> String
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
metadata
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-oauth2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
+
- Erik Berlin
|
|
7
8
|
- Michael Bleigh
|
|
8
|
-
- Erik Michaels-Ober
|
|
9
9
|
- Tom Milewski
|
|
10
|
-
autorequire:
|
|
11
10
|
bindir: bin
|
|
12
11
|
cert_chain: []
|
|
13
|
-
date:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: oauth2
|
|
@@ -38,59 +37,42 @@ dependencies:
|
|
|
38
37
|
requirements:
|
|
39
38
|
- - "~>"
|
|
40
39
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: '2.
|
|
40
|
+
version: '2.3'
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 2.3.1
|
|
42
44
|
type: :runtime
|
|
43
45
|
prerelease: false
|
|
44
46
|
version_requirements: !ruby/object:Gem::Requirement
|
|
45
47
|
requirements:
|
|
46
48
|
- - "~>"
|
|
47
49
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: '2.
|
|
49
|
-
-
|
|
50
|
-
name: bundler
|
|
51
|
-
requirement: !ruby/object:Gem::Requirement
|
|
52
|
-
requirements:
|
|
53
|
-
- - "~>"
|
|
54
|
-
- !ruby/object:Gem::Version
|
|
55
|
-
version: '2.0'
|
|
56
|
-
type: :development
|
|
57
|
-
prerelease: false
|
|
58
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
59
|
-
requirements:
|
|
60
|
-
- - "~>"
|
|
50
|
+
version: '2.3'
|
|
51
|
+
- - ">="
|
|
61
52
|
- !ruby/object:Gem::Version
|
|
62
|
-
version:
|
|
53
|
+
version: 2.3.1
|
|
63
54
|
description: An abstract OAuth2 strategy for OmniAuth.
|
|
64
55
|
email:
|
|
65
|
-
- michael@intridea.com
|
|
66
56
|
- sferik@gmail.com
|
|
57
|
+
- michael@intridea.com
|
|
67
58
|
- tmilewski@gmail.com
|
|
68
59
|
executables: []
|
|
69
60
|
extensions: []
|
|
70
61
|
extra_rdoc_files: []
|
|
71
62
|
files:
|
|
72
|
-
- ".github/FUNDING.yml"
|
|
73
|
-
- ".github/workflows/main.yml"
|
|
74
|
-
- ".gitignore"
|
|
75
|
-
- ".rspec"
|
|
76
|
-
- ".rubocop.yml"
|
|
77
|
-
- CHANGELOG.md
|
|
78
|
-
- Gemfile
|
|
79
63
|
- LICENSE.md
|
|
80
64
|
- README.md
|
|
81
|
-
- Rakefile
|
|
82
65
|
- SECURITY.md
|
|
83
66
|
- lib/omniauth-oauth2.rb
|
|
84
67
|
- lib/omniauth-oauth2/version.rb
|
|
85
68
|
- lib/omniauth/strategies/oauth2.rb
|
|
86
|
-
- omniauth-oauth2.
|
|
87
|
-
- spec/helper.rb
|
|
88
|
-
- spec/omniauth/strategies/oauth2_spec.rb
|
|
69
|
+
- sig/omniauth-oauth2.rbs
|
|
89
70
|
homepage: https://github.com/omniauth/omniauth-oauth2
|
|
90
71
|
licenses:
|
|
91
72
|
- MIT
|
|
92
|
-
metadata:
|
|
93
|
-
|
|
73
|
+
metadata:
|
|
74
|
+
changelog_uri: https://github.com/omniauth/omniauth-oauth2/blob/master/CHANGELOG.md
|
|
75
|
+
rubygems_mfa_required: 'true'
|
|
94
76
|
rdoc_options: []
|
|
95
77
|
require_paths:
|
|
96
78
|
- lib
|
|
@@ -98,17 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
98
80
|
requirements:
|
|
99
81
|
- - ">="
|
|
100
82
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: '
|
|
83
|
+
version: '2.5'
|
|
102
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
85
|
requirements:
|
|
104
86
|
- - ">="
|
|
105
87
|
- !ruby/object:Gem::Version
|
|
106
88
|
version: '0'
|
|
107
89
|
requirements: []
|
|
108
|
-
rubygems_version:
|
|
109
|
-
signing_key:
|
|
90
|
+
rubygems_version: 4.0.20
|
|
110
91
|
specification_version: 4
|
|
111
92
|
summary: An abstract OAuth2 strategy for OmniAuth.
|
|
112
|
-
test_files:
|
|
113
|
-
- spec/helper.rb
|
|
114
|
-
- spec/omniauth/strategies/oauth2_spec.rb
|
|
93
|
+
test_files: []
|
data/.github/FUNDING.yml
DELETED
data/.github/workflows/main.yml
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
name: Ruby
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ master ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ master ]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
runs-on: ${{ matrix.os }}
|
|
12
|
-
timeout-minutes: 30
|
|
13
|
-
strategy:
|
|
14
|
-
fail-fast: false
|
|
15
|
-
matrix:
|
|
16
|
-
os: [ubuntu-latest, macos-latest]
|
|
17
|
-
ruby: [2.5, 2.6, 2.7, '3.0', 3.1, 3.2, head, debug, truffleruby, truffleruby-head, jruby, jruby-head]
|
|
18
|
-
steps:
|
|
19
|
-
- uses: actions/checkout@v3
|
|
20
|
-
- name: Set up Ruby
|
|
21
|
-
uses: ruby/setup-ruby@v1
|
|
22
|
-
with:
|
|
23
|
-
ruby-version: ${{ matrix.ruby }}
|
|
24
|
-
bundler-cache: true
|
|
25
|
-
- name: Set JRUBY_OPTS environment variable
|
|
26
|
-
run: echo "JRUBY_OPTS=--debug" >> "$GITHUB_ENV"
|
|
27
|
-
if: ${{ startsWith(matrix.ruby, 'jruby') }}
|
|
28
|
-
- name: Run tests
|
|
29
|
-
run: bundle exec rake
|
|
30
|
-
- uses: actions/upload-artifact@v3
|
|
31
|
-
if: ${{ matrix.os == 'ubuntu-latest' && matrix.ruby == '3.0' }}
|
|
32
|
-
with:
|
|
33
|
-
name: coverage
|
|
34
|
-
path: coverage/
|
|
35
|
-
retention-days: 1
|
|
36
|
-
|
|
37
|
-
coveralls:
|
|
38
|
-
needs: test
|
|
39
|
-
runs-on: ubuntu-latest
|
|
40
|
-
timeout-minutes: 30
|
|
41
|
-
steps:
|
|
42
|
-
- uses: actions/download-artifact@v3
|
|
43
|
-
with:
|
|
44
|
-
name: coverage
|
|
45
|
-
path: coverage/
|
|
46
|
-
- name: Coveralls GitHub Action
|
|
47
|
-
uses: coverallsapp/github-action@v2
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
NewCops: enable
|
|
3
|
-
|
|
4
|
-
Gemspec/RequiredRubyVersion:
|
|
5
|
-
Enabled: false
|
|
6
|
-
|
|
7
|
-
Layout/AccessModifierIndentation:
|
|
8
|
-
EnforcedStyle: outdent
|
|
9
|
-
|
|
10
|
-
Layout/LineLength:
|
|
11
|
-
AllowURI: true
|
|
12
|
-
Enabled: false
|
|
13
|
-
|
|
14
|
-
Layout/SpaceInsideHashLiteralBraces:
|
|
15
|
-
EnforcedStyle: no_space
|
|
16
|
-
|
|
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
|
-
|
|
27
|
-
Metrics/BlockNesting:
|
|
28
|
-
Max: 2
|
|
29
|
-
|
|
30
|
-
Metrics/ClassLength:
|
|
31
|
-
Max: 110
|
|
32
|
-
|
|
33
|
-
Metrics/MethodLength:
|
|
34
|
-
CountComments: false
|
|
35
|
-
Max: 10
|
|
36
|
-
|
|
37
|
-
Metrics/ParameterLists:
|
|
38
|
-
Max: 4
|
|
39
|
-
CountKeywordArgs: true
|
|
40
|
-
|
|
41
|
-
Naming/FileName:
|
|
42
|
-
Exclude:
|
|
43
|
-
- lib/omniauth-oauth2.rb
|
|
44
|
-
|
|
45
|
-
Style/CollectionMethods:
|
|
46
|
-
PreferredMethods:
|
|
47
|
-
map: 'collect'
|
|
48
|
-
reduce: 'inject'
|
|
49
|
-
find: 'detect'
|
|
50
|
-
find_all: 'select'
|
|
51
|
-
|
|
52
|
-
Style/Documentation:
|
|
53
|
-
Enabled: false
|
|
54
|
-
|
|
55
|
-
Style/DoubleNegation:
|
|
56
|
-
Enabled: false
|
|
57
|
-
|
|
58
|
-
Style/ExpandPathArguments:
|
|
59
|
-
Enabled: false
|
|
60
|
-
|
|
61
|
-
Style/FrozenStringLiteralComment:
|
|
62
|
-
Enabled: false
|
|
63
|
-
|
|
64
|
-
Style/HashSyntax:
|
|
65
|
-
EnforcedStyle: hash_rockets
|
|
66
|
-
|
|
67
|
-
Style/StderrPuts:
|
|
68
|
-
Enabled: false
|
|
69
|
-
|
|
70
|
-
Style/StringLiterals:
|
|
71
|
-
EnforcedStyle: double_quotes
|
|
72
|
-
|
|
73
|
-
Style/TrailingCommaInArguments:
|
|
74
|
-
EnforcedStyleForMultiline: comma
|
|
75
|
-
|
|
76
|
-
Style/TrailingCommaInHashLiteral:
|
|
77
|
-
EnforcedStyleForMultiline: comma
|
|
78
|
-
|
|
79
|
-
Style/TrailingCommaInArrayLiteral:
|
|
80
|
-
EnforcedStyleForMultiline: comma
|
data/CHANGELOG.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
## [v1.9.0](https://github.com/omniauth/omniauth-oauth2/releases/tag/v1.9.0)
|
|
2
|
-
- Prevent timing attacks [#174](https://github.com/omniauth/omniauth-oauth2/pull/174)
|
|
3
|
-
- Rescue OAuth2 timeouts [#169](https://github.com/omniauth/omniauth-oauth2/pull/169)
|
|
4
|
-
|
|
5
|
-
## [v1.8.0](https://github.com/omniauth/omniauth-oauth2/releases/tag/v1.8.0)
|
|
6
|
-
- Relaxes allowed versions of the oauth2 gem. [#146](https://github.com/omniauth/omniauth-oauth2/pull/146)
|
|
7
|
-
- Requires omniauth `~> 2.0` [#152](https://github.com/omniauth/omniauth-oauth2/pull/152)
|
|
8
|
-
|
|
9
|
-
Please see https://github.com/omniauth/omniauth-oauth2/releases for changelog prior to 1.8.0
|
data/Gemfile
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
source "https://rubygems.org"
|
|
2
|
-
|
|
3
|
-
gem "rake", "~> 13.0"
|
|
4
|
-
|
|
5
|
-
group :test do
|
|
6
|
-
gem "addressable", "~> 2.3.8", :platforms => %i[jruby ruby_18]
|
|
7
|
-
gem 'coveralls_reborn', '~> 0.19.0', require: false
|
|
8
|
-
gem "json", :platforms => %i[jruby ruby_18 ruby_19]
|
|
9
|
-
gem "mime-types", "~> 1.25", :platforms => %i[jruby ruby_18]
|
|
10
|
-
gem "rack-test"
|
|
11
|
-
gem "rest-client", "~> 1.8.0", :platforms => %i[jruby ruby_18]
|
|
12
|
-
gem "rspec", "~> 3.2"
|
|
13
|
-
gem "rubocop", ">= 0.51", :platforms => %i[ruby_19 ruby_20 ruby_21 ruby_22 ruby_23 ruby_24]
|
|
14
|
-
gem 'simplecov-lcov'
|
|
15
|
-
gem 'tins', '~> 1.13', :platforms => %i[jruby_18 jruby_19 ruby_19]
|
|
16
|
-
gem "webmock", "~> 3.0"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Specify your gem's dependencies in omniauth-oauth2.gemspec
|
|
20
|
-
gemspec
|
data/Rakefile
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env rake
|
|
2
|
-
|
|
3
|
-
require "bundler/gem_tasks"
|
|
4
|
-
require "rspec/core/rake_task"
|
|
5
|
-
|
|
6
|
-
RSpec::Core::RakeTask.new
|
|
7
|
-
|
|
8
|
-
task :test => :spec
|
|
9
|
-
|
|
10
|
-
begin
|
|
11
|
-
require "rubocop/rake_task"
|
|
12
|
-
RuboCop::RakeTask.new
|
|
13
|
-
rescue LoadError
|
|
14
|
-
task :rubocop do
|
|
15
|
-
$stderr.puts "RuboCop is disabled"
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
task :default => %i[spec rubocop]
|
data/omniauth-oauth2.gemspec
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
-
require "omniauth-oauth2/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |gem|
|
|
6
|
-
gem.add_dependency "oauth2", [">= 2.0.2", "< 3"]
|
|
7
|
-
gem.add_dependency "omniauth", "~> 2.0"
|
|
8
|
-
|
|
9
|
-
gem.add_development_dependency "bundler", "~> 2.0"
|
|
10
|
-
|
|
11
|
-
gem.authors = ["Michael Bleigh", "Erik Michaels-Ober", "Tom Milewski"]
|
|
12
|
-
gem.email = ["michael@intridea.com", "sferik@gmail.com", "tmilewski@gmail.com"]
|
|
13
|
-
gem.description = "An abstract OAuth2 strategy for OmniAuth."
|
|
14
|
-
gem.summary = gem.description
|
|
15
|
-
gem.homepage = "https://github.com/omniauth/omniauth-oauth2"
|
|
16
|
-
gem.licenses = %w[MIT]
|
|
17
|
-
|
|
18
|
-
gem.executables = `git ls-files -- bin/*`.split("\n").collect { |f| File.basename(f) }
|
|
19
|
-
gem.files = `git ls-files`.split("\n")
|
|
20
|
-
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
21
|
-
gem.name = "omniauth-oauth2"
|
|
22
|
-
gem.require_paths = %w[lib]
|
|
23
|
-
gem.version = OmniAuth::OAuth2::VERSION
|
|
24
|
-
end
|
data/spec/helper.rb
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
$LOAD_PATH.unshift File.expand_path("..", __FILE__)
|
|
2
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
|
3
|
-
|
|
4
|
-
if RUBY_VERSION >= "1.9"
|
|
5
|
-
require "simplecov"
|
|
6
|
-
require "simplecov-lcov"
|
|
7
|
-
require "coveralls"
|
|
8
|
-
|
|
9
|
-
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
|
|
10
|
-
|
|
11
|
-
SimpleCov.formatters = [
|
|
12
|
-
SimpleCov::Formatter::HTMLFormatter,
|
|
13
|
-
SimpleCov::Formatter::LcovFormatter,
|
|
14
|
-
Coveralls::SimpleCov::Formatter
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
SimpleCov.start do
|
|
18
|
-
minimum_coverage(78.48)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
require "rspec"
|
|
23
|
-
require "rack/test"
|
|
24
|
-
require "webmock/rspec"
|
|
25
|
-
require "omniauth"
|
|
26
|
-
require "omniauth-oauth2"
|
|
27
|
-
|
|
28
|
-
RSpec.configure do |config|
|
|
29
|
-
config.expect_with :rspec do |c|
|
|
30
|
-
c.syntax = :expect
|
|
31
|
-
end
|
|
32
|
-
config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
|
|
33
|
-
config.include Rack::Test::Methods
|
|
34
|
-
config.include WebMock::API
|
|
35
|
-
end
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
require "helper"
|
|
2
|
-
|
|
3
|
-
describe OmniAuth::Strategies::OAuth2 do
|
|
4
|
-
def app
|
|
5
|
-
lambda do |_env|
|
|
6
|
-
[200, {}, ["Hello."]]
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
let(:fresh_strategy) { Class.new(OmniAuth::Strategies::OAuth2) }
|
|
10
|
-
|
|
11
|
-
before do
|
|
12
|
-
OmniAuth.config.test_mode = true
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
after do
|
|
16
|
-
OmniAuth.config.test_mode = false
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
describe "Subclassing Behavior" do
|
|
20
|
-
subject { fresh_strategy }
|
|
21
|
-
|
|
22
|
-
it "performs the OmniAuth::Strategy included hook" do
|
|
23
|
-
expect(OmniAuth.strategies).to include(OmniAuth::Strategies::OAuth2)
|
|
24
|
-
expect(OmniAuth.strategies).to include(subject)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
describe "#client" do
|
|
29
|
-
subject { fresh_strategy }
|
|
30
|
-
|
|
31
|
-
it "is initialized with symbolized client_options" do
|
|
32
|
-
instance = subject.new(app, :client_options => {"authorize_url" => "https://example.com"})
|
|
33
|
-
expect(instance.client.options[:authorize_url]).to eq("https://example.com")
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it "sets ssl options as connection options" do
|
|
37
|
-
instance = subject.new(app, :client_options => {"ssl" => {"ca_path" => "foo"}})
|
|
38
|
-
expect(instance.client.options[:connection_opts][:ssl]).to eq(:ca_path => "foo")
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
describe "#authorize_params" do
|
|
43
|
-
subject { fresh_strategy }
|
|
44
|
-
|
|
45
|
-
it "includes any authorize params passed in the :authorize_params option" do
|
|
46
|
-
instance = subject.new("abc", "def", :authorize_params => {:foo => "bar", :baz => "zip"})
|
|
47
|
-
expect(instance.authorize_params["foo"]).to eq("bar")
|
|
48
|
-
expect(instance.authorize_params["baz"]).to eq("zip")
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "includes top-level options that are marked as :authorize_options" do
|
|
52
|
-
instance = subject.new("abc", "def", :authorize_options => %i[scope foo state], :scope => "bar", :foo => "baz")
|
|
53
|
-
expect(instance.authorize_params["scope"]).to eq("bar")
|
|
54
|
-
expect(instance.authorize_params["foo"]).to eq("baz")
|
|
55
|
-
expect(instance.authorize_params["state"]).not_to be_empty
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "includes random state in the authorize params" do
|
|
59
|
-
instance = subject.new("abc", "def")
|
|
60
|
-
expect(instance.authorize_params.keys).to eq(["state"])
|
|
61
|
-
expect(instance.session["omniauth.state"]).not_to be_empty
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it "includes custom state in the authorize params" do
|
|
65
|
-
instance = subject.new("abc", "def", :state => proc { "qux" })
|
|
66
|
-
expect(instance.authorize_params.keys).to eq(["state"])
|
|
67
|
-
expect(instance.session["omniauth.state"]).to eq("qux")
|
|
68
|
-
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
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
describe "#token_params" do
|
|
79
|
-
subject { fresh_strategy }
|
|
80
|
-
|
|
81
|
-
it "includes any authorize params passed in the :authorize_params option" do
|
|
82
|
-
instance = subject.new("abc", "def", :token_params => {:foo => "bar", :baz => "zip"})
|
|
83
|
-
expect(instance.token_params).to eq("foo" => "bar", "baz" => "zip")
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it "includes top-level options that are marked as :authorize_options" do
|
|
87
|
-
instance = subject.new("abc", "def", :token_options => %i[scope foo], :scope => "bar", :foo => "baz")
|
|
88
|
-
expect(instance.token_params).to eq("scope" => "bar", "foo" => "baz")
|
|
89
|
-
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
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
describe "#callback_phase" do
|
|
100
|
-
subject(:instance) { fresh_strategy.new("abc", "def") }
|
|
101
|
-
|
|
102
|
-
let(:params) { {"error_reason" => "user_denied", "error" => "access_denied", "state" => state} }
|
|
103
|
-
let(:state) { "secret" }
|
|
104
|
-
|
|
105
|
-
before do
|
|
106
|
-
allow(instance).to receive(:request) do
|
|
107
|
-
double("Request", :params => params)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
allow(instance).to receive(:session) do
|
|
111
|
-
double("Session", :delete => state)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
it "calls fail with the error received" do
|
|
116
|
-
expect(instance).to receive(:fail!).with("user_denied", anything)
|
|
117
|
-
|
|
118
|
-
instance.callback_phase
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "calls fail with the error received if state is missing and CSRF verification is disabled" do
|
|
122
|
-
params["state"] = nil
|
|
123
|
-
instance.options.provider_ignores_state = true
|
|
124
|
-
|
|
125
|
-
expect(instance).to receive(:fail!).with("user_denied", anything)
|
|
126
|
-
|
|
127
|
-
instance.callback_phase
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
it "calls fail with a CSRF error if the state is missing" do
|
|
131
|
-
params["state"] = nil
|
|
132
|
-
|
|
133
|
-
expect(instance).to receive(:fail!).with(:csrf_detected, anything)
|
|
134
|
-
instance.callback_phase
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
it "calls fail with a CSRF error if the state is invalid" do
|
|
138
|
-
params["state"] = "invalid"
|
|
139
|
-
|
|
140
|
-
expect(instance).to receive(:fail!).with(:csrf_detected, anything)
|
|
141
|
-
instance.callback_phase
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
describe 'exception handlings' do
|
|
145
|
-
let(:params) do
|
|
146
|
-
{"code" => "code", "state" => state}
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
before do
|
|
150
|
-
allow_any_instance_of(OmniAuth::Strategies::OAuth2).to receive(:build_access_token).and_raise(exception)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
{
|
|
154
|
-
:invalid_credentials => [OAuth2::Error, OmniAuth::Strategies::OAuth2::CallbackError],
|
|
155
|
-
:timeout => [Timeout::Error, Errno::ETIMEDOUT, OAuth2::TimeoutError, OAuth2::ConnectionError],
|
|
156
|
-
:failed_to_connect => [SocketError]
|
|
157
|
-
}.each do |error_type, exceptions|
|
|
158
|
-
exceptions.each do |klass|
|
|
159
|
-
context "when #{klass}" do
|
|
160
|
-
let(:exception) { klass.new 'error' }
|
|
161
|
-
|
|
162
|
-
it do
|
|
163
|
-
expect(instance).to receive(:fail!).with(error_type, exception)
|
|
164
|
-
instance.callback_phase
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
describe "#secure_compare" do
|
|
173
|
-
subject { fresh_strategy }
|
|
174
|
-
|
|
175
|
-
it "returns true when the two inputs are the same and false otherwise" do
|
|
176
|
-
instance = subject.new("abc", "def")
|
|
177
|
-
expect(instance.send(:secure_compare, "a", "a")).to be true
|
|
178
|
-
expect(instance.send(:secure_compare, "b", "a")).to be false
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
describe OmniAuth::Strategies::OAuth2::CallbackError do
|
|
184
|
-
let(:error) { Class.new(OmniAuth::Strategies::OAuth2::CallbackError) }
|
|
185
|
-
describe "#message" do
|
|
186
|
-
subject { error }
|
|
187
|
-
it "includes all of the attributes" do
|
|
188
|
-
instance = subject.new("error", "description", "uri")
|
|
189
|
-
expect(instance.message).to match(/error/)
|
|
190
|
-
expect(instance.message).to match(/description/)
|
|
191
|
-
expect(instance.message).to match(/uri/)
|
|
192
|
-
end
|
|
193
|
-
it "includes all of the attributes" do
|
|
194
|
-
instance = subject.new(nil, :symbol)
|
|
195
|
-
expect(instance.message).to eq("symbol")
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
end
|