better_auth 0.1.1 → 0.3.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.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +110 -18
  4. data/lib/better_auth/adapters/base.rb +49 -0
  5. data/lib/better_auth/adapters/internal_adapter.rb +589 -0
  6. data/lib/better_auth/adapters/memory.rb +235 -0
  7. data/lib/better_auth/adapters/mongodb.rb +9 -0
  8. data/lib/better_auth/adapters/mssql.rb +42 -0
  9. data/lib/better_auth/adapters/mysql.rb +33 -0
  10. data/lib/better_auth/adapters/postgres.rb +17 -0
  11. data/lib/better_auth/adapters/sql.rb +441 -0
  12. data/lib/better_auth/adapters/sqlite.rb +20 -0
  13. data/lib/better_auth/api.rb +226 -0
  14. data/lib/better_auth/api_error.rb +53 -0
  15. data/lib/better_auth/auth.rb +42 -0
  16. data/lib/better_auth/configuration.rb +399 -0
  17. data/lib/better_auth/context.rb +211 -0
  18. data/lib/better_auth/cookies.rb +278 -0
  19. data/lib/better_auth/core.rb +37 -1
  20. data/lib/better_auth/crypto/jwe.rb +76 -0
  21. data/lib/better_auth/crypto.rb +191 -0
  22. data/lib/better_auth/database_hooks.rb +114 -0
  23. data/lib/better_auth/endpoint.rb +326 -0
  24. data/lib/better_auth/error.rb +52 -0
  25. data/lib/better_auth/middleware/origin_check.rb +128 -0
  26. data/lib/better_auth/password.rb +120 -0
  27. data/lib/better_auth/plugin.rb +142 -0
  28. data/lib/better_auth/plugin_context.rb +16 -0
  29. data/lib/better_auth/plugin_registry.rb +67 -0
  30. data/lib/better_auth/plugins/access.rb +87 -0
  31. data/lib/better_auth/plugins/additional_fields.rb +29 -0
  32. data/lib/better_auth/plugins/admin/schema.rb +28 -0
  33. data/lib/better_auth/plugins/admin.rb +518 -0
  34. data/lib/better_auth/plugins/anonymous.rb +198 -0
  35. data/lib/better_auth/plugins/api_key.rb +16 -0
  36. data/lib/better_auth/plugins/bearer.rb +128 -0
  37. data/lib/better_auth/plugins/captcha.rb +159 -0
  38. data/lib/better_auth/plugins/custom_session.rb +84 -0
  39. data/lib/better_auth/plugins/device_authorization.rb +302 -0
  40. data/lib/better_auth/plugins/email_otp.rb +536 -0
  41. data/lib/better_auth/plugins/expo.rb +88 -0
  42. data/lib/better_auth/plugins/generic_oauth.rb +780 -0
  43. data/lib/better_auth/plugins/have_i_been_pwned.rb +94 -0
  44. data/lib/better_auth/plugins/jwt.rb +482 -0
  45. data/lib/better_auth/plugins/last_login_method.rb +92 -0
  46. data/lib/better_auth/plugins/magic_link.rb +181 -0
  47. data/lib/better_auth/plugins/mcp.rb +342 -0
  48. data/lib/better_auth/plugins/multi_session.rb +173 -0
  49. data/lib/better_auth/plugins/oauth_protocol.rb +694 -0
  50. data/lib/better_auth/plugins/oauth_provider.rb +16 -0
  51. data/lib/better_auth/plugins/oauth_proxy.rb +257 -0
  52. data/lib/better_auth/plugins/oidc_provider.rb +597 -0
  53. data/lib/better_auth/plugins/one_tap.rb +154 -0
  54. data/lib/better_auth/plugins/one_time_token.rb +106 -0
  55. data/lib/better_auth/plugins/open_api.rb +489 -0
  56. data/lib/better_auth/plugins/organization/schema.rb +106 -0
  57. data/lib/better_auth/plugins/organization.rb +995 -0
  58. data/lib/better_auth/plugins/passkey.rb +16 -0
  59. data/lib/better_auth/plugins/phone_number.rb +321 -0
  60. data/lib/better_auth/plugins/scim.rb +16 -0
  61. data/lib/better_auth/plugins/siwe.rb +242 -0
  62. data/lib/better_auth/plugins/sso.rb +16 -0
  63. data/lib/better_auth/plugins/stripe.rb +16 -0
  64. data/lib/better_auth/plugins/two_factor.rb +514 -0
  65. data/lib/better_auth/plugins/username.rb +278 -0
  66. data/lib/better_auth/plugins.rb +46 -0
  67. data/lib/better_auth/rate_limiter.rb +232 -0
  68. data/lib/better_auth/request_ip.rb +70 -0
  69. data/lib/better_auth/router.rb +378 -0
  70. data/lib/better_auth/routes/account.rb +211 -0
  71. data/lib/better_auth/routes/email_verification.rb +111 -0
  72. data/lib/better_auth/routes/error.rb +102 -0
  73. data/lib/better_auth/routes/ok.rb +15 -0
  74. data/lib/better_auth/routes/password.rb +183 -0
  75. data/lib/better_auth/routes/session.rb +160 -0
  76. data/lib/better_auth/routes/sign_in.rb +90 -0
  77. data/lib/better_auth/routes/sign_out.rb +15 -0
  78. data/lib/better_auth/routes/sign_up.rb +196 -0
  79. data/lib/better_auth/routes/social.rb +367 -0
  80. data/lib/better_auth/routes/user.rb +205 -0
  81. data/lib/better_auth/schema/sql.rb +202 -0
  82. data/lib/better_auth/schema.rb +291 -0
  83. data/lib/better_auth/session.rb +122 -0
  84. data/lib/better_auth/session_store.rb +91 -0
  85. data/lib/better_auth/social_providers/apple.rb +91 -0
  86. data/lib/better_auth/social_providers/atlassian.rb +32 -0
  87. data/lib/better_auth/social_providers/base.rb +325 -0
  88. data/lib/better_auth/social_providers/cognito.rb +32 -0
  89. data/lib/better_auth/social_providers/discord.rb +81 -0
  90. data/lib/better_auth/social_providers/dropbox.rb +33 -0
  91. data/lib/better_auth/social_providers/facebook.rb +35 -0
  92. data/lib/better_auth/social_providers/figma.rb +31 -0
  93. data/lib/better_auth/social_providers/github.rb +74 -0
  94. data/lib/better_auth/social_providers/gitlab.rb +67 -0
  95. data/lib/better_auth/social_providers/google.rb +90 -0
  96. data/lib/better_auth/social_providers/huggingface.rb +31 -0
  97. data/lib/better_auth/social_providers/kakao.rb +32 -0
  98. data/lib/better_auth/social_providers/kick.rb +32 -0
  99. data/lib/better_auth/social_providers/line.rb +33 -0
  100. data/lib/better_auth/social_providers/linear.rb +44 -0
  101. data/lib/better_auth/social_providers/linkedin.rb +30 -0
  102. data/lib/better_auth/social_providers/microsoft_entra_id.rb +137 -0
  103. data/lib/better_auth/social_providers/naver.rb +31 -0
  104. data/lib/better_auth/social_providers/notion.rb +33 -0
  105. data/lib/better_auth/social_providers/paybin.rb +31 -0
  106. data/lib/better_auth/social_providers/paypal.rb +36 -0
  107. data/lib/better_auth/social_providers/polar.rb +31 -0
  108. data/lib/better_auth/social_providers/railway.rb +49 -0
  109. data/lib/better_auth/social_providers/reddit.rb +32 -0
  110. data/lib/better_auth/social_providers/roblox.rb +31 -0
  111. data/lib/better_auth/social_providers/salesforce.rb +38 -0
  112. data/lib/better_auth/social_providers/slack.rb +30 -0
  113. data/lib/better_auth/social_providers/spotify.rb +31 -0
  114. data/lib/better_auth/social_providers/tiktok.rb +35 -0
  115. data/lib/better_auth/social_providers/twitch.rb +39 -0
  116. data/lib/better_auth/social_providers/twitter.rb +32 -0
  117. data/lib/better_auth/social_providers/vercel.rb +47 -0
  118. data/lib/better_auth/social_providers/vk.rb +34 -0
  119. data/lib/better_auth/social_providers/wechat.rb +104 -0
  120. data/lib/better_auth/social_providers/zoom.rb +31 -0
  121. data/lib/better_auth/social_providers.rb +38 -0
  122. data/lib/better_auth/version.rb +1 -1
  123. data/lib/better_auth.rb +86 -2
  124. metadata +233 -21
  125. data/.ruby-version +0 -1
  126. data/.standard.yml +0 -12
  127. data/.vscode/settings.json +0 -22
  128. data/AGENTS.md +0 -50
  129. data/CLAUDE.md +0 -1
  130. data/CODE_OF_CONDUCT.md +0 -173
  131. data/CONTRIBUTING.md +0 -187
  132. data/Gemfile +0 -12
  133. data/Makefile +0 -207
  134. data/Rakefile +0 -25
  135. data/SECURITY.md +0 -28
  136. data/docker-compose.yml +0 -63
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def reddit(client_id:, client_secret:, scopes: ["identity"], **options)
8
+ Base.oauth_provider(
9
+ id: "reddit",
10
+ name: "Reddit",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://www.reddit.com/api/v1/authorize",
14
+ token_endpoint: "https://www.reddit.com/api/v1/access_token",
15
+ user_info_endpoint: "https://oauth.reddit.com/api/v1/me",
16
+ user_info_headers: {"User-Agent" => "better-auth"},
17
+ scopes: scopes,
18
+ auth_params: ->(_data, opts) { {duration: opts[:duration]} },
19
+ profile_map: ->(profile) {
20
+ {
21
+ id: profile["id"],
22
+ name: profile["name"],
23
+ email: profile["oauth_client_id"],
24
+ image: profile["icon_img"].to_s.split("?").first,
25
+ emailVerified: !!profile["has_verified_email"]
26
+ }
27
+ },
28
+ **options
29
+ )
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def roblox(client_id:, client_secret:, scopes: ["openid", "profile"], **options)
8
+ Base.oauth_provider(
9
+ id: "roblox",
10
+ name: "Roblox",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://apis.roblox.com/oauth/v1/authorize",
14
+ token_endpoint: "https://apis.roblox.com/oauth/v1/token",
15
+ user_info_endpoint: "https://apis.roblox.com/oauth/v1/userinfo",
16
+ scopes: scopes,
17
+ auth_params: ->(_data, opts) { {prompt: opts[:prompt] || "select_account consent"} },
18
+ profile_map: ->(profile) {
19
+ {
20
+ id: profile["sub"],
21
+ name: profile["nickname"] || profile["preferred_username"] || "",
22
+ email: profile["preferred_username"],
23
+ image: profile["picture"],
24
+ emailVerified: false
25
+ }
26
+ },
27
+ **options
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def salesforce(client_id:, client_secret:, scopes: ["openid", "email", "profile"], **options)
8
+ host = if options[:loginUrl] || options[:login_url]
9
+ "https://#{options[:loginUrl] || options[:login_url]}"
10
+ elsif options[:environment].to_s == "sandbox"
11
+ "https://test.salesforce.com"
12
+ else
13
+ "https://login.salesforce.com"
14
+ end
15
+ Base.oauth_provider(
16
+ id: "salesforce",
17
+ name: "Salesforce",
18
+ client_id: client_id,
19
+ client_secret: client_secret,
20
+ authorization_endpoint: "#{host}/services/oauth2/authorize",
21
+ token_endpoint: "#{host}/services/oauth2/token",
22
+ user_info_endpoint: "#{host}/services/oauth2/userinfo",
23
+ scopes: scopes,
24
+ pkce: true,
25
+ profile_map: ->(profile) {
26
+ {
27
+ id: profile["user_id"],
28
+ name: profile["name"],
29
+ email: profile["email"],
30
+ image: profile.dig("photos", "picture") || profile.dig("photos", "thumbnail"),
31
+ emailVerified: !!profile["email_verified"]
32
+ }
33
+ },
34
+ **options
35
+ )
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def slack(client_id:, client_secret:, scopes: ["openid", "profile", "email"], **options)
8
+ Base.oauth_provider(
9
+ id: "slack",
10
+ name: "Slack",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://slack.com/openid/connect/authorize",
14
+ token_endpoint: "https://slack.com/api/openid.connect.token",
15
+ user_info_endpoint: "https://slack.com/api/openid.connect.userInfo",
16
+ scopes: scopes,
17
+ profile_map: ->(profile) {
18
+ {
19
+ id: profile["https://slack.com/user_id"] || profile["sub"],
20
+ name: profile["name"] || "",
21
+ email: profile["email"],
22
+ image: profile["picture"] || profile["https://slack.com/user_image_512"],
23
+ emailVerified: !!profile["email_verified"]
24
+ }
25
+ },
26
+ **options
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def spotify(client_id:, client_secret:, scopes: ["user-read-email"], **options)
8
+ Base.oauth_provider(
9
+ id: "spotify",
10
+ name: "Spotify",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://accounts.spotify.com/authorize",
14
+ token_endpoint: "https://accounts.spotify.com/api/token",
15
+ user_info_endpoint: "https://api.spotify.com/v1/me",
16
+ scopes: scopes,
17
+ pkce: true,
18
+ profile_map: ->(profile) {
19
+ {
20
+ id: profile["id"],
21
+ name: profile["display_name"],
22
+ email: profile["email"],
23
+ image: Array(profile["images"]).first&.fetch("url", nil),
24
+ emailVerified: false
25
+ }
26
+ },
27
+ **options
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def tiktok(client_id:, client_secret:, scopes: ["user.info.profile"], **options)
8
+ client_key = options[:client_key] || options[:clientKey] || client_id
9
+ Base.oauth_provider(
10
+ id: "tiktok",
11
+ name: "TikTok",
12
+ client_id: client_key,
13
+ client_secret: client_secret,
14
+ authorization_endpoint: "https://www.tiktok.com/v2/auth/authorize",
15
+ token_endpoint: "https://open.tiktokapis.com/v2/oauth/token/",
16
+ user_info_endpoint: "https://open.tiktokapis.com/v2/user/info/?fields=open_id,avatar_large_url,display_name,username",
17
+ scopes: scopes,
18
+ scope_separator: ",",
19
+ auth_params: {client_key: client_key},
20
+ token_params: {client_key: client_key},
21
+ profile_map: ->(profile) {
22
+ user = profile.dig("data", "user") || profile
23
+ {
24
+ id: user["open_id"],
25
+ name: user["display_name"] || user["username"] || "",
26
+ email: user["email"] || user["username"],
27
+ image: user["avatar_large_url"],
28
+ emailVerified: false
29
+ }
30
+ },
31
+ **options
32
+ )
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def twitch(client_id:, client_secret:, scopes: ["user:read:email", "openid"], **options)
8
+ Base.oauth_provider(
9
+ id: "twitch",
10
+ name: "Twitch",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://id.twitch.tv/oauth2/authorize",
14
+ token_endpoint: "https://id.twitch.tv/oauth2/token",
15
+ scopes: scopes,
16
+ auth_params: {
17
+ claims: JSON.generate({
18
+ userinfo: {
19
+ email: nil,
20
+ email_verified: nil,
21
+ preferred_username: nil,
22
+ picture: nil
23
+ }
24
+ })
25
+ },
26
+ profile_map: ->(profile) {
27
+ {
28
+ id: profile["sub"],
29
+ name: profile["preferred_username"],
30
+ email: profile["email"],
31
+ image: profile["picture"],
32
+ emailVerified: !!profile["email_verified"]
33
+ }
34
+ },
35
+ **options
36
+ )
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def twitter(client_id:, client_secret:, scopes: ["users.read", "tweet.read", "offline.access", "users.email"], **options)
8
+ Base.oauth_provider(
9
+ id: "twitter",
10
+ name: "Twitter",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://x.com/i/oauth2/authorize",
14
+ token_endpoint: "https://api.x.com/2/oauth2/token",
15
+ user_info_endpoint: "https://api.x.com/2/users/me?user.fields=profile_image_url,verified",
16
+ scopes: scopes,
17
+ pkce: true,
18
+ profile_map: ->(profile) {
19
+ data = profile["data"] || profile
20
+ {
21
+ id: data["id"],
22
+ name: data["name"],
23
+ email: data["email"] || data["username"],
24
+ image: data["profile_image_url"],
25
+ emailVerified: !!data["confirmed_email"]
26
+ }
27
+ },
28
+ **options
29
+ )
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def vercel(client_id:, client_secret:, scopes: [], **options)
8
+ provider = Base.oauth_provider(
9
+ id: "vercel",
10
+ name: "Vercel",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://vercel.com/oauth/authorize",
14
+ token_endpoint: "https://api.vercel.com/login/oauth/token",
15
+ user_info_endpoint: "https://api.vercel.com/login/oauth/userinfo",
16
+ scopes: scopes,
17
+ pkce: true,
18
+ profile_map: ->(profile) {
19
+ {
20
+ id: profile["sub"],
21
+ name: profile["name"] || profile["preferred_username"] || "",
22
+ email: profile["email"],
23
+ image: profile["picture"],
24
+ emailVerified: !!profile["email_verified"]
25
+ }
26
+ },
27
+ **options
28
+ )
29
+ provider[:create_authorization_url] = lambda do |data|
30
+ verifier = data[:code_verifier] || data[:codeVerifier]
31
+ raise Error, "codeVerifier is required for Vercel" if verifier.to_s.empty?
32
+
33
+ selected_scopes = Base.selected_scopes(scopes, Base.normalize_options(options), data)
34
+ Base.authorization_url(options[:authorization_endpoint] || "https://vercel.com/oauth/authorize", {
35
+ client_id: Base.primary_client_id(client_id),
36
+ redirect_uri: options[:redirect_uri] || options[:redirectURI] || data[:redirect_uri] || data[:redirectURI],
37
+ response_type: "code",
38
+ scope: selected_scopes.empty? ? nil : selected_scopes,
39
+ state: data[:state],
40
+ code_challenge: Base.pkce_challenge(verifier),
41
+ code_challenge_method: "S256"
42
+ })
43
+ end
44
+ provider
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def vk(client_id:, client_secret:, scopes: ["email", "phone"], **options)
8
+ Base.oauth_provider(
9
+ id: "vk",
10
+ name: "VK",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://id.vk.com/authorize",
14
+ token_endpoint: "https://id.vk.com/oauth2/auth",
15
+ user_info_endpoint: "https://id.vk.com/oauth2/user_info",
16
+ user_info_method: :post,
17
+ user_info_body: {client_id: client_id},
18
+ scopes: scopes,
19
+ pkce: true,
20
+ profile_map: ->(profile) {
21
+ user = profile["user"] || profile
22
+ {
23
+ id: user["user_id"],
24
+ name: [user["first_name"], user["last_name"]].compact.join(" "),
25
+ email: user["email"],
26
+ image: user["avatar"],
27
+ emailVerified: false
28
+ }
29
+ },
30
+ **options
31
+ )
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def wechat(client_id:, client_secret:, scopes: ["snsapi_login"], **options)
8
+ normalized = Base.normalize_options(options)
9
+ provider = Base.oauth_provider(
10
+ id: "wechat",
11
+ name: "WeChat",
12
+ client_id: client_id,
13
+ client_secret: client_secret,
14
+ authorization_endpoint: "https://open.weixin.qq.com/connect/qrconnect",
15
+ token_endpoint: "https://api.weixin.qq.com/sns/oauth2/access_token",
16
+ user_info_endpoint: "https://api.weixin.qq.com/sns/userinfo",
17
+ scopes: scopes,
18
+ scope_separator: ",",
19
+ profile_map: ->(profile) {
20
+ {
21
+ id: profile["unionid"] || profile["openid"],
22
+ name: profile["nickname"],
23
+ email: profile["email"],
24
+ image: profile["headimgurl"],
25
+ emailVerified: false
26
+ }
27
+ },
28
+ **options
29
+ )
30
+ provider[:create_authorization_url] = lambda do |data|
31
+ "#{Base.authorization_url("https://open.weixin.qq.com/connect/qrconnect", {
32
+ appid: client_id,
33
+ redirect_uri: normalized[:redirect_uri] || data[:redirect_uri] || data[:redirectURI],
34
+ response_type: "code",
35
+ scope: Base.selected_scopes(scopes, normalized, data).join(","),
36
+ state: data[:state],
37
+ lang: options[:lang] || "cn"
38
+ })}#wechat_redirect"
39
+ end
40
+ provider[:validate_authorization_code] = lambda do |data|
41
+ url = Base.authorization_url("https://api.weixin.qq.com/sns/oauth2/access_token", {
42
+ appid: client_id,
43
+ secret: client_secret,
44
+ code: data[:code],
45
+ grant_type: "authorization_code"
46
+ })
47
+ payload = Base.get_json(url)
48
+ if !payload || payload["errcode"]
49
+ raise Error, "Failed to validate authorization code: #{payload&.fetch("errmsg", nil) || "Unknown error"}"
50
+ end
51
+
52
+ Base.normalize_tokens(payload).merge(
53
+ "openid" => payload["openid"],
54
+ "unionid" => payload["unionid"]
55
+ ).compact
56
+ end
57
+ provider[:refresh_access_token] = normalized[:refresh_access_token] || lambda do |refresh_token|
58
+ url = Base.authorization_url("https://api.weixin.qq.com/sns/oauth2/refresh_token", {
59
+ appid: client_id,
60
+ grant_type: "refresh_token",
61
+ refresh_token: refresh_token
62
+ })
63
+ payload = Base.get_json(url)
64
+ if !payload || payload["errcode"]
65
+ raise Error, "Failed to refresh access token: #{payload&.fetch("errmsg", nil) || "Unknown error"}"
66
+ end
67
+
68
+ Base.normalize_tokens(payload).merge(
69
+ "openid" => payload["openid"],
70
+ "unionid" => payload["unionid"]
71
+ ).compact
72
+ end
73
+ provider[:get_user_info] = lambda do |tokens|
74
+ custom = normalized[:get_user_info]
75
+ next custom.call(tokens) if custom
76
+
77
+ openid = tokens["openid"] || tokens[:openid]
78
+ next nil if openid.to_s.empty?
79
+
80
+ url = Base.authorization_url("https://api.weixin.qq.com/sns/userinfo", {
81
+ access_token: Base.access_token(tokens),
82
+ openid: openid,
83
+ lang: "zh_CN"
84
+ })
85
+ profile = Base.get_json(url)
86
+ next nil if !profile || profile["errcode"]
87
+
88
+ user = Base.apply_profile_mapping(
89
+ {
90
+ id: profile["unionid"] || profile["openid"] || openid,
91
+ name: profile["nickname"],
92
+ email: profile["email"],
93
+ image: profile["headimgurl"],
94
+ emailVerified: false
95
+ },
96
+ profile,
97
+ normalized
98
+ )
99
+ {user: user, data: profile}
100
+ end
101
+ provider
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterAuth
4
+ module SocialProviders
5
+ module_function
6
+
7
+ def zoom(client_id:, client_secret:, scopes: [], **options)
8
+ Base.oauth_provider(
9
+ id: "zoom",
10
+ name: "Zoom",
11
+ client_id: client_id,
12
+ client_secret: client_secret,
13
+ authorization_endpoint: "https://zoom.us/oauth/authorize",
14
+ token_endpoint: "https://zoom.us/oauth/token",
15
+ user_info_endpoint: "https://api.zoom.us/v2/users/me",
16
+ scopes: scopes,
17
+ pkce: options.fetch(:pkce, true),
18
+ profile_map: ->(profile) {
19
+ {
20
+ id: profile["id"],
21
+ name: profile["display_name"],
22
+ email: profile["email"],
23
+ image: profile["pic_url"],
24
+ emailVerified: !!profile["verified"]
25
+ }
26
+ },
27
+ **options
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "social_providers/base"
4
+ require_relative "social_providers/google"
5
+ require_relative "social_providers/github"
6
+ require_relative "social_providers/gitlab"
7
+ require_relative "social_providers/discord"
8
+ require_relative "social_providers/apple"
9
+ require_relative "social_providers/microsoft_entra_id"
10
+ require_relative "social_providers/atlassian"
11
+ require_relative "social_providers/cognito"
12
+ require_relative "social_providers/dropbox"
13
+ require_relative "social_providers/facebook"
14
+ require_relative "social_providers/figma"
15
+ require_relative "social_providers/huggingface"
16
+ require_relative "social_providers/kakao"
17
+ require_relative "social_providers/kick"
18
+ require_relative "social_providers/line"
19
+ require_relative "social_providers/linear"
20
+ require_relative "social_providers/linkedin"
21
+ require_relative "social_providers/naver"
22
+ require_relative "social_providers/notion"
23
+ require_relative "social_providers/paybin"
24
+ require_relative "social_providers/paypal"
25
+ require_relative "social_providers/polar"
26
+ require_relative "social_providers/railway"
27
+ require_relative "social_providers/reddit"
28
+ require_relative "social_providers/roblox"
29
+ require_relative "social_providers/salesforce"
30
+ require_relative "social_providers/slack"
31
+ require_relative "social_providers/spotify"
32
+ require_relative "social_providers/tiktok"
33
+ require_relative "social_providers/twitch"
34
+ require_relative "social_providers/twitter"
35
+ require_relative "social_providers/vercel"
36
+ require_relative "social_providers/vk"
37
+ require_relative "social_providers/wechat"
38
+ require_relative "social_providers/zoom"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BetterAuth
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/better_auth.rb CHANGED
@@ -2,8 +2,92 @@
2
2
 
3
3
  require_relative "better_auth/version"
4
4
  require_relative "better_auth/core"
5
+ require_relative "better_auth/error"
6
+ require_relative "better_auth/api_error"
7
+ require_relative "better_auth/crypto"
8
+ require_relative "better_auth/password"
9
+ require_relative "better_auth/plugin"
10
+ require_relative "better_auth/configuration"
11
+ require_relative "better_auth/schema"
12
+ require_relative "better_auth/schema/sql"
13
+ require_relative "better_auth/adapters/base"
14
+ require_relative "better_auth/adapters/memory"
15
+ require_relative "better_auth/adapters/sql"
16
+ require_relative "better_auth/adapters/postgres"
17
+ require_relative "better_auth/adapters/mysql"
18
+ require_relative "better_auth/adapters/sqlite"
19
+ require_relative "better_auth/adapters/mssql"
20
+ require_relative "better_auth/database_hooks"
21
+ require_relative "better_auth/adapters/internal_adapter"
22
+ require_relative "better_auth/context"
23
+ require_relative "better_auth/plugin_context"
24
+ require_relative "better_auth/plugin_registry"
25
+ require_relative "better_auth/plugins"
26
+ require_relative "better_auth/plugins/additional_fields"
27
+ require_relative "better_auth/plugins/custom_session"
28
+ require_relative "better_auth/plugins/multi_session"
29
+ require_relative "better_auth/plugins/last_login_method"
30
+ require_relative "better_auth/plugins/bearer"
31
+ require_relative "better_auth/plugins/jwt"
32
+ require_relative "better_auth/plugins/open_api"
33
+ require_relative "better_auth/plugins/access"
34
+ require_relative "better_auth/plugins/username"
35
+ require_relative "better_auth/plugins/anonymous"
36
+ require_relative "better_auth/plugins/magic_link"
37
+ require_relative "better_auth/plugins/email_otp"
38
+ require_relative "better_auth/plugins/phone_number"
39
+ require_relative "better_auth/plugins/one_time_token"
40
+ require_relative "better_auth/plugins/one_tap"
41
+ require_relative "better_auth/plugins/siwe"
42
+ require_relative "better_auth/plugins/generic_oauth"
43
+ require_relative "better_auth/plugins/oauth_proxy"
44
+ require_relative "better_auth/plugins/passkey"
45
+ require_relative "better_auth/plugins/organization/schema"
46
+ require_relative "better_auth/plugins/organization"
47
+ require_relative "better_auth/plugins/admin/schema"
48
+ require_relative "better_auth/plugins/admin"
49
+ require_relative "better_auth/plugins/oauth_protocol"
50
+ require_relative "better_auth/plugins/oidc_provider"
51
+ require_relative "better_auth/plugins/oauth_provider"
52
+ require_relative "better_auth/plugins/device_authorization"
53
+ require_relative "better_auth/plugins/mcp"
54
+ require_relative "better_auth/plugins/two_factor"
55
+ require_relative "better_auth/plugins/captcha"
56
+ require_relative "better_auth/plugins/have_i_been_pwned"
57
+ require_relative "better_auth/plugins/api_key"
58
+ require_relative "better_auth/plugins/sso"
59
+ require_relative "better_auth/plugins/scim"
60
+ require_relative "better_auth/social_providers"
61
+ %w[
62
+ better_auth/plugins/stripe
63
+ better_auth/plugins/expo
64
+ ].each do |optional_plugin|
65
+ require_relative optional_plugin if File.file?(File.expand_path("#{optional_plugin}.rb", __dir__))
66
+ end
67
+ require_relative "better_auth/session_store"
68
+ require_relative "better_auth/cookies"
69
+ require_relative "better_auth/session"
70
+ require_relative "better_auth/endpoint"
71
+ require_relative "better_auth/routes/ok"
72
+ require_relative "better_auth/routes/error"
73
+ require_relative "better_auth/routes/sign_up"
74
+ require_relative "better_auth/routes/sign_in"
75
+ require_relative "better_auth/routes/sign_out"
76
+ require_relative "better_auth/routes/session"
77
+ require_relative "better_auth/routes/password"
78
+ require_relative "better_auth/routes/email_verification"
79
+ require_relative "better_auth/routes/user"
80
+ require_relative "better_auth/routes/account"
81
+ require_relative "better_auth/routes/social"
82
+ require_relative "better_auth/api"
83
+ require_relative "better_auth/rate_limiter"
84
+ require_relative "better_auth/request_ip"
85
+ require_relative "better_auth/middleware/origin_check"
86
+ require_relative "better_auth/router"
87
+ require_relative "better_auth/auth"
5
88
 
6
89
  module BetterAuth
7
- # Main entry point for Better Auth
8
- # This module provides the core authentication functionality
90
+ def self.auth(options = {})
91
+ Auth.new(options)
92
+ end
9
93
  end