CI / Test (pull_request) Failing after 46s
- Make authorization-code redemption atomic: claim the code with a guarded delete inside the transaction that creates the authorization, so concurrent exchanges can't both issue a token (RFC 6749 4.1.3) - Compare the token request's client_id host to the code's stored client_id (defense-in-depth alongside the exact redirect_uri match) - Reject unknown response_type values with unsupported_response_type - Validate the PKCE code_challenge (43-128 unreserved chars, S256) so a client can't persist an over-long or malformed challenge - Return invalid_request for missing token-endpoint parameters, and set Cache-Control: no-store on error responses too - Use the right separator when the redirect_uri already has a query string - Look codes up through the active scope; drop the unused consume!; serialize permissions as JSON - Purge abandoned authorization codes via a recurring Solid Queue task Part of #254
34 lines
678 B
Ruby
34 lines
678 B
Ruby
class RsAuthorizationCode < ApplicationRecord
|
|
belongs_to :user
|
|
|
|
CODE_TTL = 60.seconds
|
|
|
|
serialize :permissions, coder: JSON
|
|
|
|
attr_accessor :code
|
|
|
|
before_validation :generate_code
|
|
|
|
scope :active, -> { where(expires_at: Time.current..) }
|
|
|
|
def self.find_by_code(raw_code)
|
|
return nil if raw_code.blank?
|
|
|
|
active.find_by(code_digest: Digest::SHA256.hexdigest(raw_code))
|
|
end
|
|
|
|
def expired?
|
|
expires_at <= Time.current
|
|
end
|
|
|
|
private
|
|
|
|
def generate_code
|
|
return if code_digest.present?
|
|
|
|
self.code = SecureRandom.urlsafe_base64(32)
|
|
self.code_digest = Digest::SHA256.hexdigest(code)
|
|
self.expires_at ||= CODE_TTL.from_now
|
|
end
|
|
end
|