diff --git a/app/controllers/active_sessions_controller.rb b/app/controllers/active_sessions_controller.rb
new file mode 100644
index 0000000..7fa365b
--- /dev/null
+++ b/app/controllers/active_sessions_controller.rb
@@ -0,0 +1,35 @@
+class ActiveSessionsController < ApplicationController
+ def show
+ @user = Current.session.user
+ @active_sessions = @user.sessions.active.order(last_activity_at: :desc)
+ @connected_applications = @user.oidc_user_consents.includes(:application).order(granted_at: :desc)
+ end
+
+ def revoke_consent
+ @user = Current.session.user
+ application = Application.find(params[:application_id])
+
+ # Check if user has consent for this application
+ consent = @user.oidc_user_consents.find_by(application: application)
+ unless consent
+ redirect_to active_sessions_path, alert: "No consent found for this application."
+ return
+ end
+
+ # Revoke the consent
+ consent.destroy
+ redirect_to active_sessions_path, notice: "Successfully revoked access to #{application.name}."
+ end
+
+ def revoke_all_consents
+ @user = Current.session.user
+ count = @user.oidc_user_consents.count
+
+ if count > 0
+ @user.oidc_user_consents.destroy_all
+ redirect_to active_sessions_path, notice: "Successfully revoked access to #{count} applications."
+ else
+ redirect_to active_sessions_path, alert: "No applications to revoke."
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 542dbae..2df6bc5 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -76,7 +76,7 @@ module Admin
end
def user_params
- params.require(:user).permit(:email_address, :password, :admin, :status, custom_claims: {})
+ params.require(:user).permit(:email_address, :name, :password, :admin, :status, custom_claims: {})
end
end
end
diff --git a/app/controllers/oidc_controller.rb b/app/controllers/oidc_controller.rb
index cd52552..1f8dd5c 100644
--- a/app/controllers/oidc_controller.rb
+++ b/app/controllers/oidc_controller.rb
@@ -291,7 +291,7 @@ class OidcController < ApplicationController
email: user.email_address,
email_verified: true,
preferred_username: user.email_address,
- name: user.email_address
+ name: user.name.presence || user.email_address
}
# Add groups if user has any
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index deeb1cc..30614d0 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -1,8 +1,6 @@
class ProfilesController < ApplicationController
def show
@user = Current.session.user
- @active_sessions = @user.sessions.active.order(last_activity_at: :desc)
- @connected_applications = @user.oidc_user_consents.includes(:application).order(granted_at: :desc)
end
def update
@@ -12,7 +10,6 @@ class ProfilesController < ApplicationController
# Updating password - requires current password
unless @user.authenticate(params[:user][:current_password])
@user.errors.add(:current_password, "is incorrect")
- @active_sessions = @user.sessions.active.order(last_activity_at: :desc)
render :show, status: :unprocessable_entity
return
end
@@ -20,7 +17,6 @@ class ProfilesController < ApplicationController
if @user.update(password_params)
redirect_to profile_path, notice: "Password updated successfully."
else
- @active_sessions = @user.sessions.active.order(last_activity_at: :desc)
render :show, status: :unprocessable_entity
end
else
@@ -28,40 +24,11 @@ class ProfilesController < ApplicationController
if @user.update(email_params)
redirect_to profile_path, notice: "Email updated successfully."
else
- @active_sessions = @user.sessions.active.order(last_activity_at: :desc)
render :show, status: :unprocessable_entity
end
end
end
- def revoke_consent
- @user = Current.session.user
- application = Application.find(params[:application_id])
-
- # Check if user has consent for this application
- consent = @user.oidc_user_consents.find_by(application: application)
- unless consent
- redirect_to profile_path, alert: "No consent found for this application."
- return
- end
-
- # Revoke the consent
- consent.destroy
- redirect_to profile_path, notice: "Successfully revoked access to #{application.name}."
- end
-
- def revoke_all_consents
- @user = Current.session.user
- count = @user.oidc_user_consents.count
-
- if count > 0
- @user.oidc_user_consents.destroy_all
- redirect_to profile_path, notice: "Successfully revoked access to #{count} applications."
- else
- redirect_to profile_path, alert: "No applications to revoke."
- end
- end
-
private
def email_params
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 20f389b..90b9041 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -115,7 +115,7 @@ class SessionsController < ApplicationController
def destroy_other
session = Current.session.user.sessions.find(params[:id])
session.destroy
- redirect_to profile_path, notice: "Session revoked successfully."
+ redirect_to active_sessions_path, notice: "Session revoked successfully."
end
private
diff --git a/app/models/application.rb b/app/models/application.rb
index 159f291..3b94911 100644
--- a/app/models/application.rb
+++ b/app/models/application.rb
@@ -123,8 +123,10 @@ class Application < ApplicationRecord
next unless header_name.present? # Skip disabled headers
case key
- when :user, :email, :name
+ when :user, :email
headers[header_name] = user.email_address
+ when :name
+ headers[header_name] = user.name.presence || user.email_address
when :groups
headers[header_name] = user.groups.pluck(:name).join(",") if user.groups.any?
when :admin
diff --git a/app/services/oidc_jwt_service.rb b/app/services/oidc_jwt_service.rb
index 00cee45..52f709e 100644
--- a/app/services/oidc_jwt_service.rb
+++ b/app/services/oidc_jwt_service.rb
@@ -13,7 +13,7 @@ class OidcJwtService
email: user.email_address,
email_verified: true,
preferred_username: user.email_address,
- name: user.email_address
+ name: user.name.presence || user.email_address
}
# Add nonce if provided (OIDC requires this for implicit flow)
diff --git a/app/views/active_sessions/show.html.erb b/app/views/active_sessions/show.html.erb
new file mode 100644
index 0000000..a66ee49
--- /dev/null
+++ b/app/views/active_sessions/show.html.erb
@@ -0,0 +1,114 @@
+
+
+
Sessions
+
Manage your active sessions and connected applications.
+
+
+
+
+
+
Connected Applications
+
+
These applications have access to your account. You can revoke access at any time.
+
+
+ <% if @connected_applications.any? %>
+
+ <% @connected_applications.each do |consent| %>
+
+
+
+
+ <%= consent.application.name %>
+
+
+ Access to: <%= consent.formatted_scopes %>
+
+
+ Authorized <%= time_ago_in_words(consent.granted_at) %> ago
+
+
+ <%= button_to "Revoke Access", revoke_consent_active_sessions_path(application_id: consent.application.id), method: :delete,
+ class: "inline-flex items-center rounded-md border border-red-300 bg-white px-3 py-2 text-sm font-medium text-red-700 shadow-sm hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2",
+ form: { data: { turbo_confirm: "Are you sure you want to revoke access to #{consent.application.name}? You'll need to re-authorize this application to use it again." } } %>
+
+
+ <% end %>
+
+ <% else %>
+
No connected applications.
+ <% end %>
+
+ <% if @connected_applications.any? %>
+
+
+
+ <%= button_to "Revoke All App Access", revoke_all_consents_active_sessions_path, method: :delete,
+ class: "inline-flex items-center rounded-md border border-red-300 bg-white px-3 py-2 text-sm font-medium text-red-700 shadow-sm hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 whitespace-nowrap",
+ form: { data: { turbo_confirm: "This will revoke access from all connected applications. You'll need to re-authorize each application to use them again. Are you sure?" } } %>
+
+
+
+ <% end %>
+
+
+
+
+
+
+
+
Active Sessions
+
+
These devices are currently signed in to your account. Revoke any sessions that you don't recognize.
+
+
+ <% if @active_sessions.any? %>
+
+ <% @active_sessions.each do |session| %>
+
+
+
+
+ <%= session.device_name || "Unknown Device" %>
+ <% if session.id == Current.session.id %>
+
+ This device
+
+ <% end %>
+
+
+ <%= session.ip_address %>
+
+
+ Last active <%= time_ago_in_words(session.last_activity_at || session.updated_at) %> ago
+
+
+ <% if session.id != Current.session.id %>
+ <%= button_to "Revoke", session_path(session), method: :delete,
+ class: "inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2",
+ form: { data: { turbo_confirm: "Are you sure you want to revoke this session?" } } %>
+ <% end %>
+
+
+ <% end %>
+
+ <% else %>
+
No other active sessions.
+ <% end %>
+
+ <% if @active_sessions.count > 1 %>
+
+
+
+ <%= button_to "Sign Out Everywhere Else", session_path(Current.session), method: :delete,
+ class: "inline-flex items-center rounded-md border border-orange-300 bg-white px-3 py-2 text-sm font-medium text-orange-700 shadow-sm hover:bg-orange-50 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 whitespace-nowrap",
+ form: { data: { turbo_confirm: "This will sign you out from all other devices except this one. Are you sure?" } } %>
+
+
+
+ <% end %>
+
+
+
+
+
\ No newline at end of file
diff --git a/app/views/admin/applications/_form.html.erb b/app/views/admin/applications/_form.html.erb
index 7387bdd..2de21d9 100644
--- a/app/views/admin/applications/_form.html.erb
+++ b/app/views/admin/applications/_form.html.erb
@@ -65,8 +65,23 @@