Module: BetterAuth::SocialProviders
- Defined in:
- lib/better_auth/social_providers/base.rb,
lib/better_auth/social_providers/apple.rb,
lib/better_auth/social_providers/github.rb,
lib/better_auth/social_providers/gitlab.rb,
lib/better_auth/social_providers/google.rb,
lib/better_auth/social_providers/discord.rb,
lib/better_auth/social_providers/microsoft_entra_id.rb
Defined Under Namespace
Modules: Base
Class Method Summary collapse
- .apple(client_id:, client_secret:, scopes: ["email", "name"], **options) ⇒ Object
- .discord(client_id:, client_secret:, scopes: ["identify", "email"], **options) ⇒ Object
- .discord_avatar_url(profile) ⇒ Object
- .github(client_id:, client_secret:, scopes: ["read:user", "user:email"], **options) ⇒ Object
- .gitlab(client_id:, client_secret:, issuer: "https://gitlab.com", scopes: ["read_user"], **options) ⇒ Object
- .google(client_id:, client_secret:, scopes: ["openid", "email", "profile"], **options) ⇒ Object
- .microsoft_email_verified?(profile, email) ⇒ Boolean
- .microsoft_entra_id(client_id:, client_secret:, tenant_id: "common", scopes: ["openid", "profile", "email", "User.Read", "offline_access"], **options) ⇒ Object
Class Method Details
.apple(client_id:, client_secret:, scopes: ["email", "name"], **options) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/better_auth/social_providers/apple.rb', line 7 def apple(client_id:, client_secret:, scopes: ["email", "name"], **) { id: "apple", name: "Apple", client_id: client_id, client_secret: client_secret, create_authorization_url: lambda do |data| Base.([:authorization_endpoint] || "https://appleid.apple.com/auth/authorize", { client_id: client_id, redirect_uri: data[:redirect_uri] || data[:redirectURI], response_type: "code id_token", response_mode: [:response_mode] || [:responseMode] || "form_post", scope: data[:scopes] || scopes, state: data[:state] }) end, validate_authorization_code: lambda do |data| Base.post_form("https://appleid.apple.com/auth/token", { client_id: client_id, client_secret: client_secret, code: data[:code], code_verifier: data[:code_verifier] || data[:codeVerifier], grant_type: "authorization_code", redirect_uri: data[:redirect_uri] || data[:redirectURI] }) end, get_user_info: lambda do |tokens| profile = Base.decode_jwt_payload(Base.id_token(tokens)) apple_user = tokens[:user] || tokens["user"] || {} name = apple_user.dig(:name, :firstName) || apple_user.dig("name", "firstName") last_name = apple_user.dig(:name, :lastName) || apple_user.dig("name", "lastName") full_name = [name, last_name].compact.join(" ").strip full_name = profile["name"] || " " if full_name.empty? { user: { id: profile["sub"], email: profile["email"], name: full_name, image: profile["picture"], emailVerified: profile["email_verified"] == true || profile["email_verified"] == "true" }, data: profile.merge("name" => full_name) } end } end |
.discord(client_id:, client_secret:, scopes: ["identify", "email"], **options) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/better_auth/social_providers/discord.rb', line 7 def discord(client_id:, client_secret:, scopes: ["identify", "email"], **) { id: "discord", name: "Discord", client_id: client_id, client_secret: client_secret, create_authorization_url: lambda do |data| selected_scopes = data[:scopes] || scopes params = { client_id: client_id, redirect_uri: data[:redirect_uri] || data[:redirectURI], response_type: "code", scope: selected_scopes, state: data[:state], prompt: .fetch(:prompt, "none") } params[:permissions] = [:permissions] if selected_scopes.include?("bot") && .key?(:permissions) Base.("https://discord.com/api/oauth2/authorize", params) end, validate_authorization_code: lambda do |data| Base.post_form("https://discord.com/api/oauth2/token", { client_id: client_id, client_secret: client_secret, code: data[:code], grant_type: "authorization_code", redirect_uri: data[:redirect_uri] || data[:redirectURI] }) end, get_user_info: lambda do |tokens| profile = Base.get_json("https://discord.com/api/users/@me", "Authorization" => "Bearer #{Base.access_token(tokens)}") { user: { id: profile["id"], email: profile["email"], name: profile["global_name"] || profile["username"] || "", image: discord_avatar_url(profile), emailVerified: !!profile["verified"] }, data: profile } end } end |
.discord_avatar_url(profile) ⇒ Object
51 52 53 54 55 56 57 |
# File 'lib/better_auth/social_providers/discord.rb', line 51 def discord_avatar_url(profile) avatar = profile["avatar"] return nil unless avatar format = avatar.start_with?("a_") ? "gif" : "png" "https://cdn.discordapp.com/avatars/#{profile["id"]}/#{avatar}.#{format}" end |
.github(client_id:, client_secret:, scopes: ["read:user", "user:email"], **options) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/better_auth/social_providers/github.rb', line 7 def github(client_id:, client_secret:, scopes: ["read:user", "user:email"], **) { id: "github", name: "GitHub", client_id: client_id, client_secret: client_secret, create_authorization_url: lambda do |data| Base.([:authorization_endpoint] || "https://github.com/login/oauth/authorize", { client_id: client_id, redirect_uri: data[:redirect_uri] || data[:redirectURI], scope: data[:scopes] || scopes, state: data[:state], login_hint: data[:loginHint] || data[:login_hint], prompt: [:prompt] }) end, validate_authorization_code: lambda do |data| Base.post_form("https://github.com/login/oauth/access_token", { client_id: client_id, client_secret: client_secret, code: data[:code], code_verifier: data[:code_verifier] || data[:codeVerifier], redirect_uri: data[:redirect_uri] || data[:redirectURI] }) end, get_user_info: lambda do |tokens| headers = { "Authorization" => "Bearer #{Base.access_token(tokens)}", "Accept" => "application/json", "User-Agent" => "better-auth" } profile = Base.get_json("https://api.github.com/user", headers) emails = Base.get_json("https://api.github.com/user/emails", headers) primary = Array(emails).find { |email| email["email"] == profile["email"] } || Array(emails).find { |email| email["primary"] } || Array(emails).first || {} { user: { id: profile["id"].to_s, email: profile["email"] || primary["email"], name: profile["name"] || profile["login"], image: profile["avatar_url"], emailVerified: !!primary["verified"] }, data: profile } end } end |
.gitlab(client_id:, client_secret:, issuer: "https://gitlab.com", scopes: ["read_user"], **options) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/better_auth/social_providers/gitlab.rb', line 7 def gitlab(client_id:, client_secret:, issuer: "https://gitlab.com", scopes: ["read_user"], **) base = issuer.to_s.sub(%r{/+\z}, "") { id: "gitlab", name: "GitLab", client_id: client_id, client_secret: client_secret, create_authorization_url: lambda do |data| Base.([:authorization_endpoint] || "#{base}/oauth/authorize", { client_id: client_id, redirect_uri: data[:redirect_uri] || data[:redirectURI], response_type: "code", scope: data[:scopes] || scopes, state: data[:state], code_challenge: (data[:code_verifier] || data[:codeVerifier]) && Base.pkce_challenge(data[:code_verifier] || data[:codeVerifier]), code_challenge_method: (data[:code_verifier] || data[:codeVerifier]) && "S256", login_hint: data[:loginHint] || data[:login_hint] }) end, validate_authorization_code: lambda do |data| Base.post_form("#{base}/oauth/token", { client_id: client_id, client_secret: client_secret, code: data[:code], code_verifier: data[:code_verifier] || data[:codeVerifier], grant_type: "authorization_code", redirect_uri: data[:redirect_uri] || data[:redirectURI] }) end, get_user_info: lambda do |tokens| profile = Base.get_json("#{base}/api/v4/user", "Authorization" => "Bearer #{Base.access_token(tokens)}") return nil if profile["state"] && profile["state"] != "active" { user: { id: profile["id"].to_s, email: profile["email"], name: profile["name"] || profile["username"], image: profile["avatar_url"], emailVerified: !!profile["email_verified"] }, data: profile } end } end |
.google(client_id:, client_secret:, scopes: ["openid", "email", "profile"], **options) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/better_auth/social_providers/google.rb', line 7 def google(client_id:, client_secret:, scopes: ["openid", "email", "profile"], **) { id: "google", name: "Google", client_id: client_id, client_secret: client_secret, create_authorization_url: lambda do |data| verifier = data[:code_verifier] || data[:codeVerifier] Base.([:authorization_endpoint] || "https://accounts.google.com/o/oauth2/v2/auth", { client_id: client_id, redirect_uri: data[:redirect_uri] || data[:redirectURI], response_type: "code", scope: data[:scopes] || scopes, state: data[:state], code_challenge: verifier && Base.pkce_challenge(verifier), code_challenge_method: verifier && "S256", login_hint: data[:loginHint] || data[:login_hint], prompt: [:prompt], access_type: [:access_type] || [:accessType] || "offline", display: data[:display] || [:display], hd: [:hd], include_granted_scopes: "true" }) end, validate_authorization_code: lambda do |data| Base.post_form("https://oauth2.googleapis.com/token", { client_id: client_id, client_secret: client_secret, code: data[:code], code_verifier: data[:code_verifier] || data[:codeVerifier], grant_type: "authorization_code", redirect_uri: data[:redirect_uri] || data[:redirectURI] }) end, get_user_info: lambda do |tokens| profile = if Base.id_token(tokens) Base.decode_jwt_payload(Base.id_token(tokens)) else Base.get_json( "https://openidconnect.googleapis.com/v1/userinfo", "Authorization" => "Bearer #{Base.access_token(tokens)}" ) end { user: { id: profile["sub"], email: profile["email"], name: profile["name"], image: profile["picture"], emailVerified: !!profile["email_verified"] }, data: profile } end } end |
.microsoft_email_verified?(profile, email) ⇒ Boolean
58 59 60 61 62 63 |
# File 'lib/better_auth/social_providers/microsoft_entra_id.rb', line 58 def microsoft_email_verified?(profile, email) return !!profile["email_verified"] if profile.key?("email_verified") Array(profile["verified_primary_email"]).include?(email) || Array(profile["verified_secondary_email"]).include?(email) end |
.microsoft_entra_id(client_id:, client_secret:, tenant_id: "common", scopes: ["openid", "profile", "email", "User.Read", "offline_access"], **options) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/better_auth/social_providers/microsoft_entra_id.rb', line 7 def microsoft_entra_id(client_id:, client_secret:, tenant_id: "common", scopes: ["openid", "profile", "email", "User.Read", "offline_access"], **) = [:authority] || "https://login.microsoftonline.com" base = "#{.to_s.sub(%r{/+\z}, "")}/#{tenant_id}/oauth2/v2.0" { id: "microsoft-entra-id", name: "Microsoft Entra ID", client_id: client_id, client_secret: client_secret, create_authorization_url: lambda do |data| verifier = data[:code_verifier] || data[:codeVerifier] Base.([:authorization_endpoint] || "#{base}/authorize", { client_id: client_id, redirect_uri: data[:redirect_uri] || data[:redirectURI], response_type: "code", scope: data[:scopes] || scopes, state: data[:state], code_challenge: verifier && Base.pkce_challenge(verifier), code_challenge_method: verifier && "S256", login_hint: data[:loginHint] || data[:login_hint], prompt: [:prompt] }) end, validate_authorization_code: lambda do |data| Base.post_form("#{base}/token", { client_id: client_id, client_secret: client_secret, code: data[:code], code_verifier: data[:code_verifier] || data[:codeVerifier], grant_type: "authorization_code", redirect_uri: data[:redirect_uri] || data[:redirectURI] }) end, get_user_info: lambda do |tokens| profile = Base.id_token(tokens) ? Base.decode_jwt_payload(Base.id_token(tokens)) : {} profile = Base.get_json("https://graph.microsoft.com/v1.0/me", "Authorization" => "Bearer #{Base.access_token(tokens)}") if profile.empty? email = profile["email"] || profile["mail"] || profile["userPrincipalName"] || profile["preferred_username"] { user: { id: profile["sub"] || profile["id"] || profile["oid"], email: email, name: profile["name"] || profile["displayName"], image: profile["picture"], emailVerified: microsoft_email_verified?(profile, email) }, data: profile } end } end |