Class: Admin::DeviceAuthorization

Inherits:
ApplicationRecord show all
Defined in:
app/models/admin/device_authorization.rb

Defined Under Namespace

Classes: TokenError

Constant Summary collapse

EXPIRES_IN =
10.minutes

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.digest(device_code) ⇒ Object



41
42
43
# File 'app/models/admin/device_authorization.rb', line 41

def self.digest(device_code)
  Digest::SHA256.hexdigest(device_code)
end

.generate_user_codeObject



45
46
47
# File 'app/models/admin/device_authorization.rb', line 45

def self.generate_user_code
  "#{SecureRandom.alphanumeric(4).upcase}-#{SecureRandom.alphanumeric(4).upcase}"
end

.issue!(requested_ip:, user_agent:) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'app/models/admin/device_authorization.rb', line 27

def self.issue!(requested_ip:, user_agent:)
  device_code = SecureRandom.urlsafe_base64(32)

  device_authorization = create!(
    device_code_digest: digest(device_code),
    user_code:          generate_user_code,
    request_expires_at: EXPIRES_IN.from_now,
    requested_ip:,
    user_agent:,
  )

  [device_authorization, device_code]
end

.issue_access_token!(device_code:, token_expires_in: 12.hours) ⇒ Object

Raises:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/models/admin/device_authorization.rb', line 49

def self.issue_access_token!(device_code:, token_expires_in: 12.hours)
  device_authorization = find_by(device_code_digest: digest(device_code.to_s))
  raise TokenError.new("invalid_grant") unless device_authorization

  device_authorization.with_lock do
    device_authorization.reload

    if (error = device_authorization.token_error)
      raise TokenError.new(error)
    end

    access_token = device_authorization.admin_user.generate_token_for(:api_access)
    device_authorization.consume!(token_expires_in:)

    {
      access_token:,
      token_type:   "Bearer",
      expires_in:   token_expires_in.to_i,
    }
  end
end

Instance Method Details

#actionable?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'app/models/admin/device_authorization.rb', line 109

def actionable?
  pending? && !expired?
end

#approve!(admin_user:) ⇒ Object



94
95
96
97
98
99
100
# File 'app/models/admin/device_authorization.rb', line 94

def approve!(admin_user:)
  update!(
    status:      "approved",
    approved_at: Time.current,
    admin_user:,
  )
end

#consume!(token_expires_in:) ⇒ Object



86
87
88
89
90
91
92
# File 'app/models/admin/device_authorization.rb', line 86

def consume!(token_expires_in:)
  update!(
    status:           "consumed",
    consumed_at:      Time.current,
    token_expires_at: token_expires_in.from_now,
  )
end

#deny!(admin_user:) ⇒ Object



102
103
104
105
106
107
# File 'app/models/admin/device_authorization.rb', line 102

def deny!(admin_user:)
  update!(
    status:     "denied",
    admin_user:,
  )
end

#expired?Boolean

Returns:

  • (Boolean)


71
72
73
# File 'app/models/admin/device_authorization.rb', line 71

def expired?
  request_expires_at <= Time.current
end

#issuable?Boolean

Returns:

  • (Boolean)


75
76
77
# File 'app/models/admin/device_authorization.rb', line 75

def issuable?
  approved? && !expired?
end

#token_errorObject



79
80
81
82
83
84
# File 'app/models/admin/device_authorization.rb', line 79

def token_error
  return "authorization_pending" if pending?
  return "access_denied" if denied?

  "invalid_grant" if consumed? || expired?
end