better_auth 0.4.0 → 0.6.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 +2 -0
- data/README.md +24 -0
- data/lib/better_auth/adapters/internal_adapter.rb +5 -5
- data/lib/better_auth/adapters/sql.rb +96 -18
- data/lib/better_auth/api.rb +113 -13
- 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 +5 -5
- data/lib/better_auth/endpoint.rb +87 -3
- data/lib/better_auth/error.rb +8 -1
- 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 +246 -15
- data/lib/better_auth/plugins/expo.rb +17 -1
- data/lib/better_auth/plugins/generic_oauth.rb +53 -7
- 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 +2 -2
- 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.rb +135 -36
- 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 +65 -23
- data/lib/better_auth/plugins/username.rb +57 -2
- data/lib/better_auth/rate_limiter.rb +20 -0
- data/lib/better_auth/response.rb +42 -0
- data/lib/better_auth/router.rb +7 -1
- data/lib/better_auth/routes/account.rb +204 -38
- data/lib/better_auth/routes/email_verification.rb +98 -14
- data/lib/better_auth/routes/password.rb +125 -8
- data/lib/better_auth/routes/session.rb +128 -13
- data/lib/better_auth/routes/sign_in.rb +24 -2
- data/lib/better_auth/routes/sign_out.rb +13 -1
- data/lib/better_auth/routes/sign_up.rb +62 -4
- data/lib/better_auth/routes/social.rb +102 -7
- data/lib/better_auth/routes/user.rb +222 -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 +1 -1
- data/lib/better_auth/url_helpers.rb +12 -1
- data/lib/better_auth/version.rb +1 -1
- data/lib/better_auth.rb +4 -0
- metadata +15 -1
|
@@ -156,7 +156,7 @@ module BetterAuth
|
|
|
156
156
|
end
|
|
157
157
|
|
|
158
158
|
def organization_create_endpoint(config)
|
|
159
|
-
Endpoint.new(path: "/organization/create", method: "POST") do |ctx|
|
|
159
|
+
Endpoint.new(path: "/organization/create", method: "POST", metadata: organization_openapi("createOrganization", "Create an organization", response: organization_ref_schema("Organization"))) do |ctx|
|
|
160
160
|
body = normalize_hash(ctx.body)
|
|
161
161
|
session = Routes.current_session(ctx, allow_nil: true)
|
|
162
162
|
user = session ? session[:user] : ctx.context.internal_adapter.find_user_by_id(body[:user_id])
|
|
@@ -204,7 +204,8 @@ module BetterAuth
|
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
def organization_check_slug_endpoint
|
|
207
|
-
Endpoint.new(path: "/organization/check-slug", method: "POST") do |ctx|
|
|
207
|
+
Endpoint.new(path: "/organization/check-slug", method: "POST", metadata: organization_openapi("checkOrganizationSlug", "Check if an organization slug is available", response: OpenAPI.status_response_schema)) do |ctx|
|
|
208
|
+
Routes.request_only_session(ctx)
|
|
208
209
|
slug = normalize_hash(ctx.body)[:slug].to_s
|
|
209
210
|
if slug.empty? || organization_by_slug(ctx, slug)
|
|
210
211
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("ORGANIZATION_SLUG_ALREADY_TAKEN"))
|
|
@@ -214,7 +215,7 @@ module BetterAuth
|
|
|
214
215
|
end
|
|
215
216
|
|
|
216
217
|
def organization_list_endpoint
|
|
217
|
-
Endpoint.new(path: "/organization/list", method: "GET") do |ctx|
|
|
218
|
+
Endpoint.new(path: "/organization/list", method: "GET", metadata: organization_openapi("listOrganizations", "List organizations", response: organization_array_schema("Organization"))) do |ctx|
|
|
218
219
|
session = Routes.current_session(ctx)
|
|
219
220
|
members = ctx.context.adapter.find_many(model: "member", where: [{field: "userId", value: session[:user]["id"]}])
|
|
220
221
|
organizations = members.filter_map { |member| organization_by_id(ctx, member["organizationId"]) }
|
|
@@ -223,7 +224,7 @@ module BetterAuth
|
|
|
223
224
|
end
|
|
224
225
|
|
|
225
226
|
def organization_update_endpoint(config)
|
|
226
|
-
Endpoint.new(path: "/organization/update", method: "POST") do |ctx|
|
|
227
|
+
Endpoint.new(path: "/organization/update", method: "POST", metadata: organization_openapi("updateOrganization", "Update an organization", response: organization_ref_schema("Organization"))) do |ctx|
|
|
227
228
|
session = Routes.current_session(ctx)
|
|
228
229
|
body = normalize_hash(ctx.body)
|
|
229
230
|
id = body[:organization_id] || body[:organizationId]
|
|
@@ -248,7 +249,7 @@ module BetterAuth
|
|
|
248
249
|
end
|
|
249
250
|
|
|
250
251
|
def organization_delete_endpoint(config)
|
|
251
|
-
Endpoint.new(path: "/organization/delete", method: "POST") do |ctx|
|
|
252
|
+
Endpoint.new(path: "/organization/delete", method: "POST", metadata: organization_openapi("deleteOrganization", "Delete an organization", response: OpenAPI.status_response_schema)) do |ctx|
|
|
252
253
|
session = Routes.current_session(ctx)
|
|
253
254
|
body = normalize_hash(ctx.body)
|
|
254
255
|
organization = organization_by_id(ctx, body[:organization_id]) || organization_by_slug(ctx, body[:organization_slug])
|
|
@@ -269,7 +270,7 @@ module BetterAuth
|
|
|
269
270
|
end
|
|
270
271
|
|
|
271
272
|
def organization_set_active_endpoint
|
|
272
|
-
Endpoint.new(path: "/organization/set-active", method: "POST") do |ctx|
|
|
273
|
+
Endpoint.new(path: "/organization/set-active", method: "POST", metadata: organization_openapi("setActiveOrganization", "Set the active organization", response: organization_nullable_schema("Organization"))) do |ctx|
|
|
273
274
|
session = Routes.current_session(ctx, sensitive: true)
|
|
274
275
|
body = normalize_hash(ctx.body)
|
|
275
276
|
if body.key?(:organization_id) && body[:organization_id].nil?
|
|
@@ -288,7 +289,7 @@ module BetterAuth
|
|
|
288
289
|
end
|
|
289
290
|
|
|
290
291
|
def organization_get_full_endpoint(config)
|
|
291
|
-
Endpoint.new(path: "/organization/get-full-organization", method: "GET") do |ctx|
|
|
292
|
+
Endpoint.new(path: "/organization/get-full-organization", method: "GET", metadata: organization_openapi("getOrganization", "Get the full organization", response: organization_nullable_schema("Organization"))) do |ctx|
|
|
292
293
|
session = Routes.current_session(ctx)
|
|
293
294
|
query = normalize_hash(ctx.query)
|
|
294
295
|
explicit_lookup = query.key?(:organization_slug) || query.key?(:organization_id)
|
|
@@ -315,7 +316,7 @@ module BetterAuth
|
|
|
315
316
|
end
|
|
316
317
|
|
|
317
318
|
def organization_invite_endpoint(config)
|
|
318
|
-
Endpoint.new(path: "/organization/invite-member", method: "POST") do |ctx|
|
|
319
|
+
Endpoint.new(path: "/organization/invite-member", method: "POST", metadata: organization_openapi("createOrganizationInvitation", "Create an organization invitation", response: organization_ref_schema("Invitation"))) do |ctx|
|
|
319
320
|
session = Routes.current_session(ctx)
|
|
320
321
|
body = normalize_hash(ctx.body)
|
|
321
322
|
organization = organization_by_id(ctx, body[:organization_id] || session[:session]["activeOrganizationId"]) || organization_by_slug(ctx, body[:organization_slug])
|
|
@@ -370,7 +371,7 @@ module BetterAuth
|
|
|
370
371
|
end
|
|
371
372
|
|
|
372
373
|
def organization_accept_invitation_endpoint(config)
|
|
373
|
-
Endpoint.new(path: "/organization/accept-invitation", method: "POST") do |ctx|
|
|
374
|
+
Endpoint.new(path: "/organization/accept-invitation", method: "POST", metadata: organization_openapi("acceptOrganizationInvitation", "Accept an organization invitation", response: organization_accept_invitation_schema)) do |ctx|
|
|
374
375
|
session = Routes.current_session(ctx)
|
|
375
376
|
body = normalize_hash(ctx.body)
|
|
376
377
|
invitation = invitation_by_id(ctx, body[:invitation_id] || body[:id])
|
|
@@ -394,7 +395,7 @@ module BetterAuth
|
|
|
394
395
|
end
|
|
395
396
|
|
|
396
397
|
def organization_reject_invitation_endpoint(_config)
|
|
397
|
-
Endpoint.new(path: "/organization/reject-invitation", method: "POST") do |ctx|
|
|
398
|
+
Endpoint.new(path: "/organization/reject-invitation", method: "POST", metadata: organization_openapi("rejectOrganizationInvitation", "Reject an organization invitation", response: organization_ref_schema("Invitation"))) do |ctx|
|
|
398
399
|
session = Routes.current_session(ctx)
|
|
399
400
|
invitation = invitation_by_id(ctx, normalize_hash(ctx.body)[:invitation_id])
|
|
400
401
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("INVITATION_NOT_FOUND")) unless invitation
|
|
@@ -405,7 +406,7 @@ module BetterAuth
|
|
|
405
406
|
end
|
|
406
407
|
|
|
407
408
|
def organization_cancel_invitation_endpoint(config)
|
|
408
|
-
Endpoint.new(path: "/organization/cancel-invitation", method: "POST") do |ctx|
|
|
409
|
+
Endpoint.new(path: "/organization/cancel-invitation", method: "POST", metadata: organization_openapi("cancelOrganizationInvitation", "Cancel an organization invitation", response: organization_ref_schema("Invitation"))) do |ctx|
|
|
409
410
|
session = Routes.current_session(ctx)
|
|
410
411
|
invitation = invitation_by_id(ctx, normalize_hash(ctx.body)[:invitation_id])
|
|
411
412
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("INVITATION_NOT_FOUND")) unless invitation
|
|
@@ -416,7 +417,7 @@ module BetterAuth
|
|
|
416
417
|
end
|
|
417
418
|
|
|
418
419
|
def organization_get_invitation_endpoint
|
|
419
|
-
Endpoint.new(path: "/organization/get-invitation", method: "GET") do |ctx|
|
|
420
|
+
Endpoint.new(path: "/organization/get-invitation", method: "GET", metadata: organization_openapi("getOrganizationInvitation", "Get an organization invitation", response: organization_ref_schema("Invitation"))) do |ctx|
|
|
420
421
|
invitation = invitation_by_id(ctx, normalize_hash(ctx.query)[:id] || normalize_hash(ctx.query)[:invitation_id])
|
|
421
422
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("INVITATION_NOT_FOUND")) unless invitation
|
|
422
423
|
ctx.json(invitation_wire(ctx, invitation))
|
|
@@ -424,7 +425,7 @@ module BetterAuth
|
|
|
424
425
|
end
|
|
425
426
|
|
|
426
427
|
def organization_list_invitations_endpoint(config)
|
|
427
|
-
Endpoint.new(path: "/organization/list-invitations", method: "GET") do |ctx|
|
|
428
|
+
Endpoint.new(path: "/organization/list-invitations", method: "GET", metadata: organization_openapi("listOrganizationInvitations", "List organization invitations", response: organization_array_schema("Invitation"))) do |ctx|
|
|
428
429
|
session = Routes.current_session(ctx)
|
|
429
430
|
organization_id = normalize_hash(ctx.query)[:organization_id] || session[:session]["activeOrganizationId"]
|
|
430
431
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("NO_ACTIVE_ORGANIZATION")) unless organization_id
|
|
@@ -435,7 +436,7 @@ module BetterAuth
|
|
|
435
436
|
end
|
|
436
437
|
|
|
437
438
|
def organization_list_user_invitations_endpoint
|
|
438
|
-
Endpoint.new(path: "/organization/list-user-invitations", method: "GET") do |ctx|
|
|
439
|
+
Endpoint.new(path: "/organization/list-user-invitations", method: "GET", metadata: organization_openapi("listUserInvitations", "List user invitations", response: organization_array_schema("Invitation"))) do |ctx|
|
|
439
440
|
session = Routes.current_session(ctx)
|
|
440
441
|
invitations = ctx.context.adapter.find_many(model: "invitation", where: [{field: "email", value: session[:user]["email"].to_s.downcase}, {field: "status", value: "pending"}])
|
|
441
442
|
ctx.json(invitations.map { |entry| invitation_wire(ctx, entry) })
|
|
@@ -443,7 +444,7 @@ module BetterAuth
|
|
|
443
444
|
end
|
|
444
445
|
|
|
445
446
|
def organization_add_member_endpoint(config)
|
|
446
|
-
Endpoint.new(path: "/organization/add-member", method: "POST") do |ctx|
|
|
447
|
+
Endpoint.new(path: "/organization/add-member", method: "POST", metadata: organization_openapi("addOrganizationMember", "Add an organization member", response: organization_ref_schema("Member"))) do |ctx|
|
|
447
448
|
session = Routes.current_session(ctx)
|
|
448
449
|
body = normalize_hash(ctx.body)
|
|
449
450
|
organization_id = body[:organization_id]
|
|
@@ -464,7 +465,7 @@ module BetterAuth
|
|
|
464
465
|
end
|
|
465
466
|
|
|
466
467
|
def organization_remove_member_endpoint(config)
|
|
467
|
-
Endpoint.new(path: "/organization/remove-member", method: "POST") do |ctx|
|
|
468
|
+
Endpoint.new(path: "/organization/remove-member", method: "POST", metadata: organization_openapi("removeOrganizationMember", "Remove an organization member", response: OpenAPI.status_response_schema)) do |ctx|
|
|
468
469
|
session = Routes.current_session(ctx)
|
|
469
470
|
body = normalize_hash(ctx.body)
|
|
470
471
|
member = member_by_id(ctx, body[:member_id]) || require_member(ctx, body[:user_id], body[:organization_id])
|
|
@@ -481,7 +482,7 @@ module BetterAuth
|
|
|
481
482
|
end
|
|
482
483
|
|
|
483
484
|
def organization_update_member_role_endpoint(config)
|
|
484
|
-
Endpoint.new(path: "/organization/update-member-role", method: "POST") do |ctx|
|
|
485
|
+
Endpoint.new(path: "/organization/update-member-role", method: "POST", metadata: organization_openapi("updateOrganizationMemberRole", "Update an organization member role", response: organization_ref_schema("Member"))) do |ctx|
|
|
485
486
|
session = Routes.current_session(ctx)
|
|
486
487
|
body = normalize_hash(ctx.body)
|
|
487
488
|
member = member_by_id(ctx, body[:member_id]) || require_member(ctx, body[:user_id], body[:organization_id])
|
|
@@ -493,7 +494,7 @@ module BetterAuth
|
|
|
493
494
|
end
|
|
494
495
|
|
|
495
496
|
def organization_get_active_member_endpoint(_config)
|
|
496
|
-
Endpoint.new(path: "/organization/get-active-member", method: "GET") do |ctx|
|
|
497
|
+
Endpoint.new(path: "/organization/get-active-member", method: "GET", metadata: organization_openapi("getActiveOrganizationMember", "Get the active organization member", response: organization_ref_schema("Member"))) do |ctx|
|
|
497
498
|
session = Routes.current_session(ctx)
|
|
498
499
|
organization_id = normalize_hash(ctx.query)[:organization_id] || session[:session]["activeOrganizationId"]
|
|
499
500
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("NO_ACTIVE_ORGANIZATION")) unless organization_id
|
|
@@ -503,7 +504,7 @@ module BetterAuth
|
|
|
503
504
|
end
|
|
504
505
|
|
|
505
506
|
def organization_get_active_member_role_endpoint(_config)
|
|
506
|
-
Endpoint.new(path: "/organization/get-active-member-role", method: "GET") do |ctx|
|
|
507
|
+
Endpoint.new(path: "/organization/get-active-member-role", method: "GET", metadata: organization_openapi("getActiveOrganizationMemberRole", "Get the active organization member role", response: organization_active_member_role_schema)) do |ctx|
|
|
507
508
|
session = Routes.current_session(ctx)
|
|
508
509
|
query = normalize_hash(ctx.query)
|
|
509
510
|
organization_id = query[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -515,7 +516,7 @@ module BetterAuth
|
|
|
515
516
|
end
|
|
516
517
|
|
|
517
518
|
def organization_leave_endpoint(config)
|
|
518
|
-
Endpoint.new(path: "/organization/leave", method: "POST") do |ctx|
|
|
519
|
+
Endpoint.new(path: "/organization/leave", method: "POST", metadata: organization_openapi("leaveOrganization", "Leave an organization", response: OpenAPI.status_response_schema)) do |ctx|
|
|
519
520
|
session = Routes.current_session(ctx)
|
|
520
521
|
organization_id = normalize_hash(ctx.body)[:organization_id]
|
|
521
522
|
member = require_member!(ctx, session[:user]["id"], organization_id)
|
|
@@ -527,7 +528,7 @@ module BetterAuth
|
|
|
527
528
|
end
|
|
528
529
|
|
|
529
530
|
def organization_list_members_endpoint(_config)
|
|
530
|
-
Endpoint.new(path: "/organization/list-members", method: "GET") do |ctx|
|
|
531
|
+
Endpoint.new(path: "/organization/list-members", method: "GET", metadata: organization_openapi("listOrganizationMembers", "List organization members", response: organization_members_response_schema)) do |ctx|
|
|
531
532
|
session = Routes.current_session(ctx)
|
|
532
533
|
query = normalize_hash(ctx.query)
|
|
533
534
|
organization_id = query[:organization_id] || organization_by_slug(ctx, query[:organization_slug])&.fetch("id") || session[:session]["activeOrganizationId"]
|
|
@@ -538,7 +539,7 @@ module BetterAuth
|
|
|
538
539
|
end
|
|
539
540
|
|
|
540
541
|
def organization_has_permission_endpoint(config)
|
|
541
|
-
Endpoint.new(path: "/organization/has-permission", method: "POST") do |ctx|
|
|
542
|
+
Endpoint.new(path: "/organization/has-permission", method: "POST", metadata: organization_openapi("hasOrganizationPermission", "Check if the member has organization permission", response: organization_permission_response_schema)) do |ctx|
|
|
542
543
|
session = Routes.current_session(ctx)
|
|
543
544
|
body = normalize_hash(ctx.body)
|
|
544
545
|
organization_id = body[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -550,7 +551,7 @@ module BetterAuth
|
|
|
550
551
|
end
|
|
551
552
|
|
|
552
553
|
def organization_create_team_endpoint(config)
|
|
553
|
-
Endpoint.new(path: "/organization/create-team", method: "POST") do |ctx|
|
|
554
|
+
Endpoint.new(path: "/organization/create-team", method: "POST", metadata: organization_openapi("createOrganizationTeam", "Create an organization team", response: organization_ref_schema("Team"))) do |ctx|
|
|
554
555
|
session = Routes.current_session(ctx)
|
|
555
556
|
body = normalize_hash(ctx.body)
|
|
556
557
|
organization_id = body[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -570,7 +571,7 @@ module BetterAuth
|
|
|
570
571
|
end
|
|
571
572
|
|
|
572
573
|
def organization_list_teams_endpoint(_config)
|
|
573
|
-
Endpoint.new(path: "/organization/list-teams", method: "GET") do |ctx|
|
|
574
|
+
Endpoint.new(path: "/organization/list-teams", method: "GET", metadata: organization_openapi("listOrganizationTeams", "List organization teams", response: organization_array_schema("Team"))) do |ctx|
|
|
574
575
|
session = Routes.current_session(ctx)
|
|
575
576
|
organization_id = normalize_hash(ctx.query)[:organization_id] || session[:session]["activeOrganizationId"]
|
|
576
577
|
require_member!(ctx, session[:user]["id"], organization_id)
|
|
@@ -580,7 +581,7 @@ module BetterAuth
|
|
|
580
581
|
end
|
|
581
582
|
|
|
582
583
|
def organization_update_team_endpoint(config)
|
|
583
|
-
Endpoint.new(path: "/organization/update-team", method: "POST") do |ctx|
|
|
584
|
+
Endpoint.new(path: "/organization/update-team", method: "POST", metadata: organization_openapi("updateOrganizationTeam", "Update an organization team", response: organization_ref_schema("Team"))) do |ctx|
|
|
584
585
|
session = Routes.current_session(ctx)
|
|
585
586
|
body = normalize_hash(ctx.body)
|
|
586
587
|
team = team_by_id(ctx, body[:team_id])
|
|
@@ -592,7 +593,7 @@ module BetterAuth
|
|
|
592
593
|
end
|
|
593
594
|
|
|
594
595
|
def organization_remove_team_endpoint(config)
|
|
595
|
-
Endpoint.new(path: "/organization/remove-team", method: "POST") do |ctx|
|
|
596
|
+
Endpoint.new(path: "/organization/remove-team", method: "POST", metadata: organization_openapi("removeOrganizationTeam", "Remove an organization team", response: OpenAPI.status_response_schema)) do |ctx|
|
|
596
597
|
session = Routes.current_session(ctx)
|
|
597
598
|
team = team_by_id(ctx, normalize_hash(ctx.body)[:team_id])
|
|
598
599
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("TEAM_NOT_FOUND")) unless team
|
|
@@ -608,7 +609,7 @@ module BetterAuth
|
|
|
608
609
|
end
|
|
609
610
|
|
|
610
611
|
def organization_set_active_team_endpoint(_config)
|
|
611
|
-
Endpoint.new(path: "/organization/set-active-team", method: "POST") do |ctx|
|
|
612
|
+
Endpoint.new(path: "/organization/set-active-team", method: "POST", metadata: organization_openapi("setActiveOrganizationTeam", "Set the active organization team", response: organization_nullable_schema("Team"))) do |ctx|
|
|
612
613
|
session = Routes.current_session(ctx)
|
|
613
614
|
body = normalize_hash(ctx.body)
|
|
614
615
|
if body.key?(:team_id) && body[:team_id].nil?
|
|
@@ -626,7 +627,7 @@ module BetterAuth
|
|
|
626
627
|
end
|
|
627
628
|
|
|
628
629
|
def organization_list_user_teams_endpoint
|
|
629
|
-
Endpoint.new(path: "/organization/list-user-teams", method: "GET") do |ctx|
|
|
630
|
+
Endpoint.new(path: "/organization/list-user-teams", method: "GET", metadata: organization_openapi("listUserTeams", "List user teams", response: organization_array_schema("Team"))) do |ctx|
|
|
630
631
|
session = Routes.current_session(ctx)
|
|
631
632
|
memberships = ctx.context.adapter.find_many(model: "teamMember", where: [{field: "userId", value: session[:user]["id"]}])
|
|
632
633
|
ctx.json(memberships.filter_map { |entry| team_by_id(ctx, entry["teamId"]) }.map { |team| team_wire(ctx, team) })
|
|
@@ -634,7 +635,7 @@ module BetterAuth
|
|
|
634
635
|
end
|
|
635
636
|
|
|
636
637
|
def organization_list_team_members_endpoint(_config)
|
|
637
|
-
Endpoint.new(path: "/organization/list-team-members", method: "GET") do |ctx|
|
|
638
|
+
Endpoint.new(path: "/organization/list-team-members", method: "GET", metadata: organization_openapi("listTeamMembers", "List team members", response: organization_array_schema("TeamMember"))) do |ctx|
|
|
638
639
|
session = Routes.current_session(ctx)
|
|
639
640
|
team_id = normalize_hash(ctx.query)[:team_id] || session[:session]["activeTeamId"]
|
|
640
641
|
raise APIError.new("BAD_REQUEST", message: ORGANIZATION_ERROR_CODES.fetch("YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM")) unless team_id
|
|
@@ -646,7 +647,7 @@ module BetterAuth
|
|
|
646
647
|
end
|
|
647
648
|
|
|
648
649
|
def organization_add_team_member_endpoint(config)
|
|
649
|
-
Endpoint.new(path: "/organization/add-team-member", method: "POST") do |ctx|
|
|
650
|
+
Endpoint.new(path: "/organization/add-team-member", method: "POST", metadata: organization_openapi("addTeamMember", "Add a team member", response: organization_ref_schema("TeamMember"))) do |ctx|
|
|
650
651
|
session = Routes.current_session(ctx)
|
|
651
652
|
body = normalize_hash(ctx.body)
|
|
652
653
|
team = team_by_id(ctx, body[:team_id])
|
|
@@ -667,7 +668,7 @@ module BetterAuth
|
|
|
667
668
|
end
|
|
668
669
|
|
|
669
670
|
def organization_remove_team_member_endpoint(config)
|
|
670
|
-
Endpoint.new(path: "/organization/remove-team-member", method: "POST") do |ctx|
|
|
671
|
+
Endpoint.new(path: "/organization/remove-team-member", method: "POST", metadata: organization_openapi("removeTeamMember", "Remove a team member", response: OpenAPI.status_response_schema)) do |ctx|
|
|
671
672
|
session = Routes.current_session(ctx)
|
|
672
673
|
body = normalize_hash(ctx.body)
|
|
673
674
|
team = team_by_id(ctx, body[:team_id])
|
|
@@ -679,7 +680,7 @@ module BetterAuth
|
|
|
679
680
|
end
|
|
680
681
|
|
|
681
682
|
def organization_create_role_endpoint(config)
|
|
682
|
-
Endpoint.new(path: "/organization/create-role", method: "POST") do |ctx|
|
|
683
|
+
Endpoint.new(path: "/organization/create-role", method: "POST", metadata: organization_openapi("createOrganizationRole", "Create an organization role", response: organization_role_action_schema)) do |ctx|
|
|
683
684
|
session = Routes.current_session(ctx)
|
|
684
685
|
body = normalize_hash(ctx.body)
|
|
685
686
|
organization_id = body[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -698,7 +699,7 @@ module BetterAuth
|
|
|
698
699
|
end
|
|
699
700
|
|
|
700
701
|
def organization_list_roles_endpoint(config)
|
|
701
|
-
Endpoint.new(path: "/organization/list-roles", method: "GET") do |ctx|
|
|
702
|
+
Endpoint.new(path: "/organization/list-roles", method: "GET", metadata: organization_openapi("listOrganizationRoles", "List organization roles", response: {type: "array", items: organization_role_schema})) do |ctx|
|
|
702
703
|
session = Routes.current_session(ctx)
|
|
703
704
|
organization_id = normalize_hash(ctx.query)[:organization_id] || session[:session]["activeOrganizationId"]
|
|
704
705
|
require_org_permission!(ctx, config, session, organization_id, {ac: ["read"]}, ORGANIZATION_ERROR_CODES.fetch("YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE"))
|
|
@@ -709,7 +710,7 @@ module BetterAuth
|
|
|
709
710
|
end
|
|
710
711
|
|
|
711
712
|
def organization_get_role_endpoint(config)
|
|
712
|
-
Endpoint.new(path: "/organization/get-role", method: "GET") do |ctx|
|
|
713
|
+
Endpoint.new(path: "/organization/get-role", method: "GET", metadata: organization_openapi("getOrganizationRole", "Get an organization role", response: organization_role_schema)) do |ctx|
|
|
713
714
|
session = Routes.current_session(ctx)
|
|
714
715
|
query = normalize_hash(ctx.query)
|
|
715
716
|
organization_id = query[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -721,7 +722,7 @@ module BetterAuth
|
|
|
721
722
|
end
|
|
722
723
|
|
|
723
724
|
def organization_update_role_endpoint(config)
|
|
724
|
-
Endpoint.new(path: "/organization/update-role", method: "POST") do |ctx|
|
|
725
|
+
Endpoint.new(path: "/organization/update-role", method: "POST", metadata: organization_openapi("updateOrganizationRole", "Update an organization role", response: organization_role_action_schema)) do |ctx|
|
|
725
726
|
session = Routes.current_session(ctx)
|
|
726
727
|
body = normalize_hash(ctx.body)
|
|
727
728
|
organization_id = body[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -746,7 +747,7 @@ module BetterAuth
|
|
|
746
747
|
end
|
|
747
748
|
|
|
748
749
|
def organization_delete_role_endpoint(config)
|
|
749
|
-
Endpoint.new(path: "/organization/delete-role", method: "POST") do |ctx|
|
|
750
|
+
Endpoint.new(path: "/organization/delete-role", method: "POST", metadata: organization_openapi("deleteOrganizationRole", "Delete an organization role", response: OpenAPI.success_response_schema)) do |ctx|
|
|
750
751
|
session = Routes.current_session(ctx)
|
|
751
752
|
body = normalize_hash(ctx.body)
|
|
752
753
|
organization_id = body[:organization_id] || session[:session]["activeOrganizationId"]
|
|
@@ -766,6 +767,104 @@ module BetterAuth
|
|
|
766
767
|
end
|
|
767
768
|
end
|
|
768
769
|
|
|
770
|
+
def organization_openapi(operation_id, description, response:, response_description: "Success", request: nil, required: [], parameters: nil)
|
|
771
|
+
openapi = {
|
|
772
|
+
operationId: operation_id,
|
|
773
|
+
description: description,
|
|
774
|
+
responses: {
|
|
775
|
+
"200" => OpenAPI.json_response(response_description, response)
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
openapi[:requestBody] = OpenAPI.json_request_body(OpenAPI.object_schema(request, required: required)) if request
|
|
779
|
+
openapi[:parameters] = parameters if parameters
|
|
780
|
+
|
|
781
|
+
{openapi: openapi}
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
def organization_ref_schema(name)
|
|
785
|
+
{
|
|
786
|
+
type: "object",
|
|
787
|
+
"$ref": "#/components/schemas/#{name}"
|
|
788
|
+
}
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def organization_nullable_schema(name)
|
|
792
|
+
{
|
|
793
|
+
type: ["object", "null"],
|
|
794
|
+
"$ref": "#/components/schemas/#{name}"
|
|
795
|
+
}
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
def organization_array_schema(name)
|
|
799
|
+
{
|
|
800
|
+
type: "array",
|
|
801
|
+
items: organization_ref_schema(name)
|
|
802
|
+
}
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
def organization_accept_invitation_schema
|
|
806
|
+
OpenAPI.object_schema(
|
|
807
|
+
{
|
|
808
|
+
invitation: organization_ref_schema("Invitation"),
|
|
809
|
+
member: organization_ref_schema("Member")
|
|
810
|
+
},
|
|
811
|
+
required: ["invitation", "member"]
|
|
812
|
+
)
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
def organization_active_member_role_schema
|
|
816
|
+
OpenAPI.object_schema(
|
|
817
|
+
{
|
|
818
|
+
role: {type: "string"},
|
|
819
|
+
member: organization_ref_schema("Member")
|
|
820
|
+
},
|
|
821
|
+
required: ["role", "member"]
|
|
822
|
+
)
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
def organization_members_response_schema
|
|
826
|
+
OpenAPI.object_schema(
|
|
827
|
+
{
|
|
828
|
+
members: organization_array_schema("Member"),
|
|
829
|
+
total: {type: "number"}
|
|
830
|
+
},
|
|
831
|
+
required: ["members", "total"]
|
|
832
|
+
)
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
def organization_permission_response_schema
|
|
836
|
+
OpenAPI.object_schema(
|
|
837
|
+
{
|
|
838
|
+
error: {type: ["string", "null"]},
|
|
839
|
+
success: {type: "boolean"}
|
|
840
|
+
},
|
|
841
|
+
required: ["success"]
|
|
842
|
+
)
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
def organization_role_schema
|
|
846
|
+
OpenAPI.object_schema(
|
|
847
|
+
{
|
|
848
|
+
id: {type: "string"},
|
|
849
|
+
organizationId: {type: "string"},
|
|
850
|
+
role: {type: "string"},
|
|
851
|
+
permission: {type: "object"}
|
|
852
|
+
},
|
|
853
|
+
required: ["role", "permission"]
|
|
854
|
+
)
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
def organization_role_action_schema
|
|
858
|
+
OpenAPI.object_schema(
|
|
859
|
+
{
|
|
860
|
+
success: {type: "boolean"},
|
|
861
|
+
roleData: organization_role_schema,
|
|
862
|
+
statements: {type: "object"}
|
|
863
|
+
},
|
|
864
|
+
required: ["success", "roleData"]
|
|
865
|
+
)
|
|
866
|
+
end
|
|
867
|
+
|
|
769
868
|
def parse_roles(roles)
|
|
770
869
|
Array(roles).join(",")
|
|
771
870
|
end
|
|
@@ -65,7 +65,38 @@ module BetterAuth
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def sign_in_phone_number_endpoint(config)
|
|
68
|
-
Endpoint.new(
|
|
68
|
+
Endpoint.new(
|
|
69
|
+
path: "/sign-in/phone-number",
|
|
70
|
+
method: "POST",
|
|
71
|
+
metadata: {
|
|
72
|
+
openapi: {
|
|
73
|
+
operationId: "signInPhoneNumber",
|
|
74
|
+
description: "Sign in with phone number and password",
|
|
75
|
+
requestBody: OpenAPI.json_request_body(
|
|
76
|
+
OpenAPI.object_schema(
|
|
77
|
+
{
|
|
78
|
+
phoneNumber: {type: "string"},
|
|
79
|
+
password: {type: "string"},
|
|
80
|
+
rememberMe: {type: ["boolean", "null"]}
|
|
81
|
+
},
|
|
82
|
+
required: ["phoneNumber", "password"]
|
|
83
|
+
)
|
|
84
|
+
),
|
|
85
|
+
responses: {
|
|
86
|
+
"200" => OpenAPI.json_response(
|
|
87
|
+
"Signed in",
|
|
88
|
+
OpenAPI.object_schema(
|
|
89
|
+
{
|
|
90
|
+
token: {type: "string"},
|
|
91
|
+
user: {type: "object", "$ref": "#/components/schemas/User"}
|
|
92
|
+
},
|
|
93
|
+
required: ["token", "user"]
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
) do |ctx|
|
|
69
100
|
body = normalize_hash(ctx.body)
|
|
70
101
|
phone_number = body[:phone_number].to_s
|
|
71
102
|
password = body[:password].to_s
|
|
@@ -101,7 +132,35 @@ module BetterAuth
|
|
|
101
132
|
end
|
|
102
133
|
|
|
103
134
|
def send_phone_number_otp_endpoint(config)
|
|
104
|
-
Endpoint.new(
|
|
135
|
+
Endpoint.new(
|
|
136
|
+
path: "/phone-number/send-otp",
|
|
137
|
+
method: "POST",
|
|
138
|
+
metadata: {
|
|
139
|
+
openapi: {
|
|
140
|
+
operationId: "sendPhoneNumberOTP",
|
|
141
|
+
description: "Send a phone number OTP",
|
|
142
|
+
requestBody: OpenAPI.json_request_body(
|
|
143
|
+
OpenAPI.object_schema(
|
|
144
|
+
{
|
|
145
|
+
phoneNumber: {type: "string"}
|
|
146
|
+
},
|
|
147
|
+
required: ["phoneNumber"]
|
|
148
|
+
)
|
|
149
|
+
),
|
|
150
|
+
responses: {
|
|
151
|
+
"200" => OpenAPI.json_response(
|
|
152
|
+
"OTP sent",
|
|
153
|
+
OpenAPI.object_schema(
|
|
154
|
+
{
|
|
155
|
+
message: {type: "string"}
|
|
156
|
+
},
|
|
157
|
+
required: ["message"]
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
) do |ctx|
|
|
105
164
|
sender = config[:send_otp]
|
|
106
165
|
unless sender.respond_to?(:call)
|
|
107
166
|
raise APIError.new("NOT_IMPLEMENTED", message: PHONE_NUMBER_ERROR_CODES["SEND_OTP_NOT_IMPLEMENTED"])
|
|
@@ -118,7 +177,40 @@ module BetterAuth
|
|
|
118
177
|
end
|
|
119
178
|
|
|
120
179
|
def verify_phone_number_endpoint(config)
|
|
121
|
-
Endpoint.new(
|
|
180
|
+
Endpoint.new(
|
|
181
|
+
path: "/phone-number/verify",
|
|
182
|
+
method: "POST",
|
|
183
|
+
metadata: {
|
|
184
|
+
openapi: {
|
|
185
|
+
operationId: "verifyPhoneNumber",
|
|
186
|
+
description: "Verify a phone number OTP",
|
|
187
|
+
requestBody: OpenAPI.json_request_body(
|
|
188
|
+
OpenAPI.object_schema(
|
|
189
|
+
{
|
|
190
|
+
phoneNumber: {type: "string"},
|
|
191
|
+
code: {type: "string"},
|
|
192
|
+
updatePhoneNumber: {type: ["boolean", "null"]},
|
|
193
|
+
disableSession: {type: ["boolean", "null"]}
|
|
194
|
+
},
|
|
195
|
+
required: ["phoneNumber", "code"]
|
|
196
|
+
)
|
|
197
|
+
),
|
|
198
|
+
responses: {
|
|
199
|
+
"200" => OpenAPI.json_response(
|
|
200
|
+
"Phone number verified",
|
|
201
|
+
OpenAPI.object_schema(
|
|
202
|
+
{
|
|
203
|
+
status: {type: "boolean"},
|
|
204
|
+
token: {type: ["string", "null"]},
|
|
205
|
+
user: {type: "object", "$ref": "#/components/schemas/User"}
|
|
206
|
+
},
|
|
207
|
+
required: ["status", "user"]
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
) do |ctx|
|
|
122
214
|
body = normalize_hash(ctx.body)
|
|
123
215
|
phone_number = body[:phone_number].to_s
|
|
124
216
|
code = body[:code].to_s
|
|
@@ -163,7 +255,27 @@ module BetterAuth
|
|
|
163
255
|
end
|
|
164
256
|
|
|
165
257
|
def request_password_reset_phone_number_endpoint(config)
|
|
166
|
-
Endpoint.new(
|
|
258
|
+
Endpoint.new(
|
|
259
|
+
path: "/phone-number/request-password-reset",
|
|
260
|
+
method: "POST",
|
|
261
|
+
metadata: {
|
|
262
|
+
openapi: {
|
|
263
|
+
operationId: "requestPasswordResetPhoneNumber",
|
|
264
|
+
description: "Request a phone number password reset OTP",
|
|
265
|
+
requestBody: OpenAPI.json_request_body(
|
|
266
|
+
OpenAPI.object_schema(
|
|
267
|
+
{
|
|
268
|
+
phoneNumber: {type: "string"}
|
|
269
|
+
},
|
|
270
|
+
required: ["phoneNumber"]
|
|
271
|
+
)
|
|
272
|
+
),
|
|
273
|
+
responses: {
|
|
274
|
+
"200" => OpenAPI.json_response("Password reset OTP requested", OpenAPI.status_response_schema)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
) do |ctx|
|
|
167
279
|
body = normalize_hash(ctx.body)
|
|
168
280
|
phone_number = body[:phone_number].to_s
|
|
169
281
|
user = ctx.context.adapter.find_one(model: "user", where: [{field: "phoneNumber", value: phone_number}])
|
|
@@ -179,7 +291,29 @@ module BetterAuth
|
|
|
179
291
|
end
|
|
180
292
|
|
|
181
293
|
def reset_password_phone_number_endpoint(config)
|
|
182
|
-
Endpoint.new(
|
|
294
|
+
Endpoint.new(
|
|
295
|
+
path: "/phone-number/reset-password",
|
|
296
|
+
method: "POST",
|
|
297
|
+
metadata: {
|
|
298
|
+
openapi: {
|
|
299
|
+
operationId: "resetPasswordPhoneNumber",
|
|
300
|
+
description: "Reset a password with a phone number OTP",
|
|
301
|
+
requestBody: OpenAPI.json_request_body(
|
|
302
|
+
OpenAPI.object_schema(
|
|
303
|
+
{
|
|
304
|
+
phoneNumber: {type: "string"},
|
|
305
|
+
otp: {type: "string"},
|
|
306
|
+
newPassword: {type: "string"}
|
|
307
|
+
},
|
|
308
|
+
required: ["phoneNumber", "otp", "newPassword"]
|
|
309
|
+
)
|
|
310
|
+
),
|
|
311
|
+
responses: {
|
|
312
|
+
"200" => OpenAPI.json_response("Password reset", OpenAPI.status_response_schema)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
) do |ctx|
|
|
183
317
|
body = normalize_hash(ctx.body)
|
|
184
318
|
phone_number = body[:phone_number].to_s
|
|
185
319
|
otp = body[:otp].to_s
|
|
@@ -225,7 +359,8 @@ module BetterAuth
|
|
|
225
359
|
"phoneNumber" => phone_number,
|
|
226
360
|
"phoneNumberVerified" => true,
|
|
227
361
|
"emailVerified" => false
|
|
228
|
-
)
|
|
362
|
+
),
|
|
363
|
+
context: ctx
|
|
229
364
|
)
|
|
230
365
|
end
|
|
231
366
|
|