Module: Koi::Identity

Defined in:
app/models/koi/identity.rb,
app/models/koi/identity/member.rb,
app/models/koi/identity/provider.rb,
app/models/koi/identity/assertion.rb,
app/models/koi/identity/principal.rb

Defined Under Namespace

Classes: Assertion, Member, Principal, Provider

Class Method Summary collapse

Class Method Details

.authorize_bearer_token!(token, audience:) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
# File 'app/models/koi/identity.rb', line 10

def authorize_bearer_token!(token, audience:)
  assertion = Assertion.new(token)
  provider  = provider_for(assertion)

  provider.audience ||= audience
  raise JWT::InvalidAudError, "no expected audience" if provider.audience.blank?

  assertion.verify!(provider)
rescue JWT::DecodeError => e
  Rails.logger.warn("#{e.class}: Koi::Identity rejected assertion #{assertion.inspect}: #{e.message}")
  raise
end

.membersObject



52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/models/koi/identity.rb', line 52

def members
  provider_names = Koi.config.identity.providers.keys.map(&:to_s)

  Koi.config.identity.members.map do |name, config|
    member = Member.new(name:, provider_names:, **config)
    unless member.valid?
      raise ArgumentError, "Invalid identity member #{name}: #{member.errors.full_messages.to_sentence}"
    end

    member
  end
end

.principal_for(provider, assertion) ⇒ Object

Resolves the member matching the verified (provider, subject) into a principal, carrying whatever identity attributes the issuer publishes.



30
31
32
33
34
35
36
37
38
39
# File 'app/models/koi/identity.rb', line 30

def principal_for(provider, assertion)
  member = members.find { |m| m.provider == provider.name && m.subject == assertion.subject }

  return if member.nil?

  Principal.new(provider: provider.name,
                scope:    member.scope,
                subject:  assertion.subject,
                **provider.identity_attributes(assertion.claims))
end

.provider_for(assertion) ⇒ Object



23
24
25
26
# File 'app/models/koi/identity.rb', line 23

def provider_for(assertion)
  providers.find { |provider| provider.issuer == assertion.issuer } ||
    raise(JWT::InvalidIssuerError, "unknown issuer #{assertion.issuer}")
end

.providersObject



41
42
43
44
45
46
47
48
49
50
# File 'app/models/koi/identity.rb', line 41

def providers
  Koi.config.identity.providers.map do |name, config|
    provider = Provider.new(name:, **config)
    unless provider.valid?
      raise ArgumentError, "Invalid identity provider #{name}: #{provider.errors.full_messages.to_sentence}"
    end

    provider
  end
end

.role_slugsObject

Role slugs granted by config; roles outside this set are not assumable.



66
67
68
# File 'app/models/koi/identity.rb', line 66

def role_slugs
  members.filter_map(&:role_slug)
end