Class: Himari::ClientRegistration

Inherits:
Object
  • Object
show all
Defined in:
lib/himari/client_registration.rb

Constant Summary collapse

LOOPBACK_HOSTS =

Loopback hosts whose redirect_uri port may be relaxed (RFC 8252 §7.3, draft-ietf-oauth-v2-1-15 §8.4.2). Addressable returns IPv6 hosts bracketed.

%w[127.0.0.1 [::1] localhost].freeze
IMPLICIT_SCOPES =

Scopes Himari itself acts on; recognised for every client regardless of the configured scopes list, so a client need not enumerate them to use OIDC or obtain a refresh token.

%w[openid offline_access].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, redirect_uris:, name: nil, secret: nil, secret_hash: nil, preferred_key_group: nil, require_pkce: false, confidential: true, ignore_localhost_redirect_uri_port: true, skip_consent: false, scopes: IMPLICIT_SCOPES) ⇒ ClientRegistration

Returns a new instance of ClientRegistration.

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/himari/client_registration.rb', line 16

def initialize(id:, redirect_uris:, name: nil, secret: nil, secret_hash: nil, preferred_key_group: nil, require_pkce: false, confidential: true, ignore_localhost_redirect_uri_port: true, skip_consent: false, scopes: IMPLICIT_SCOPES)
  @name = name
  @id = id
  @secret = secret
  @secret_hash = secret_hash
  @redirect_uris = redirect_uris
  @preferred_key_group = preferred_key_group
  @require_pkce = require_pkce
  @confidential = confidential
  @ignore_localhost_redirect_uri_port = ignore_localhost_redirect_uri_port
  @skip_consent = skip_consent
  @scopes = (Array(scopes) | IMPLICIT_SCOPES).freeze

  raise ArgumentError, "name starts with '_' is reserved" if @name&.start_with?('_')
  raise ArgumentError, "either secret or secret_hash must be present" if confidential && !@secret && !@secret_hash
end

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def id
  @id
end

#ignore_localhost_redirect_uri_portObject (readonly)

Returns the value of attribute ignore_localhost_redirect_uri_port.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def ignore_localhost_redirect_uri_port
  @ignore_localhost_redirect_uri_port
end

#nameObject (readonly)

Returns the value of attribute name.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def name
  @name
end

#preferred_key_groupObject (readonly)

Returns the value of attribute preferred_key_group.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def preferred_key_group
  @preferred_key_group
end

#redirect_urisObject (readonly)

Returns the value of attribute redirect_uris.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def redirect_uris
  @redirect_uris
end

#require_pkceObject (readonly)

Returns the value of attribute require_pkce.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def require_pkce
  @require_pkce
end

#scopesObject (readonly)

Returns the value of attribute scopes.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def scopes
  @scopes
end

Returns the value of attribute skip_consent.



33
34
35
# File 'lib/himari/client_registration.rb', line 33

def skip_consent
  @skip_consent
end

Instance Method Details

#as_logObject



72
73
74
# File 'lib/himari/client_registration.rb', line 72

def as_log
  {name: name, id: id, skip_consent: skip_consent, scopes: scopes}
end

#confidential?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'lib/himari/client_registration.rb', line 35

def confidential?
  @confidential
end

#filter_scopes(requested) ⇒ Object

Drop requested scopes this client does not recognise. OAuth servers are expected to ignore unknown scopes rather than reject the request (draft-ietf-oauth-v2-1 §3.2.2.1); request order is preserved.



68
69
70
# File 'lib/himari/client_registration.rb', line 68

def filter_scopes(requested)
  Array(requested).select { |scope| scopes.include?(scope) }
end

#match_hint?(id: nil) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
79
80
81
82
83
84
85
86
# File 'lib/himari/client_registration.rb', line 76

def match_hint?(id: nil)
  result = true

  result &&= if id
    id == self.id
  else
    true
  end

  result
end

#match_secret?(given_secret) ⇒ Boolean

Returns:

  • (Boolean)


43
44
45
46
47
48
49
50
51
52
# File 'lib/himari/client_registration.rb', line 43

def match_secret?(given_secret)
  return false unless confidential? && given_secret

  if @secret
    Rack::Utils.secure_compare(@secret, given_secret)
  else
    dgst = [secret_hash].pack('H*')
    Rack::Utils.secure_compare(dgst, Digest::SHA384.digest(given_secret))
  end
end

#redirect_uri_covers?(given) ⇒ Boolean

True when one of the registered redirect_uris covers the given (request) redirect_uri. draft-ietf-oauth-v2-1-15 §4.1.3 / RFC 3986 §6.2.1: simple (exact) string comparison, with the loopback-port exception of RFC 8252 §7.3 / draft-v2-1 §8.4.2 applied when enabled. A registered entry may also be a Regexp (operator-supplied via static config), matched against the request URI.

Returns:

  • (Boolean)


58
59
60
61
62
63
# File 'lib/himari/client_registration.rb', line 58

def redirect_uri_covers?(given)
  given = given.to_s
  return false if given.empty?

  redirect_uris.any? { |registered| redirect_uri_match?(registered, given) }
end

#secret_hashObject



39
40
41
# File 'lib/himari/client_registration.rb', line 39

def secret_hash
  @secret_hash ||= Digest::SHA384.hexdigest(secret)
end