Class: Himari::Services::UpstreamAuthentication

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

Defined Under Namespace

Classes: Result, UnauthorizedError

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(auth: nil, session: nil, grant_type: :initial, request: nil, claims_rules: [], authn_rules: [], logger: nil) ⇒ UpstreamAuthentication

Returns a new instance of UpstreamAuthentication.

Parameters:

  • auth (Hash, nil) (defaults to: nil)

    Omniauth Auth Hash (nil on revalidation)

  • session (Himari::SessionData, nil) (defaults to: nil)

    Existing session to revalidate (nil on initial login)

  • grant_type (Symbol) (defaults to: :initial)

    :initial for omniauth callback, :refresh_token for revalidation

  • claims_rules (Array<Himari::Rule>) (defaults to: [])

    Claims Rules

  • authn_rules (Array<Himari::Rule>) (defaults to: [])

    Authentication Rules

  • logger (Logger) (defaults to: nil)

Raises:

  • (ArgumentError)


47
48
49
50
51
52
53
54
55
56
57
# File 'lib/himari/services/upstream_authentication.rb', line 47

def initialize(auth: nil, session: nil, grant_type: :initial, request: nil, claims_rules: [], authn_rules: [], logger: nil)
  raise ArgumentError, "auth or session is required" if auth.nil? && session.nil?

  @request = request
  @auth = auth
  @session = session
  @grant_type = grant_type
  @claims_rules = claims_rules
  @authn_rules = authn_rules
  @logger = logger
end

Class Method Details

.from_request(request) ⇒ Object

Parameters:

  • request (Rack::Request)


60
61
62
63
64
65
66
67
68
69
# File 'lib/himari/services/upstream_authentication.rb', line 60

def self.from_request(request)
  new(
    auth: request.env.fetch('omniauth.auth'),
    grant_type: :initial,
    request: request,
    claims_rules: Himari::ProviderChain.new(request.env[Himari::Middlewares::ClaimsRule::RACK_KEY] || []).collect,
    authn_rules: Himari::ProviderChain.new(request.env[Himari::Middlewares::AuthenticationRule::RACK_KEY] || []).collect,
    logger: request.env['rack.logger'],
  )
end

.revalidate_from_request(session:, request:) ⇒ Object

Re-run claims/authn rules against an existing session, e.g. on refresh_token grant.

Parameters:

  • session (Himari::SessionData)

    existing session loaded from storage

  • request (Rack::Request)


75
76
77
78
79
80
81
82
83
84
# File 'lib/himari/services/upstream_authentication.rb', line 75

def self.revalidate_from_request(session:, request:)
  new(
    session: session,
    grant_type: :refresh_token,
    request: request,
    claims_rules: Himari::ProviderChain.new(request.env[Himari::Middlewares::ClaimsRule::RACK_KEY] || []).collect,
    authn_rules: Himari::ProviderChain.new(request.env[Himari::Middlewares::AuthenticationRule::RACK_KEY] || []).collect,
    logger: request.env['rack.logger'],
  )
end

Instance Method Details

#check_authn(claims_result, session_data) ⇒ Object

Raises:



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/himari/services/upstream_authentication.rb', line 139

def check_authn(claims_result, session_data)
  context = Himari::Decisions::Authentication::Context.new(provider: provider, claims: session_data.claims, user_data: session_data.user_data, request: @request, grant_type: @grant_type, refresh_info: @session&.refresh_info).freeze
  # Don't preseed decision.refresh_info from session; otherwise a no-op authn rule would clobber whatever
  # the claims rule wrote (via Claims#refresh_info=). Authn rules that want to preserve session.refresh_info
  # must read context.refresh_info and assign it explicitly.
  result = Himari::RuleProcessor.new(context, Himari::Decisions::Authentication.new).run(@authn_rules)

  @logger&.debug(Himari::LogLine.new('UpstreamAuthentication: authentication', objid: object_id.to_s(16), uid: uid_for_log, provider: provider, grant_type: @grant_type, authn_result: result.as_log))

  raise UnauthorizedError.new(Result.new(claims_result, result, nil)) unless result.allowed

  result
end

#derive_base_session(claims_result) ⇒ Object



129
130
131
132
133
134
135
136
137
# File 'lib/himari/services/upstream_authentication.rb', line 129

def derive_base_session(claims_result)
  decision = claims_result.decision
  if @session
    # revalidation: keep existing handle/secret/expiry, refresh claims/user_data
    @session.with(claims: decision.claims, user_data: decision.user_data)
  else
    decision.output
  end
end

#make_claimsObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/himari/services/upstream_authentication.rb', line 108

def make_claims
  context = Himari::Decisions::Claims::Context.new(request: @request, auth: @auth, provider: provider, grant_type: @grant_type, refresh_info: @session&.refresh_info).freeze
  result = Himari::RuleProcessor.new(context, Himari::Decisions::Claims.new).run(@claims_rules)

  @logger&.debug(Himari::LogLine.new('UpstreamAuthentication: claims', objid: object_id.to_s(16), uid: uid_for_log, provider: provider, grant_type: @grant_type, claims_result: result.as_log))

  if result.explicit_deny
    @logger&.warn(Himari::LogLine.new('UpstreamAuthentication: claims explicit deny', objid: object_id.to_s(16), uid: uid_for_log, provider: provider, grant_type: @grant_type, claims_result: result.as_log))
    raise UnauthorizedError.new(Result.new(result, nil, nil))
  end

  begin
    claims = result.decision&.output&.claims
    raise UnauthorizedError.new(Result.new(result, nil, nil)) unless claims
  rescue Himari::Decisions::Claims::UninitializedError
    raise UnauthorizedError.new(Result.new(result, nil, nil))
  end

  result
end

#performObject



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/himari/services/upstream_authentication.rb', line 94

def perform
  @logger&.debug(Himari::LogLine.new('UpstreamAuthentication: perform', objid: object_id.to_s(16), uid: uid_for_log, provider: provider, grant_type: @grant_type))
  claims_result = make_claims
  base = derive_base_session(claims_result)

  authn_result = check_authn(claims_result, base)
  final_refresh_info = authn_result.decision&.refresh_info || claims_result.decision&.refresh_info
  session_data = base.with(refresh_info: final_refresh_info)

  result = Result.new(claims_result, authn_result, session_data)
  @logger&.debug(Himari::LogLine.new('UpstreamAuthentication: result', objid: object_id.to_s(16), uid: uid_for_log, provider: provider, grant_type: @grant_type, result: result.as_log))
  result
end

#providerObject



86
87
88
# File 'lib/himari/services/upstream_authentication.rb', line 86

def provider
  (@auth && @auth[:provider]) || @session&.user_data&.dig(:provider)
end

#uid_for_logObject



90
91
92
# File 'lib/himari/services/upstream_authentication.rb', line 90

def uid_for_log
  (@auth && @auth[:uid]) || @session&.claims&.dig(:sub)
end