2025-06-18 08:20:22 -05:00
|
|
|
class MobileDevice < ApplicationRecord
|
|
|
|
belongs_to :user
|
|
|
|
belongs_to :oauth_application, class_name: "Doorkeeper::Application", optional: true
|
|
|
|
|
|
|
|
validates :device_id, presence: true, uniqueness: { scope: :user_id }
|
|
|
|
validates :device_name, presence: true
|
|
|
|
validates :device_type, presence: true, inclusion: { in: %w[ios android] }
|
|
|
|
|
|
|
|
before_validation :set_last_seen_at, on: :create
|
|
|
|
|
|
|
|
scope :active, -> { where("last_seen_at > ?", 90.days.ago) }
|
|
|
|
|
|
|
|
def active?
|
|
|
|
last_seen_at > 90.days.ago
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_last_seen!
|
|
|
|
update_column(:last_seen_at, Time.current)
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_oauth_application!
|
|
|
|
return oauth_application if oauth_application.present?
|
|
|
|
|
|
|
|
app = Doorkeeper::Application.create!(
|
|
|
|
name: "Mobile App - #{device_id}",
|
|
|
|
redirect_uri: "maybe://oauth/callback", # Custom scheme for mobile
|
|
|
|
scopes: "read_write", # Use the configured scope
|
|
|
|
confidential: false # Public client for mobile
|
|
|
|
)
|
2025-06-18 08:28:32 -05:00
|
|
|
|
2025-06-18 08:20:22 -05:00
|
|
|
# Store the association
|
|
|
|
update!(oauth_application: app)
|
|
|
|
app
|
|
|
|
end
|
|
|
|
|
|
|
|
def active_tokens
|
|
|
|
return Doorkeeper::AccessToken.none unless oauth_application
|
2025-06-18 08:28:32 -05:00
|
|
|
|
2025-06-18 08:20:22 -05:00
|
|
|
Doorkeeper::AccessToken
|
|
|
|
.where(application: oauth_application)
|
|
|
|
.where(resource_owner_id: user_id)
|
|
|
|
.where(revoked_at: nil)
|
|
|
|
.where("expires_in IS NULL OR created_at + expires_in * interval '1 second' > ?", Time.current)
|
|
|
|
end
|
|
|
|
|
|
|
|
def revoke_all_tokens!
|
|
|
|
active_tokens.update_all(revoked_at: Time.current)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def set_last_seen_at
|
|
|
|
self.last_seen_at ||= Time.current
|
|
|
|
end
|
|
|
|
end
|