Class: Hitch::DeviceActivation

Inherits:
Object
  • Object
show all
Includes:
UriValidation
Defined in:
app/models/hitch/device_activation.rb

Overview

What the /activate confirmation screen may honestly say about the client behind a pending device grant.

The device flow delivers nothing to a redirect_uri, so the consent screen's verified signal — the host the authorization code is actually sent to — does not survive the trip. What replaces it is a vouching rule: a device grant is approvable only for a client somebody real vouches for, and only the voucher's word is displayed. A CIMD client is branded by its client_id's own URL host, earned by serving the document there. A confidential client is branded by the name the operator chose when they registered it at a console. A client that only ever vouched for itself through open registration — even one DCR issued a secret — is the §5.4 phishing shape: the mint endpoint refuses it, and this object independently refuses to verify it, so the guarantee holds whichever door a grant came through.

CIMD resolution happens here, with the signed-in approver as the rate-limit actor — never at the mint endpoint, which has no actor.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(grant, principal:) ⇒ DeviceActivation

Returns a new instance of DeviceActivation.



27
28
29
30
# File 'app/models/hitch/device_activation.rb', line 27

def initialize(grant, principal:)
  @grant = grant
  @principal = principal
end

Instance Attribute Details

#grantObject (readonly)

Returns the value of attribute grant.



32
33
34
# File 'app/models/hitch/device_activation.rb', line 32

def grant
  @grant
end

Instance Method Details

#audit_client_nameObject



64
65
66
# File 'app/models/hitch/device_activation.rb', line 64

def audit_client_name
  document&.client_name || client&.client_name || "Unknown"
end

#display_client_nameObject



55
56
57
58
59
60
61
62
# File 'app/models/hitch/device_activation.rb', line 55

def display_client_name
  return client.client_name if operator_registered?

  # hostname, not host: URI#host keeps IPv6 brackets, which would
  # defeat a client_names entry keyed on the bare address.
  host = URI.parse(grant.client_id).hostname
  Hitch.configuration.client_label(host) || host
end

#localhost_only_client?Boolean

The consent screen's own-computer warning, carried over: a client whose every declared redirect is loopback http runs on the user's machine, not at the host it displays as.

Returns:

  • (Boolean)


71
72
73
74
# File 'app/models/hitch/device_activation.rb', line 71

def localhost_only_client?
  declared = document&.redirect_uris
  declared.present? && declared.all? { |uri| loopback_http_uri?(uri) }
end

#operator_registered?Boolean

A confidential client the operator registered at a console; its display name is the operator's word, and the view says whose word it is.

Returns:

  • (Boolean)


50
51
52
53
# File 'app/models/hitch/device_activation.rb', line 50

def operator_registered?
  grant.token_endpoint_auth_method == "client_secret_basic" &&
    client&.operator_registered_confidential_client?
end

#scopesObject



76
77
78
# File 'app/models/hitch/device_activation.rb', line 76

def scopes
  @scopes ||= grant.scopes.to_s.split(/\s+/)
end

#unverified?Boolean

The live voucher must still exist and must agree with the immutable authentication posture recorded at mint. That refuses both directions of a registration race: a public/CIMD grant cannot borrow an operator registration, and a confidential grant cannot fall back to CIMD after its operator registration disappears.

Returns:

  • (Boolean)


39
40
41
42
43
44
45
# File 'app/models/hitch/device_activation.rb', line 39

def unverified?
  case grant.token_endpoint_auth_method
  when "client_secret_basic" then !operator_registered?
  when "none" then document.nil?
  else true
  end
end