Class: Admin::DeviceAuthorization
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Admin::DeviceAuthorization
- Defined in:
- app/models/admin/device_authorization.rb
Defined Under Namespace
Classes: TokenError
Constant Summary collapse
- REQUEST_EXPIRES_IN =
10.minutes
- TOKEN_EXPIRES_IN =
1.hour
Class Method Summary collapse
-
.consume_request!(device_code:) ⇒ Object
Consume an approved request, returns the issued token payload.
-
.create_request!(requested_ip:, user_agent:) ⇒ Object
Creates a new un-approved request.
- .digest(device_code) ⇒ Object
- .generate_user_code ⇒ Object
-
.issue_token!(principal:) ⇒ Object
Issue a new token directly from a validated assertion, returns the issued token payload.
Instance Method Summary collapse
- #actionable? ⇒ Boolean
- #actor ⇒ Admin::User, ...
- #approve!(admin_user:) ⇒ Object
- #consume! ⇒ Object
- #deny!(admin_user:) ⇒ Object
- #expired? ⇒ Boolean
- #issuable? ⇒ Boolean
- #token_error ⇒ Object
Class Method Details
.consume_request!(device_code:) ⇒ Object
Consume an approved request, returns the issued token payload.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'app/models/admin/device_authorization.rb', line 72 def self.consume_request!(device_code:) = find_by(device_code_digest: digest(device_code.to_s)) raise TokenError.new("invalid_grant") unless .with_lock do .reload if (error = .token_error) raise TokenError.new(error) end .consume! end end |
.create_request!(requested_ip:, user_agent:) ⇒ Object
Creates a new un-approved request.
49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'app/models/admin/device_authorization.rb', line 49 def self.create_request!(requested_ip:, user_agent:) device_code = SecureRandom.urlsafe_base64(32) = create!( device_code_digest: digest(device_code), user_code: generate_user_code, request_expires_at: REQUEST_EXPIRES_IN.from_now, requested_ip:, user_agent:, ) [, device_code] end |
.digest(device_code) ⇒ Object
63 64 65 |
# File 'app/models/admin/device_authorization.rb', line 63 def self.digest(device_code) Digest::SHA256.hexdigest(device_code) end |
.generate_user_code ⇒ Object
67 68 69 |
# File 'app/models/admin/device_authorization.rb', line 67 def self.generate_user_code "#{SecureRandom.alphanumeric(4).upcase}-#{SecureRandom.alphanumeric(4).upcase}" end |
.issue_token!(principal:) ⇒ Object
Issue a new token directly from a validated assertion, returns the issued token payload.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'app/models/admin/device_authorization.rb', line 88 def self.issue_token!(principal:, **) case principal.scope when "admin/user" admin_user = Admin::User.find_by(principal.attributes_for_find) unless admin_user Rails.logger.warn("Koi::Identity rejected #{principal}: no matching admin user") raise JWT::VerificationError, "unknown user for #{principal}" end new(admin_user:, principal:, **).consume! when %r{\Aadmin/role/(?<slug>[a-z0-9_]+)\z} admin_role = Admin::Role.materialize(Regexp.last_match(:slug)) new(admin_role:, principal:, **).consume! else raise ArgumentError, "unknown scope #{principal.scope.inspect}" end end |
Instance Method Details
#actionable? ⇒ Boolean
153 154 155 |
# File 'app/models/admin/device_authorization.rb', line 153 def actionable? pending? && !expired? end |
#actor ⇒ Admin::User, ...
109 110 111 |
# File 'app/models/admin/device_authorization.rb', line 109 def actor admin_user || admin_role end |
#approve!(admin_user:) ⇒ Object
138 139 140 141 142 143 144 |
# File 'app/models/admin/device_authorization.rb', line 138 def approve!(admin_user:) update!( status: "approved", approved_at: Time.current, admin_user:, ) end |
#consume! ⇒ Object
128 129 130 131 132 133 134 135 136 |
# File 'app/models/admin/device_authorization.rb', line 128 def consume! update!( status: "consumed", consumed_at: Time.current, token_expires_at: TOKEN_EXPIRES_IN.from_now, ) token_payload end |
#deny!(admin_user:) ⇒ Object
146 147 148 149 150 151 |
# File 'app/models/admin/device_authorization.rb', line 146 def deny!(admin_user:) update!( status: "denied", admin_user:, ) end |
#expired? ⇒ Boolean
113 114 115 |
# File 'app/models/admin/device_authorization.rb', line 113 def expired? request_expires_at.present? && request_expires_at <= Time.current end |
#issuable? ⇒ Boolean
117 118 119 |
# File 'app/models/admin/device_authorization.rb', line 117 def issuable? approved? && !expired? end |
#token_error ⇒ Object
121 122 123 124 125 126 |
# File 'app/models/admin/device_authorization.rb', line 121 def token_error return "authorization_pending" if pending? return "access_denied" if denied? "invalid_grant" if consumed? || expired? end |