better_auth 0.8.0 → 0.11.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/CHANGELOG.md +92 -0
- data/LICENSE.md +1 -1
- data/README.md +142 -95
- data/lib/better_auth/adapters/base.rb +64 -1
- data/lib/better_auth/adapters/internal_adapter.rb +197 -11
- data/lib/better_auth/adapters/join_support.rb +10 -0
- data/lib/better_auth/adapters/memory.rb +303 -54
- data/lib/better_auth/adapters/mssql.rb +7 -3
- data/lib/better_auth/adapters/sql.rb +390 -63
- data/lib/better_auth/api.rb +3 -24
- data/lib/better_auth/configuration.rb +48 -47
- data/lib/better_auth/context.rb +43 -67
- data/lib/better_auth/cookies.rb +119 -15
- data/lib/better_auth/core.rb +0 -2
- data/lib/better_auth/crypto.rb +4 -3
- data/lib/better_auth/doctor.rb +108 -0
- data/lib/better_auth/endpoint.rb +88 -5
- data/lib/better_auth/error.rb +3 -1
- data/lib/better_auth/http_client.rb +46 -0
- data/lib/better_auth/logger.rb +1 -1
- data/lib/better_auth/middleware/origin_check.rb +9 -1
- data/lib/better_auth/migration_plan.rb +15 -0
- data/lib/better_auth/oauth2.rb +1 -1
- data/lib/better_auth/oauth_state.rb +147 -0
- data/lib/better_auth/plugin_loader.rb +220 -0
- data/lib/better_auth/plugins/access.rb +10 -13
- data/lib/better_auth/plugins/admin.rb +85 -29
- data/lib/better_auth/plugins/anonymous.rb +2 -0
- data/lib/better_auth/plugins/api_key.rb +4 -7
- data/lib/better_auth/plugins/bearer.rb +8 -22
- data/lib/better_auth/plugins/captcha.rb +24 -6
- data/lib/better_auth/plugins/device_authorization.rb +77 -5
- data/lib/better_auth/plugins/dub.rb +10 -2
- data/lib/better_auth/plugins/email_otp.rb +30 -47
- data/lib/better_auth/plugins/generic_oauth.rb +162 -140
- data/lib/better_auth/plugins/have_i_been_pwned.rb +1 -1
- data/lib/better_auth/plugins/i18n.rb +215 -0
- data/lib/better_auth/plugins/jwt.rb +44 -19
- data/lib/better_auth/plugins/magic_link.rb +18 -21
- data/lib/better_auth/plugins/oauth_popup.rb +330 -0
- data/lib/better_auth/plugins/oauth_protocol.rb +233 -46
- data/lib/better_auth/plugins/oauth_provider.rb +4 -7
- data/lib/better_auth/plugins/oauth_proxy.rb +1 -1
- data/lib/better_auth/plugins/one_tap.rb +78 -65
- data/lib/better_auth/plugins/one_time_token.rb +1 -4
- data/lib/better_auth/plugins/open_api.rb +77 -9
- data/lib/better_auth/plugins/organization.rb +546 -102
- data/lib/better_auth/plugins/passkey.rb +4 -7
- data/lib/better_auth/plugins/phone_number.rb +25 -27
- data/lib/better_auth/plugins/scim.rb +4 -7
- data/lib/better_auth/plugins/siwe.rb +126 -16
- data/lib/better_auth/plugins/sso.rb +4 -7
- data/lib/better_auth/plugins/stripe.rb +4 -7
- data/lib/better_auth/plugins/two_factor.rb +205 -19
- data/lib/better_auth/plugins/username.rb +56 -13
- data/lib/better_auth/plugins.rb +57 -0
- data/lib/better_auth/rate_limiter.rb +300 -133
- data/lib/better_auth/request_ip.rb +123 -13
- data/lib/better_auth/router.rb +3 -3
- data/lib/better_auth/routes/account.rb +44 -8
- data/lib/better_auth/routes/email_verification.rb +93 -6
- data/lib/better_auth/routes/password.rb +6 -6
- data/lib/better_auth/routes/session.rb +7 -2
- data/lib/better_auth/routes/sign_in.rb +10 -1
- data/lib/better_auth/routes/sign_up.rb +45 -22
- data/lib/better_auth/routes/social.rb +197 -54
- data/lib/better_auth/routes/user.rb +17 -10
- data/lib/better_auth/schema/sql.rb +189 -34
- data/lib/better_auth/schema.rb +159 -5
- data/lib/better_auth/session.rb +29 -10
- data/lib/better_auth/session_store.rb +1 -1
- data/lib/better_auth/social_providers/apple.rb +6 -1
- data/lib/better_auth/social_providers/atlassian.rb +1 -0
- data/lib/better_auth/social_providers/base.rb +45 -23
- data/lib/better_auth/social_providers/cognito.rb +75 -5
- data/lib/better_auth/social_providers/facebook.rb +95 -1
- data/lib/better_auth/social_providers/figma.rb +2 -0
- data/lib/better_auth/social_providers/github.rb +2 -0
- data/lib/better_auth/social_providers/google.rb +12 -1
- data/lib/better_auth/social_providers/line.rb +35 -1
- data/lib/better_auth/social_providers/linear.rb +1 -0
- data/lib/better_auth/social_providers/microsoft_entra_id.rb +9 -0
- data/lib/better_auth/social_providers/naver.rb +24 -1
- data/lib/better_auth/social_providers/notion.rb +1 -0
- data/lib/better_auth/social_providers/paybin.rb +25 -1
- data/lib/better_auth/social_providers/paypal.rb +118 -2
- data/lib/better_auth/social_providers/reddit.rb +25 -4
- data/lib/better_auth/social_providers/salesforce.rb +1 -0
- data/lib/better_auth/social_providers/twitch.rb +24 -1
- data/lib/better_auth/social_providers/twitter.rb +36 -1
- data/lib/better_auth/social_providers/vk.rb +34 -1
- data/lib/better_auth/social_providers/wechat.rb +6 -4
- data/lib/better_auth/sql_migration.rb +566 -0
- data/lib/better_auth/version.rb +1 -1
- data/lib/better_auth.rb +14 -41
- metadata +14 -18
- data/lib/better_auth/plugins/mcp/authorization.rb +0 -111
- data/lib/better_auth/plugins/mcp/config.rb +0 -51
- data/lib/better_auth/plugins/mcp/consent.rb +0 -31
- data/lib/better_auth/plugins/mcp/legacy_aliases.rb +0 -43
- data/lib/better_auth/plugins/mcp/metadata.rb +0 -81
- data/lib/better_auth/plugins/mcp/registration.rb +0 -31
- data/lib/better_auth/plugins/mcp/resource_handler.rb +0 -37
- data/lib/better_auth/plugins/mcp/schema.rb +0 -91
- data/lib/better_auth/plugins/mcp/token.rb +0 -108
- data/lib/better_auth/plugins/mcp/userinfo.rb +0 -37
- data/lib/better_auth/plugins/mcp.rb +0 -215
- data/lib/better_auth/plugins/oidc_provider.rb +0 -710
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module BetterAuth
|
|
7
|
+
module Plugins
|
|
8
|
+
OAUTH_POPUP_MESSAGE_TYPE = "better-auth:oauth-popup"
|
|
9
|
+
OAUTH_POPUP_DATA_ELEMENT_ID = "better-auth-oauth-popup"
|
|
10
|
+
OAUTH_POPUP_MARKER_COOKIE = "oauth_popup"
|
|
11
|
+
OAUTH_POPUP_ERROR_CODES = {
|
|
12
|
+
"POPUP_SIGN_IN_FAILED" => "Popup sign-in failed",
|
|
13
|
+
"POPUP_BLOCKED" => "Sign-in popup was blocked by the browser",
|
|
14
|
+
"POPUP_CLOSED" => "Sign-in popup was closed before completing",
|
|
15
|
+
"POPUP_TIMEOUT" => "Sign-in popup timed out"
|
|
16
|
+
}.freeze
|
|
17
|
+
OAUTH_POPUP_INTERNAL_STATE_KEYS = OAuthState::INTERNAL_KEYS
|
|
18
|
+
OAUTH_POPUP_COMPLETE_SCRIPT = [
|
|
19
|
+
"(function () {",
|
|
20
|
+
"\tvar el = document.getElementById(\"better-auth-oauth-popup\");",
|
|
21
|
+
"\tif (!el) return;",
|
|
22
|
+
"\tvar payload;",
|
|
23
|
+
"\ttry {",
|
|
24
|
+
"\t\tpayload = JSON.parse(el.textContent || \"\");",
|
|
25
|
+
"\t} catch (e) {",
|
|
26
|
+
"\t\treturn;",
|
|
27
|
+
"\t}",
|
|
28
|
+
"\tvar target = window.opener || window.parent;",
|
|
29
|
+
"\tif (target && target !== window) {",
|
|
30
|
+
"\t\ttry {",
|
|
31
|
+
"\t\t\ttarget.postMessage(",
|
|
32
|
+
"\t\t\t\t{",
|
|
33
|
+
"\t\t\t\t\ttype: payload.type,",
|
|
34
|
+
"\t\t\t\t\tnonce: payload.nonce,",
|
|
35
|
+
"\t\t\t\t\ttoken: payload.token,",
|
|
36
|
+
"\t\t\t\t\tredirectTo: payload.redirectTo,",
|
|
37
|
+
"\t\t\t\t\terror: payload.error,",
|
|
38
|
+
"\t\t\t\t},",
|
|
39
|
+
"\t\t\t\tpayload.targetOrigin,",
|
|
40
|
+
"\t\t\t);",
|
|
41
|
+
"\t\t} catch (e) {}",
|
|
42
|
+
"\t}",
|
|
43
|
+
"\twindow.close();",
|
|
44
|
+
"})();",
|
|
45
|
+
""
|
|
46
|
+
].join("\n")
|
|
47
|
+
OAUTH_POPUP_SCRIPT_CSP_HASH = "sha256-tIo2K8VBC9SnhvdZ+9GsGkQoZm+jm/JcxL+d+i8b8KQ="
|
|
48
|
+
|
|
49
|
+
module_function
|
|
50
|
+
|
|
51
|
+
def oauth_popup
|
|
52
|
+
Plugin.new(
|
|
53
|
+
id: "oauth-popup",
|
|
54
|
+
endpoints: {
|
|
55
|
+
oauth_popup_start: oauth_popup_start_endpoint
|
|
56
|
+
},
|
|
57
|
+
hooks: {
|
|
58
|
+
after: [
|
|
59
|
+
{
|
|
60
|
+
matcher: ->(ctx) { oauth_popup_callback_path?(ctx.path) },
|
|
61
|
+
handler: ->(ctx) { oauth_popup_after_callback(ctx) }
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
error_codes: OAUTH_POPUP_ERROR_CODES
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def oauth_popup_start_endpoint
|
|
70
|
+
Endpoint.new(
|
|
71
|
+
path: "/oauth-popup/start",
|
|
72
|
+
method: "GET",
|
|
73
|
+
query_schema: Routes.request_query_schema(
|
|
74
|
+
required_strings: %w[provider popupOrigin],
|
|
75
|
+
optional_strings: %w[popupNonce callbackURL errorCallbackURL newUserCallbackURL scopes requestSignUp additionalData]
|
|
76
|
+
),
|
|
77
|
+
metadata: {
|
|
78
|
+
hide: true,
|
|
79
|
+
openapi: {
|
|
80
|
+
parameters: [
|
|
81
|
+
{name: "provider", in: "query", required: true, schema: {type: "string"}},
|
|
82
|
+
{name: "popupOrigin", in: "query", required: true, schema: {type: "string"}},
|
|
83
|
+
{name: "popupNonce", in: "query", required: false, schema: {type: "string"}},
|
|
84
|
+
{name: "callbackURL", in: "query", required: false, schema: {type: "string"}},
|
|
85
|
+
{name: "errorCallbackURL", in: "query", required: false, schema: {type: "string"}},
|
|
86
|
+
{name: "newUserCallbackURL", in: "query", required: false, schema: {type: "string"}},
|
|
87
|
+
{name: "scopes", in: "query", required: false, schema: {type: "string"}},
|
|
88
|
+
{name: "requestSignUp", in: "query", required: false, schema: {type: "string"}},
|
|
89
|
+
{name: "additionalData", in: "query", required: false, schema: {type: "string"}}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
) do |ctx|
|
|
94
|
+
query = normalize_hash(ctx.query)
|
|
95
|
+
popup_origin = oauth_popup_validate_origin!(ctx, query[:popup_origin].to_s)
|
|
96
|
+
|
|
97
|
+
nonce = query[:popup_nonce].to_s
|
|
98
|
+
invalid_redirect = oauth_popup_invalid_redirect(ctx, query, popup_origin, nonce)
|
|
99
|
+
next invalid_redirect if invalid_redirect
|
|
100
|
+
|
|
101
|
+
provider_id = query[:provider].to_s
|
|
102
|
+
provider = Routes.social_provider(ctx.context, provider_id)
|
|
103
|
+
unless provider
|
|
104
|
+
next oauth_popup_completion(ctx, popup_origin, nonce: nonce, error: {
|
|
105
|
+
code: "provider_not_found",
|
|
106
|
+
description: "Unknown provider: #{provider_id}"
|
|
107
|
+
})
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
code_verifier = Crypto.random_string(128)
|
|
111
|
+
scopes = query[:scopes]&.to_s&.split(",")
|
|
112
|
+
additional_data = oauth_popup_additional_data(query[:additional_data])
|
|
113
|
+
callback_url = query[:callback_url].to_s
|
|
114
|
+
callback_url = ctx.context.base_url if callback_url.empty?
|
|
115
|
+
request_sign_up = (query[:request_sign_up].to_s == "true") ? true : nil
|
|
116
|
+
|
|
117
|
+
begin
|
|
118
|
+
authorization_url = if (start = fetch_value(provider, "oauthPopupAuthorizationUrl")).respond_to?(:call)
|
|
119
|
+
start.call(
|
|
120
|
+
ctx,
|
|
121
|
+
callbackURL: callback_url,
|
|
122
|
+
errorCallbackURL: query[:error_callback_url],
|
|
123
|
+
newUserCallbackURL: query[:new_user_callback_url],
|
|
124
|
+
requestSignUp: request_sign_up,
|
|
125
|
+
scopes: scopes,
|
|
126
|
+
additionalData: additional_data,
|
|
127
|
+
codeVerifier: code_verifier
|
|
128
|
+
)
|
|
129
|
+
else
|
|
130
|
+
state_data = additional_data.merge(
|
|
131
|
+
"callbackURL" => callback_url,
|
|
132
|
+
"errorURL" => query[:error_callback_url],
|
|
133
|
+
"newUserURL" => query[:new_user_callback_url],
|
|
134
|
+
"requestSignUp" => request_sign_up,
|
|
135
|
+
"codeVerifier" => code_verifier,
|
|
136
|
+
"expiresAt" => Time.now.to_i + 600
|
|
137
|
+
).compact
|
|
138
|
+
state = OAuthState.generate(ctx, state_data)
|
|
139
|
+
Routes.call_provider(provider, :create_authorization_url, {
|
|
140
|
+
state: state,
|
|
141
|
+
codeVerifier: code_verifier,
|
|
142
|
+
code_verifier: code_verifier,
|
|
143
|
+
redirectURI: "#{ctx.context.canonical_base_url}/callback/#{provider_id}",
|
|
144
|
+
redirect_uri: "#{ctx.context.canonical_base_url}/callback/#{provider_id}",
|
|
145
|
+
scopes: scopes
|
|
146
|
+
})
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
oauth_popup_set_marker(ctx, popup_origin, nonce)
|
|
150
|
+
rescue => error
|
|
151
|
+
oauth_popup_log(ctx.context, :error, "OAuth popup failed to start", error)
|
|
152
|
+
next oauth_popup_completion(ctx, popup_origin, nonce: nonce, error: {
|
|
153
|
+
code: "popup_sign_in_failed",
|
|
154
|
+
description: "Failed to start the OAuth flow."
|
|
155
|
+
})
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
raise ctx.redirect(authorization_url.to_s)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def oauth_popup_validate_origin!(ctx, popup_origin)
|
|
163
|
+
uri = URI.parse(popup_origin)
|
|
164
|
+
valid_scheme = %w[http https].include?(uri.scheme)
|
|
165
|
+
valid_path = uri.path.to_s.empty? || uri.path == "/"
|
|
166
|
+
absolute = valid_scheme && !uri.host.to_s.empty?
|
|
167
|
+
canonical = Configuration.origin_for(uri) if absolute
|
|
168
|
+
valid = absolute &&
|
|
169
|
+
uri.userinfo.nil? &&
|
|
170
|
+
valid_path &&
|
|
171
|
+
uri.query.nil? &&
|
|
172
|
+
uri.fragment.nil? &&
|
|
173
|
+
!popup_origin.match?(/[?*]/) &&
|
|
174
|
+
ctx.context.trusted_origin?(canonical, allow_relative_paths: false)
|
|
175
|
+
return canonical if valid
|
|
176
|
+
|
|
177
|
+
oauth_popup_log(ctx.context, :error, "OAuth popup origin is not trusted")
|
|
178
|
+
raise APIError.new("FORBIDDEN", code: "INVALID_ORIGIN", message: BASE_ERROR_CODES["INVALID_ORIGIN"])
|
|
179
|
+
rescue URI::InvalidURIError
|
|
180
|
+
raise APIError.new("FORBIDDEN", code: "INVALID_ORIGIN", message: BASE_ERROR_CODES["INVALID_ORIGIN"])
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def oauth_popup_invalid_redirect(ctx, query, popup_origin, nonce)
|
|
184
|
+
{
|
|
185
|
+
callback_url: "invalid_callback_url",
|
|
186
|
+
error_callback_url: "invalid_error_callback_url",
|
|
187
|
+
new_user_callback_url: "invalid_new_user_callback_url"
|
|
188
|
+
}.each do |key, code|
|
|
189
|
+
value = query[key]
|
|
190
|
+
next if value.to_s.empty?
|
|
191
|
+
next if ctx.context.trusted_origin?(value.to_s, allow_relative_paths: true)
|
|
192
|
+
|
|
193
|
+
oauth_popup_log(ctx.context, :error, "OAuth popup redirect URL is not trusted")
|
|
194
|
+
return oauth_popup_completion(ctx, popup_origin, nonce: nonce, error: {
|
|
195
|
+
code: code,
|
|
196
|
+
description: "Untrusted redirect URL"
|
|
197
|
+
})
|
|
198
|
+
end
|
|
199
|
+
nil
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def oauth_popup_additional_data(value)
|
|
203
|
+
parsed = value.is_a?(Hash) ? value : JSON.parse(value.to_s)
|
|
204
|
+
return {} unless parsed.is_a?(Hash)
|
|
205
|
+
|
|
206
|
+
parsed.reject { |key, _entry| OAUTH_POPUP_INTERNAL_STATE_KEYS.include?(key.to_s) }
|
|
207
|
+
rescue JSON::ParserError
|
|
208
|
+
{}
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def oauth_popup_set_marker(ctx, popup_origin, nonce)
|
|
212
|
+
cookie = ctx.context.create_auth_cookie(OAUTH_POPUP_MARKER_COOKIE, max_age: 600)
|
|
213
|
+
ctx.set_signed_cookie(
|
|
214
|
+
cookie.name,
|
|
215
|
+
JSON.generate({popupOrigin: popup_origin, popupNonce: nonce}),
|
|
216
|
+
ctx.context.secret,
|
|
217
|
+
cookie.attributes
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def oauth_popup_callback_path?(path)
|
|
222
|
+
path.to_s.start_with?("/callback/", "/oauth2/callback/")
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def oauth_popup_after_callback(ctx)
|
|
226
|
+
location = ctx.response_headers["location"].to_s
|
|
227
|
+
return if location.empty?
|
|
228
|
+
|
|
229
|
+
marker_cookie = ctx.context.create_auth_cookie(OAUTH_POPUP_MARKER_COOKIE)
|
|
230
|
+
marker = ctx.get_signed_cookie(marker_cookie.name, ctx.context.secret)
|
|
231
|
+
return unless marker
|
|
232
|
+
|
|
233
|
+
Cookies.expire_cookie(ctx, marker_cookie)
|
|
234
|
+
data = JSON.parse(marker)
|
|
235
|
+
popup_origin = data.fetch("popupOrigin").to_s
|
|
236
|
+
nonce = data["popupNonce"].to_s
|
|
237
|
+
return if popup_origin.empty?
|
|
238
|
+
|
|
239
|
+
token = oauth_popup_session_token(ctx)
|
|
240
|
+
message = if token
|
|
241
|
+
{nonce: nonce, token: token, redirectTo: location}
|
|
242
|
+
else
|
|
243
|
+
error = oauth_popup_redirect_error(location)
|
|
244
|
+
return unless error
|
|
245
|
+
|
|
246
|
+
{nonce: nonce, error: error}
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
oauth_popup_completion(ctx, popup_origin, message)
|
|
250
|
+
rescue JSON::ParserError, KeyError
|
|
251
|
+
nil
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def oauth_popup_session_token(ctx)
|
|
255
|
+
name = ctx.context.auth_cookies[:session_token].name
|
|
256
|
+
Cookies.split_set_cookie_header(ctx.response_headers["set-cookie"]).each do |line|
|
|
257
|
+
cookie = Cookies.parse_set_cookie(line)
|
|
258
|
+
next unless cookie && cookie[:name] == name
|
|
259
|
+
max_age = cookie.dig(:attributes, "max-age")
|
|
260
|
+
next if cookie[:value].empty? || (max_age.to_s.strip.match?(/\A[+-]?\d+\z/) && max_age.to_i == 0)
|
|
261
|
+
|
|
262
|
+
return URI.decode_uri_component(cookie[:value])
|
|
263
|
+
rescue ArgumentError
|
|
264
|
+
return cookie[:value]
|
|
265
|
+
end
|
|
266
|
+
nil
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def oauth_popup_redirect_error(location)
|
|
270
|
+
uri = URI.parse(location)
|
|
271
|
+
params = URI.decode_www_form(uri.query.to_s).to_h
|
|
272
|
+
return if params["error"].to_s.empty?
|
|
273
|
+
|
|
274
|
+
{code: params["error"], description: params["error_description"]}.compact
|
|
275
|
+
rescue URI::InvalidURIError
|
|
276
|
+
nil
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def oauth_popup_completion(ctx, popup_origin, message)
|
|
280
|
+
oauth_popup_warn_missing_bearer(ctx) if message[:token]
|
|
281
|
+
payload = {
|
|
282
|
+
type: OAUTH_POPUP_MESSAGE_TYPE,
|
|
283
|
+
targetOrigin: popup_origin
|
|
284
|
+
}.merge(message)
|
|
285
|
+
serialized = JSON.generate(payload)
|
|
286
|
+
.gsub("<", "\\u003c")
|
|
287
|
+
.gsub("\u2028", "\\u2028")
|
|
288
|
+
.gsub("\u2029", "\\u2029")
|
|
289
|
+
html = <<~HTML
|
|
290
|
+
<!doctype html>
|
|
291
|
+
<html>
|
|
292
|
+
<head><meta charset="utf-8"><title>Completing sign-in</title></head>
|
|
293
|
+
<body>
|
|
294
|
+
<script type="application/json" id="#{OAUTH_POPUP_DATA_ELEMENT_ID}">#{serialized}</script>
|
|
295
|
+
<script>#{OAUTH_POPUP_COMPLETE_SCRIPT}</script>
|
|
296
|
+
</body>
|
|
297
|
+
</html>
|
|
298
|
+
HTML
|
|
299
|
+
headers = ctx.response_headers.merge(
|
|
300
|
+
"content-type" => "text/html; charset=utf-8",
|
|
301
|
+
"content-security-policy" => "default-src 'none'; script-src '#{OAUTH_POPUP_SCRIPT_CSP_HASH}'; base-uri 'none'",
|
|
302
|
+
"cache-control" => "no-store",
|
|
303
|
+
"pragma" => "no-cache"
|
|
304
|
+
)
|
|
305
|
+
headers.delete("location")
|
|
306
|
+
Endpoint::Result.new(response: html, status: 200, headers: headers)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def oauth_popup_warn_missing_bearer(ctx)
|
|
310
|
+
return if ctx.context.options.plugins.any? { |plugin| plugin.id == "bearer" }
|
|
311
|
+
return if @oauth_popup_warned_missing_bearer
|
|
312
|
+
|
|
313
|
+
@oauth_popup_warned_missing_bearer = true
|
|
314
|
+
oauth_popup_log(
|
|
315
|
+
ctx.context,
|
|
316
|
+
:warn,
|
|
317
|
+
"OAuth popup hands the session token back via postMessage, but the `bearer` plugin is not registered. Add bearer() for embedded cross-site authentication."
|
|
318
|
+
)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def oauth_popup_log(context, level, message, *details)
|
|
322
|
+
logger = context.logger
|
|
323
|
+
if logger.respond_to?(:call)
|
|
324
|
+
logger.call(level, message, *details)
|
|
325
|
+
elsif logger.respond_to?(level)
|
|
326
|
+
logger.public_send(level, message, *details)
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|