Skip to content

Commit 4552c39

Browse files
committed
radis_sessionへの対応
1 parent 319ed46 commit 4552c39

File tree

1 file changed

+147
-4
lines changed

1 file changed

+147
-4
lines changed

lib/omniauth/strategies/cityos_dcp_login.rb

+147-4
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ class AgreementRequiredError < StandardError; end
99
option state: SecureRandom.hex(24)
1010
option :pkce, true
1111

12+
SRF_GUARD_COOKIE_NAME = 'xsrf_guard'.freeze
13+
1214
uid { raw_info["user_id"] }
1315

1416
info do
1517
{
16-
nickname: raw_info["nickname"],
18+
user_id: raw_info['user_id'],
1719
email: raw_info["user_email"],
1820
name: raw_info["nickname"]
1921
}
@@ -28,9 +30,38 @@ def raw_info
2830
end
2931

3032
def callback_phase
31-
super
33+
34+
if cookie_store?
35+
super
36+
end
37+
38+
error = request.params["error_reason"] || request.params["error"]
39+
40+
# エラーチェック
41+
if error
42+
fail!(error, CallbackError.new(request.params["error"], request.params["error_description"] || request.params["error_reason"], request.params["error_uri"]))
43+
elsif request.params['code']
44+
super
45+
elsif request.cookies[SRF_GUARD_COOKIE_NAME] && session[:csrf_guard]
46+
47+
# オプトインフローでのCSRFチェック
48+
handle_opt_in_flow_with_csrf_check
49+
OmniAuth::Strategy.instance_method(:callback_phase).bind(self).call
50+
else
51+
# 期待されるパラメータがない場合のエラー処理
52+
fail!(:invalid_request, CallbackError.new(:invalid_request, "Invalid request in callback"))
53+
end
3254
rescue AgreementRequiredError => e
33-
return redirect(e.message)
55+
if cookie_store?
56+
return redirect(e.message)
57+
end
58+
return redirect_with_csrf_token(e.message)
59+
rescue ::OAuth2::Error, CallbackError => e
60+
fail!(:invalid_credentials, e)
61+
rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
62+
fail!(:timeout, e)
63+
rescue ::SocketError => e
64+
fail!(:failed_to_connect, e)
3465
end
3566

3667
def setup_phase
@@ -83,8 +114,15 @@ def user_info_opt
83114
}
84115
}.to_json
85116
).parsed
117+
session["access_token"] = access_token.token
86118
if @id_token_payload.agreeFlg == "0" then
87-
agreement_url = "https://#{options.optin_url}?serviceId=#{options.service_id}&redirectUrl=#{CGI.escape(agreement_callback_url)}"
119+
redirect_url = nil
120+
if cookie_store?
121+
redirect_url = agreement_callback_url
122+
else
123+
redirect_url = callback_url
124+
end
125+
agreement_url = "https://#{options.optin_url}?serviceId=#{options.service_id}&redirectUrl=#{CGI.escape(redirect_url)}"
88126
raise AgreementRequiredError, agreement_url
89127
else
90128
Rails.logger.info("token:#{@id_token_payload.inspect}")
@@ -106,6 +144,111 @@ def site_url
106144
return parts[0] + '.com' if parts.size > 1
107145
url
108146
end
147+
148+
def handle_opt_in_flow_with_csrf_check
149+
csrf_token = request.cookies[SRF_GUARD_COOKIE_NAME]
150+
151+
if csrf_token.nil? || csrf_token != session[:csrf_guard]
152+
fail!(:csrf_detected, CallbackError.new(:csrf_detected, "CSRF detected"))
153+
else
154+
# CSRFトークンが一致した場合、セッションとクッキーからCSRFトークンを削除
155+
session.delete(:csrf_guard)
156+
session.delete("omniauth.pkce.verifier")
157+
request.cookies.delete(SRF_GUARD_COOKIE_NAME)
158+
# オプトイン承認後に必要な処理を行う
159+
request.params['state'] = csrf_token
160+
session['omniauth.state'] = csrf_token
161+
Rails.logger.debug "Current sessions: #{session.to_hash.inspect}"
162+
self.access_token = build_access_token_class session["access_token"]
163+
end
164+
end
165+
166+
def redirect_with_csrf_token(uri, options = {})
167+
r = Rack::Response.new
168+
169+
# URIからパスを抽出
170+
begin
171+
parsed_uri = URI(uri)
172+
path = parsed_uri.path.empty? ? '/' : parsed_uri.path
173+
domain = extract_domain(parsed_uri.host)
174+
rescue URI::InvalidURIError
175+
# 無効なURIの場合のエラー処理
176+
Rails.logger.error "Invalid URI provided: #{uri}"
177+
raise ArgumentError, "Invalid URI provided: #{uri}"
178+
end
179+
180+
181+
csrf_token = SecureRandom.urlsafe_base64(32)
182+
session[:csrf_guard] = csrf_token
183+
184+
r.set_cookie(SRF_GUARD_COOKIE_NAME, {
185+
value: csrf_token,
186+
expires: 5.minutes.from_now,
187+
secure: true,
188+
http_only: true,
189+
same_site: :none # クロスサイトリクエストを許可する場合
190+
})
191+
192+
Rails.logger.debug "Cookies after setting:"
193+
Rails.logger.debug r.headers["Set-Cookie"]
194+
195+
if options[:iframe]
196+
r.write("<script type='text/javascript' charset='utf-8'>top.location.href = '#{uri.to_json}';</script>")
197+
else
198+
# r.write("Redirecting to #{uri}...")
199+
r.redirect(uri)
200+
end
201+
202+
r.finish
203+
end
204+
205+
def extract_domain(host)
206+
return nil if host.nil?
207+
208+
parts = host.split('.')
209+
return host if parts.length <= 2
210+
211+
# サブドメインを除去し、メインドメインとTLDを返す
212+
parts.slice(-2, 2).join('.')
213+
end
214+
215+
def build_access_token_class(existing_token)
216+
::OAuth2::AccessToken.new(
217+
client,
218+
existing_token
219+
)
220+
end
221+
222+
def sanitize_nickname(nickname)
223+
# 正規表現パターン: 文字、数字、'-' および '_' のみを許可
224+
valid_pattern = /^[a-zA-Z0-9\-_]+$/
225+
226+
if nickname.present? && nickname.match?(valid_pattern)
227+
nickname
228+
else
229+
# ランダムな8文字の英数字を生成
230+
SecureRandom.alphanumeric(8)
231+
end
232+
end
233+
234+
def detect_session_store
235+
return false unless defined?(Rails)
236+
return false unless Rails.application
237+
238+
begin
239+
store = Rails.application.config.session_store
240+
return store if store
241+
false
242+
rescue
243+
false
244+
end
245+
end
246+
247+
def cookie_store?
248+
store = detect_session_store
249+
return true unless store # セッションが検出できない場合はcookie扱い
250+
store.to_s.include?('CookieStore')
251+
end
109252
end
110253
end
111254
end

0 commit comments

Comments
 (0)