Show connected OAuth provider on the settings page
Settings previously had no signal of how an account signs in. Records which provider (GitHub/Google) a brokered OAuth sign-in used on the User record, and shows it under a new "Connected accounts" card; falls back to a "no OAuth connection" message for email/password accounts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Seto Elkahfi committed
Jul 5, 2026 at 10:40 UTC
a0cb76ee96230acfd6693d65dfc3acf350339c2a
7 files changed
+63
-5
app/controllers/oauth/github_controller.rb
+4
index 5080f48..dc11ae3 100644
--- a/app/controllers/oauth/github_controller.rb
+++ b/app/controllers/oauth/github_controller.rb
@@ -9,6 +9,10 @@ module Oauth
"GitHub"
end
+ def provider_key
+ "github"
+ end
+
def authorize_url
SmbcloudAuthService.github_authorize_url(redirect_uri: github_auth_callback_url)
end
app/controllers/oauth/google_controller.rb
+4
index 8cf98ff..3987a69 100644
--- a/app/controllers/oauth/google_controller.rb
+++ b/app/controllers/oauth/google_controller.rb
@@ -9,6 +9,10 @@ module Oauth
"Google"
end
+ def provider_key
+ "google"
+ end
+
def authorize_url
SmbcloudAuthService.google_authorize_url(redirect_uri: google_auth_callback_url)
end
app/controllers/oauth/provider_controller.rb
+6
-1
index 5a8e6e6..d7a759c 100644
--- a/app/controllers/oauth/provider_controller.rb
+++ b/app/controllers/oauth/provider_controller.rb
@@ -10,6 +10,7 @@ module Oauth
#
# Subclasses supply the provider specifics:
# provider_label — human name used in flash/log messages ("GitHub")
+ # provider_key — lowercase key stored on User#oauth_provider ("github")
# authorize_url — the smbCloud authorize URL carrying our callback
class ProviderController < ApplicationController
before_action :redirect_if_signed_in
@@ -32,7 +33,11 @@ module Oauth
end
profile = SmbcloudAuthService.me(access_token: access_token)
- user = User.find_or_create_from_smbcloud(profile.merge(access_token: access_token))
+ user = User.find_or_create_from_smbcloud(
+ profile,
+ access_token: access_token,
+ oauth_provider: provider_key
+ )
reset_session
session[:user_id] = user.id
app/models/user.rb
+16
-3
index 9bfbaa1..14ac8ed 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -84,6 +84,14 @@ class User < ApplicationRecord
"https://ui-avatars.com/api/?name=#{encoded_name}&background=0057B8&color=fff&size=128"
end
+ OAUTH_PROVIDER_LABELS = { "github" => "GitHub", "google" => "Google" }.freeze
+
+ # Human label for the OAuth provider this account last signed in with, or
+ # nil if it has only ever used email/password sign-in.
+ def oauth_provider_label
+ OAUTH_PROVIDER_LABELS[oauth_provider]
+ end
+
# ---------------------------------------------------------------------------
# smbCloud Auth integration
# ---------------------------------------------------------------------------
@@ -92,11 +100,15 @@ class User < ApplicationRecord
# 1. SmbcloudAuthService.me(access_token:)
# → { id: Integer, email: String, created_at: String, updated_at: String }
# 2. The access_token passed through from login.
+ # 3. `oauth_provider`, when this upsert followed a brokered "Continue with
+ # <provider>" sign-in (e.g. "github", "google") — nil for plain
+ # email/password sign-in. Recorded so Settings can show which OAuth
+ # connection, if any, the account last used.
#
# Accepts either string or symbol keys.
#
# Returns the persisted User. Raises ActiveRecord::RecordInvalid on failure.
- def self.find_or_create_from_smbcloud(userinfo, access_token: nil)
+ def self.find_or_create_from_smbcloud(userinfo, access_token: nil, oauth_provider: nil)
smbcloud_id = Integer(userinfo[:id] || userinfo["id"])
email = (userinfo[:email] || userinfo["email"]).to_s.strip.downcase
@@ -118,8 +130,9 @@ class User < ApplicationRecord
end
# Always sync email and token in case they changed on the auth server.
- user.email = email if email.present?
- user.access_token = access_token if access_token.present?
+ user.email = email if email.present?
+ user.access_token = access_token if access_token.present?
+ user.oauth_provider = oauth_provider if oauth_provider.present?
# Derive a username only for new records.
user.username = generate_username_from_email(email) if user.new_record?
app/views/users/settings.html.erb
+21
index 92f3b25..e704110 100644
--- a/app/views/users/settings.html.erb
+++ b/app/views/users/settings.html.erb
@@ -32,6 +32,27 @@
</div>
</div>
+ <div class="card mt-6">
+ <div class="px-6 py-4 border-b border-surface-600">
+ <h2 class="text-sm font-semibold text-gray-100">Connected accounts</h2>
+ </div>
+ <div class="px-6 py-6">
+ <% if @user.oauth_provider_label %>
+ <div class="flex items-center justify-between">
+ <div class="flex items-center gap-3">
+ <span class="inline-flex h-2 w-2 rounded-full bg-green-500"></span>
+ <p class="text-sm text-gray-300">
+ Signed in with <span class="font-medium text-gray-100"><%= @user.oauth_provider_label %></span>
+ </p>
+ </div>
+ <span class="text-xs text-gray-500">Connected</span>
+ </div>
+ <% else %>
+ <p class="text-sm text-gray-400">No OAuth connection. You're signing in with email and password.</p>
+ <% end %>
+ </div>
+ </div>
+
<div class="card mt-6">
<div class="px-6 py-4 border-b border-surface-600">
<h2 class="text-sm font-semibold text-gray-100">Billing</h2>
db/migrate/20250101000012_add_oauth_provider_to_users.rb
+10
new file mode 100644
index 0000000..17c16af
--- /dev/null
+++ b/db/migrate/20250101000012_add_oauth_provider_to_users.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+# Records which OAuth provider (if any) a user last signed in with, so
+# Settings can show "Connected via GitHub/Google" for accounts that used the
+# brokered social sign-in flow. Left blank for email/password-only accounts.
+class AddOauthProviderToUsers < ActiveRecord::Migration[8.1]
+ def change
+ add_column :users, :oauth_provider, :string
+ end
+end
db/schema.rb
+2
-1
index 51e8b3f..f57e441 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2025_01_01_000011) do
+ActiveRecord::Schema[8.1].define(version: 2025_01_01_000012) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@@ -146,6 +146,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_01_01_000011) do
t.datetime "created_at", null: false
t.string "display_name"
t.string "email", null: false
+ t.string "oauth_provider"
t.integer "smbcloud_id", null: false
t.datetime "updated_at", null: false
t.string "username", null: false