better_auth 0.3.0 → 0.5.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 +17 -0
- data/README.md +24 -0
- data/lib/better_auth/adapters/internal_adapter.rb +10 -7
- data/lib/better_auth/adapters/memory.rb +57 -11
- data/lib/better_auth/adapters/sql.rb +123 -20
- data/lib/better_auth/api.rb +114 -9
- data/lib/better_auth/async.rb +70 -0
- data/lib/better_auth/configuration.rb +97 -7
- data/lib/better_auth/context.rb +165 -12
- data/lib/better_auth/cookies.rb +6 -4
- data/lib/better_auth/core.rb +2 -0
- data/lib/better_auth/crypto/jwe.rb +27 -5
- data/lib/better_auth/crypto.rb +32 -0
- data/lib/better_auth/database_hooks.rb +8 -8
- data/lib/better_auth/deprecate.rb +28 -0
- data/lib/better_auth/endpoint.rb +92 -5
- data/lib/better_auth/error.rb +8 -1
- data/lib/better_auth/host.rb +166 -0
- data/lib/better_auth/instrumentation.rb +74 -0
- data/lib/better_auth/logger.rb +31 -0
- data/lib/better_auth/middleware/origin_check.rb +2 -2
- data/lib/better_auth/oauth2.rb +94 -0
- data/lib/better_auth/plugins/admin/schema.rb +2 -2
- data/lib/better_auth/plugins/admin.rb +344 -16
- data/lib/better_auth/plugins/anonymous.rb +37 -3
- data/lib/better_auth/plugins/device_authorization.rb +102 -5
- data/lib/better_auth/plugins/dub.rb +148 -0
- data/lib/better_auth/plugins/email_otp.rb +261 -19
- data/lib/better_auth/plugins/expo.rb +17 -1
- data/lib/better_auth/plugins/generic_oauth.rb +67 -35
- data/lib/better_auth/plugins/jwt.rb +37 -4
- data/lib/better_auth/plugins/last_login_method.rb +2 -2
- data/lib/better_auth/plugins/magic_link.rb +66 -3
- data/lib/better_auth/plugins/mcp/authorization.rb +111 -0
- data/lib/better_auth/plugins/mcp/config.rb +51 -0
- data/lib/better_auth/plugins/mcp/consent.rb +31 -0
- data/lib/better_auth/plugins/mcp/legacy_aliases.rb +39 -0
- data/lib/better_auth/plugins/mcp/metadata.rb +81 -0
- data/lib/better_auth/plugins/mcp/registration.rb +31 -0
- data/lib/better_auth/plugins/mcp/resource_handler.rb +37 -0
- data/lib/better_auth/plugins/mcp/schema.rb +91 -0
- data/lib/better_auth/plugins/mcp/token.rb +108 -0
- data/lib/better_auth/plugins/mcp/userinfo.rb +37 -0
- data/lib/better_auth/plugins/mcp.rb +111 -263
- data/lib/better_auth/plugins/multi_session.rb +61 -3
- data/lib/better_auth/plugins/oauth_protocol.rb +173 -30
- data/lib/better_auth/plugins/oauth_proxy.rb +26 -6
- data/lib/better_auth/plugins/oidc_provider.rb +118 -14
- data/lib/better_auth/plugins/one_tap.rb +7 -2
- data/lib/better_auth/plugins/one_time_token.rb +42 -2
- data/lib/better_auth/plugins/open_api.rb +163 -318
- data/lib/better_auth/plugins/organization/schema.rb +6 -0
- data/lib/better_auth/plugins/organization.rb +186 -56
- data/lib/better_auth/plugins/phone_number.rb +141 -6
- data/lib/better_auth/plugins/siwe.rb +69 -3
- data/lib/better_auth/plugins/two_factor.rb +118 -41
- data/lib/better_auth/plugins/username.rb +57 -2
- data/lib/better_auth/rate_limiter.rb +38 -0
- data/lib/better_auth/request_state.rb +44 -0
- data/lib/better_auth/response.rb +42 -0
- data/lib/better_auth/router.rb +7 -1
- data/lib/better_auth/routes/account.rb +220 -42
- data/lib/better_auth/routes/email_verification.rb +98 -14
- data/lib/better_auth/routes/password.rb +126 -8
- data/lib/better_auth/routes/session.rb +128 -13
- data/lib/better_auth/routes/sign_in.rb +26 -2
- data/lib/better_auth/routes/sign_out.rb +13 -1
- data/lib/better_auth/routes/sign_up.rb +70 -4
- data/lib/better_auth/routes/social.rb +132 -7
- data/lib/better_auth/routes/user.rb +228 -20
- data/lib/better_auth/routes/validation.rb +50 -0
- data/lib/better_auth/secret_config.rb +115 -0
- data/lib/better_auth/session.rb +13 -2
- data/lib/better_auth/url_helpers.rb +206 -0
- data/lib/better_auth/version.rb +1 -1
- data/lib/better_auth.rb +12 -0
- metadata +23 -1
|
@@ -6,9 +6,21 @@ module BetterAuth
|
|
|
6
6
|
module Plugins
|
|
7
7
|
module OIDCProvider
|
|
8
8
|
VALID_PROMPTS = %w[none login consent create select_account].freeze
|
|
9
|
+
DEPRECATION_MESSAGE = 'The "oidc-provider" plugin is deprecated and will be removed in the next major version. Migrate to better_auth-oauth-provider. See: https://www.better-auth.com/docs/plugins/oauth-provider'
|
|
9
10
|
|
|
10
11
|
module_function
|
|
11
12
|
|
|
13
|
+
def warn_deprecation!(logger = nil)
|
|
14
|
+
return if @deprecation_warned
|
|
15
|
+
|
|
16
|
+
Deprecate.warn_once("[Deprecation] #{DEPRECATION_MESSAGE}", logger)
|
|
17
|
+
@deprecation_warned = true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reset_deprecation_warning!
|
|
21
|
+
@deprecation_warned = false
|
|
22
|
+
end
|
|
23
|
+
|
|
12
24
|
def normalize_issuer(value)
|
|
13
25
|
uri = URI.parse(value.to_s)
|
|
14
26
|
uri.query = nil
|
|
@@ -34,6 +46,9 @@ module BetterAuth
|
|
|
34
46
|
module_function
|
|
35
47
|
|
|
36
48
|
def oidc_provider(options = {})
|
|
49
|
+
raw_options = normalize_hash(options)
|
|
50
|
+
OIDCProvider.warn_deprecation!(raw_options[:logger]) unless raw_options[:__skip_deprecation_warning]
|
|
51
|
+
|
|
37
52
|
config = {
|
|
38
53
|
code_expires_in: 600,
|
|
39
54
|
consent_page: "/oauth2/authorize",
|
|
@@ -45,7 +60,7 @@ module BetterAuth
|
|
|
45
60
|
store_client_secret: "plain",
|
|
46
61
|
scopes: %w[openid profile email offline_access],
|
|
47
62
|
store: OAuthProtocol.stores
|
|
48
|
-
}.merge(
|
|
63
|
+
}.merge(raw_options.except(:logger, :__skip_deprecation_warning))
|
|
49
64
|
|
|
50
65
|
Plugin.new(
|
|
51
66
|
id: "oidc-provider",
|
|
@@ -113,7 +128,7 @@ module BetterAuth
|
|
|
113
128
|
end
|
|
114
129
|
|
|
115
130
|
def oidc_register_endpoint(config)
|
|
116
|
-
Endpoint.new(path: "/oauth2/register", method: "POST") do |ctx|
|
|
131
|
+
Endpoint.new(path: "/oauth2/register", method: "POST", metadata: oidc_openapi("registerOAuthApplication", "Register an OAuth2 application", "OAuth2 application registered successfully", oidc_client_schema)) do |ctx|
|
|
117
132
|
session = Routes.current_session(ctx, allow_nil: true)
|
|
118
133
|
unless session || config[:allow_dynamic_client_registration]
|
|
119
134
|
raise APIError.new("UNAUTHORIZED", message: "invalid_token")
|
|
@@ -146,7 +161,7 @@ module BetterAuth
|
|
|
146
161
|
end
|
|
147
162
|
|
|
148
163
|
def oidc_get_client_endpoint
|
|
149
|
-
Endpoint.new(path: "/oauth2/client/:id", method: "GET") do |ctx|
|
|
164
|
+
Endpoint.new(path: "/oauth2/client/:id", method: "GET", metadata: oidc_openapi("getOAuthClient", "Get OAuth2 client details", "OAuth2 client retrieved successfully", oidc_client_schema)) do |ctx|
|
|
150
165
|
client = OAuthProtocol.find_client(ctx, "oauthApplication", ctx.params["id"] || ctx.params[:id])
|
|
151
166
|
raise APIError.new("NOT_FOUND", message: "client not found") unless client
|
|
152
167
|
|
|
@@ -155,7 +170,7 @@ module BetterAuth
|
|
|
155
170
|
end
|
|
156
171
|
|
|
157
172
|
def oidc_list_clients_endpoint
|
|
158
|
-
Endpoint.new(path: "/oauth2/clients", method: "GET") do |ctx|
|
|
173
|
+
Endpoint.new(path: "/oauth2/clients", method: "GET", metadata: oidc_openapi("listOAuthApplications", "List OAuth2 applications", "OAuth2 applications retrieved successfully", {type: "array", items: oidc_client_schema})) do |ctx|
|
|
159
174
|
session = Routes.current_session(ctx)
|
|
160
175
|
clients = ctx.context.adapter.find_many(model: "oauthApplication", where: [{field: "userId", value: session[:user]["id"]}])
|
|
161
176
|
ctx.json(clients.map { |client| OAuthProtocol.client_response(client, include_secret: false) })
|
|
@@ -163,7 +178,7 @@ module BetterAuth
|
|
|
163
178
|
end
|
|
164
179
|
|
|
165
180
|
def oidc_update_client_endpoint
|
|
166
|
-
Endpoint.new(path: "/oauth2/client/:id", method: "PATCH") do |ctx|
|
|
181
|
+
Endpoint.new(path: "/oauth2/client/:id", method: "PATCH", metadata: oidc_openapi("updateOAuthApplication", "Update an OAuth2 application", "OAuth2 application updated successfully", oidc_client_schema)) do |ctx|
|
|
167
182
|
session = Routes.current_session(ctx)
|
|
168
183
|
client = oidc_find_owned_client!(ctx, session)
|
|
169
184
|
body = OAuthProtocol.stringify_keys(ctx.body)
|
|
@@ -191,7 +206,7 @@ module BetterAuth
|
|
|
191
206
|
end
|
|
192
207
|
|
|
193
208
|
def oidc_rotate_client_secret_endpoint(config)
|
|
194
|
-
Endpoint.new(path: "/oauth2/client/:id/rotate-secret", method: "POST") do |ctx|
|
|
209
|
+
Endpoint.new(path: "/oauth2/client/:id/rotate-secret", method: "POST", metadata: oidc_openapi("rotateOAuthApplicationSecret", "Rotate an OAuth2 application secret", "OAuth2 application secret rotated successfully", oidc_client_schema)) do |ctx|
|
|
195
210
|
session = Routes.current_session(ctx)
|
|
196
211
|
client = oidc_find_owned_client!(ctx, session)
|
|
197
212
|
if OAuthProtocol.stringify_keys(client)["tokenEndpointAuthMethod"] == "none"
|
|
@@ -209,7 +224,7 @@ module BetterAuth
|
|
|
209
224
|
end
|
|
210
225
|
|
|
211
226
|
def oidc_delete_client_endpoint
|
|
212
|
-
Endpoint.new(path: "/oauth2/client/:id", method: "DELETE") do |ctx|
|
|
227
|
+
Endpoint.new(path: "/oauth2/client/:id", method: "DELETE", metadata: oidc_openapi("deleteOAuthApplication", "Delete an OAuth2 application", "OAuth2 application deleted successfully", OpenAPI.success_response_schema)) do |ctx|
|
|
213
228
|
session = Routes.current_session(ctx)
|
|
214
229
|
client = oidc_find_owned_client!(ctx, session)
|
|
215
230
|
ctx.context.adapter.delete(model: "oauthApplication", where: [{field: "id", value: client.fetch("id")}])
|
|
@@ -218,7 +233,7 @@ module BetterAuth
|
|
|
218
233
|
end
|
|
219
234
|
|
|
220
235
|
def oidc_authorize_endpoint(config)
|
|
221
|
-
Endpoint.new(path: "/oauth2/authorize", method: "GET") do |ctx|
|
|
236
|
+
Endpoint.new(path: "/oauth2/authorize", method: "GET", metadata: oidc_openapi("oauth2Authorize", "Authorize an OAuth2 request", "Authorization response generated successfully", {type: "object", additionalProperties: true})) do |ctx|
|
|
222
237
|
query = OAuthProtocol.stringify_keys(ctx.query)
|
|
223
238
|
prompts = OIDCProvider.parse_prompt(query["prompt"])
|
|
224
239
|
session = Routes.current_session(ctx, allow_nil: true)
|
|
@@ -308,7 +323,7 @@ module BetterAuth
|
|
|
308
323
|
end
|
|
309
324
|
|
|
310
325
|
def oidc_consent_endpoint(config)
|
|
311
|
-
Endpoint.new(path: "/oauth2/consent", method: "POST") do |ctx|
|
|
326
|
+
Endpoint.new(path: "/oauth2/consent", method: "POST", metadata: oidc_openapi("oauth2Consent", "Handle OAuth2 consent", "OAuth2 consent handled successfully", oidc_redirect_response_schema)) do |ctx|
|
|
312
327
|
Routes.current_session(ctx)
|
|
313
328
|
body = OAuthProtocol.stringify_keys(ctx.body)
|
|
314
329
|
consent = config[:store][:consents].delete(body["consent_code"].to_s)
|
|
@@ -338,7 +353,11 @@ module BetterAuth
|
|
|
338
353
|
end
|
|
339
354
|
|
|
340
355
|
def oidc_token_endpoint(config)
|
|
341
|
-
Endpoint.new(
|
|
356
|
+
Endpoint.new(
|
|
357
|
+
path: "/oauth2/token",
|
|
358
|
+
method: "POST",
|
|
359
|
+
metadata: oidc_openapi("oauth2Token", "Exchange OAuth2 code for tokens", "OAuth2 tokens issued successfully", oidc_token_response_schema).merge(allowed_media_types: ["application/x-www-form-urlencoded", "application/json"])
|
|
360
|
+
) do |ctx|
|
|
342
361
|
body = OAuthProtocol.stringify_keys(ctx.body)
|
|
343
362
|
client = OAuthProtocol.authenticate_client!(ctx, "oauthApplication", store_client_secret: config[:store_client_secret])
|
|
344
363
|
raise APIError.new("UNAUTHORIZED", message: "invalid_client") unless client
|
|
@@ -374,13 +393,17 @@ module BetterAuth
|
|
|
374
393
|
end
|
|
375
394
|
|
|
376
395
|
def oidc_userinfo_endpoint(config)
|
|
377
|
-
Endpoint.new(path: "/oauth2/userinfo", method: "GET") do |ctx|
|
|
396
|
+
Endpoint.new(path: "/oauth2/userinfo", method: "GET", metadata: oidc_openapi("oauth2Userinfo", "Get OAuth2 user information", "User information retrieved successfully", oidc_userinfo_schema)) do |ctx|
|
|
378
397
|
ctx.json(OAuthProtocol.userinfo(config[:store], ctx.headers["authorization"], additional_claim: config[:get_additional_user_info_claim]))
|
|
379
398
|
end
|
|
380
399
|
end
|
|
381
400
|
|
|
382
401
|
def oidc_introspect_endpoint(config)
|
|
383
|
-
Endpoint.new(
|
|
402
|
+
Endpoint.new(
|
|
403
|
+
path: "/oauth2/introspect",
|
|
404
|
+
method: "POST",
|
|
405
|
+
metadata: oidc_openapi("oauth2Introspect", "Introspect an OAuth2 token", "OAuth2 token introspection result", oidc_introspection_schema).merge(allowed_media_types: ["application/x-www-form-urlencoded", "application/json"])
|
|
406
|
+
) do |ctx|
|
|
384
407
|
OAuthProtocol.authenticate_client!(ctx, "oauthApplication", store_client_secret: config[:store_client_secret])
|
|
385
408
|
body = OAuthProtocol.stringify_keys(ctx.body)
|
|
386
409
|
token = config[:store][:tokens][body["token"].to_s] || config[:store][:refresh_tokens][body["token"].to_s]
|
|
@@ -396,7 +419,11 @@ module BetterAuth
|
|
|
396
419
|
end
|
|
397
420
|
|
|
398
421
|
def oidc_revoke_endpoint(config)
|
|
399
|
-
Endpoint.new(
|
|
422
|
+
Endpoint.new(
|
|
423
|
+
path: "/oauth2/revoke",
|
|
424
|
+
method: "POST",
|
|
425
|
+
metadata: oidc_openapi("oauth2Revoke", "Revoke an OAuth2 token", "OAuth2 token revoked successfully", OpenAPI.object_schema({revoked: {type: "boolean"}}, required: ["revoked"])).merge(allowed_media_types: ["application/x-www-form-urlencoded", "application/json"])
|
|
426
|
+
) do |ctx|
|
|
400
427
|
OAuthProtocol.authenticate_client!(ctx, "oauthApplication", store_client_secret: config[:store_client_secret])
|
|
401
428
|
body = OAuthProtocol.stringify_keys(ctx.body)
|
|
402
429
|
if (token = config[:store][:tokens][body["token"].to_s] || config[:store][:refresh_tokens][body["token"].to_s])
|
|
@@ -407,7 +434,11 @@ module BetterAuth
|
|
|
407
434
|
end
|
|
408
435
|
|
|
409
436
|
def oidc_end_session_endpoint
|
|
410
|
-
Endpoint.new(
|
|
437
|
+
Endpoint.new(
|
|
438
|
+
path: "/oauth2/endsession",
|
|
439
|
+
method: ["GET", "POST"],
|
|
440
|
+
metadata: oidc_openapi("oauth2EndSession", "RP-Initiated Logout endpoint", "Logout request handled").merge(allowed_media_types: ["application/x-www-form-urlencoded", "application/json"])
|
|
441
|
+
) do |ctx|
|
|
411
442
|
input_source = (ctx.method == "GET") ? ctx.query : ctx.body
|
|
412
443
|
input = OAuthProtocol.stringify_keys(input_source)
|
|
413
444
|
if input["post_logout_redirect_uri"]
|
|
@@ -425,6 +456,79 @@ module BetterAuth
|
|
|
425
456
|
end
|
|
426
457
|
end
|
|
427
458
|
|
|
459
|
+
def oidc_openapi(operation_id, description, response_description = "Success", response_schema = {type: "object"})
|
|
460
|
+
{
|
|
461
|
+
openapi: {
|
|
462
|
+
operationId: operation_id,
|
|
463
|
+
description: description,
|
|
464
|
+
responses: {
|
|
465
|
+
"200" => OpenAPI.json_response(response_description, response_schema)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def oidc_client_schema
|
|
472
|
+
OpenAPI.object_schema(
|
|
473
|
+
{
|
|
474
|
+
clientId: {type: "string"},
|
|
475
|
+
clientSecret: {type: ["string", "null"]},
|
|
476
|
+
name: {type: "string"},
|
|
477
|
+
redirectUris: {type: "array", items: {type: "string"}},
|
|
478
|
+
grantTypes: {type: "array", items: {type: "string"}},
|
|
479
|
+
responseTypes: {type: "array", items: {type: "string"}}
|
|
480
|
+
},
|
|
481
|
+
required: ["clientId", "name"]
|
|
482
|
+
)
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def oidc_redirect_response_schema
|
|
486
|
+
OpenAPI.object_schema(
|
|
487
|
+
{redirectURI: {type: "string", format: "uri"}},
|
|
488
|
+
required: ["redirectURI"]
|
|
489
|
+
)
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def oidc_token_response_schema
|
|
493
|
+
OpenAPI.object_schema(
|
|
494
|
+
{
|
|
495
|
+
access_token: {type: "string"},
|
|
496
|
+
token_type: {type: "string"},
|
|
497
|
+
expires_in: {type: "number"},
|
|
498
|
+
refresh_token: {type: ["string", "null"]},
|
|
499
|
+
id_token: {type: ["string", "null"]},
|
|
500
|
+
scope: {type: ["string", "null"]}
|
|
501
|
+
},
|
|
502
|
+
required: ["access_token", "token_type", "expires_in"]
|
|
503
|
+
)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def oidc_userinfo_schema
|
|
507
|
+
OpenAPI.object_schema(
|
|
508
|
+
{
|
|
509
|
+
sub: {type: "string"},
|
|
510
|
+
email: {type: ["string", "null"]},
|
|
511
|
+
email_verified: {type: ["boolean", "null"]},
|
|
512
|
+
name: {type: ["string", "null"]},
|
|
513
|
+
picture: {type: ["string", "null"]}
|
|
514
|
+
},
|
|
515
|
+
required: ["sub"]
|
|
516
|
+
)
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def oidc_introspection_schema
|
|
520
|
+
OpenAPI.object_schema(
|
|
521
|
+
{
|
|
522
|
+
active: {type: "boolean"},
|
|
523
|
+
client_id: {type: ["string", "null"]},
|
|
524
|
+
scope: {type: ["string", "null"]},
|
|
525
|
+
sub: {type: ["string", "null"]},
|
|
526
|
+
exp: {type: ["number", "null"]}
|
|
527
|
+
},
|
|
528
|
+
required: ["active"]
|
|
529
|
+
)
|
|
530
|
+
end
|
|
531
|
+
|
|
428
532
|
def oidc_provider_schema
|
|
429
533
|
{
|
|
430
534
|
oauthApplication: {
|
|
@@ -29,8 +29,12 @@ module BetterAuth
|
|
|
29
29
|
},
|
|
30
30
|
metadata: {
|
|
31
31
|
openapi: {
|
|
32
|
+
operationId: "oneTapCallback",
|
|
32
33
|
summary: "One tap callback",
|
|
33
|
-
description: "Use this endpoint to authenticate with Google One Tap"
|
|
34
|
+
description: "Use this endpoint to authenticate with Google One Tap",
|
|
35
|
+
responses: {
|
|
36
|
+
"200" => OpenAPI.json_response("Success", OpenAPI.session_response_schema_pair)
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
) do |ctx|
|
|
@@ -61,7 +65,8 @@ module BetterAuth
|
|
|
61
65
|
providerId: "google",
|
|
62
66
|
accountId: fetch_value(payload, "sub").to_s,
|
|
63
67
|
idToken: id_token
|
|
64
|
-
}
|
|
68
|
+
},
|
|
69
|
+
context: ctx
|
|
65
70
|
)
|
|
66
71
|
raise APIError.new("INTERNAL_SERVER_ERROR", message: "Could not create user") unless created
|
|
67
72
|
|
|
@@ -29,7 +29,27 @@ module BetterAuth
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def generate_one_time_token_endpoint(config)
|
|
32
|
-
Endpoint.new(
|
|
32
|
+
Endpoint.new(
|
|
33
|
+
path: "/one-time-token/generate",
|
|
34
|
+
method: "GET",
|
|
35
|
+
metadata: {
|
|
36
|
+
openapi: {
|
|
37
|
+
operationId: "generateOneTimeToken",
|
|
38
|
+
description: "Generate a one-time token for the current session",
|
|
39
|
+
responses: {
|
|
40
|
+
"200" => OpenAPI.json_response(
|
|
41
|
+
"One-time token",
|
|
42
|
+
OpenAPI.object_schema(
|
|
43
|
+
{
|
|
44
|
+
token: {type: "string"}
|
|
45
|
+
},
|
|
46
|
+
required: ["token"]
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
) do |ctx|
|
|
33
53
|
if config[:disable_client_request] && ctx.request
|
|
34
54
|
raise APIError.new("BAD_REQUEST", message: "Client requests are disabled")
|
|
35
55
|
end
|
|
@@ -41,7 +61,27 @@ module BetterAuth
|
|
|
41
61
|
end
|
|
42
62
|
|
|
43
63
|
def verify_one_time_token_endpoint(config)
|
|
44
|
-
Endpoint.new(
|
|
64
|
+
Endpoint.new(
|
|
65
|
+
path: "/one-time-token/verify",
|
|
66
|
+
method: "POST",
|
|
67
|
+
metadata: {
|
|
68
|
+
openapi: {
|
|
69
|
+
operationId: "verifyOneTimeToken",
|
|
70
|
+
description: "Verify a one-time token and restore its session",
|
|
71
|
+
requestBody: OpenAPI.json_request_body(
|
|
72
|
+
OpenAPI.object_schema(
|
|
73
|
+
{
|
|
74
|
+
token: {type: "string"}
|
|
75
|
+
},
|
|
76
|
+
required: ["token"]
|
|
77
|
+
)
|
|
78
|
+
),
|
|
79
|
+
responses: {
|
|
80
|
+
"200" => OpenAPI.json_response("Session restored", OpenAPI.session_response_schema_pair)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
) do |ctx|
|
|
45
85
|
body = normalize_hash(ctx.body)
|
|
46
86
|
token = body[:token].to_s
|
|
47
87
|
stored_token = one_time_token_stored_value(config, token)
|