Class: RubyNative::OAuthMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_native/oauth_middleware.rb

Constant Summary collapse

"_ruby_native_oauth"
CALLBACK_SCHEME =

The scheme the native app registers is always "rubynative-" plus its bundle identifier with dots as dashes, built by OAuthManager.callbackScheme on both platforms. The scheme arrives as a request param and ends up as the target of the redirect carrying the session token, so anything that could name a different origin -- a colon, slash, dot, "@", or percent escape -- is rejected outright rather than sanitized.

/\Arubynative-[a-z0-9][a-z0-9_-]{0,127}\z/i

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ OAuthMiddleware

Returns a new instance of OAuthMiddleware.



13
14
15
# File 'lib/ruby_native/oauth_middleware.rb', line 13

def initialize(app)
  @app = app
end

Class Method Details

.build_token(cookies:, redirect_url:) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/ruby_native/oauth_middleware.rb', line 64

def self.build_token(cookies:, redirect_url:)
  encryptor.encrypt_and_sign(
    {cookies: cookies, redirect_url: redirect_url},
    expires_in: 5.minutes,
    purpose: "ruby_native_oauth"
  )
end

.encryptorObject



56
57
58
59
60
61
62
# File 'lib/ruby_native/oauth_middleware.rb', line 56

def self.encryptor
  @encryptor ||= begin
    key = ActiveSupport::KeyGenerator.new(Rails.application.secret_key_base)
      .generate_key("ruby_native_oauth", ActiveSupport::MessageEncryptor.key_len)
    ActiveSupport::MessageEncryptor.new(key)
  end
end

.read_token(token) ⇒ Object



72
73
74
75
76
77
# File 'lib/ruby_native/oauth_middleware.rb', line 72

def self.read_token(token)
  data = encryptor.decrypt_and_verify(token, purpose: "ruby_native_oauth")
  data&.symbolize_keys
rescue ActiveSupport::MessageEncryptor::InvalidMessage
  nil
end

Instance Method Details

#call(env) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ruby_native/oauth_middleware.rb', line 17

def call(env)
  request = ActionDispatch::Request.new(env)
  on_oauth_path = oauth_path?(request)
  started_native_oauth = on_oauth_path && request.params["ruby_native"] == "1"
  callback_scheme = permitted_scheme(request.params["callback_scheme"]) if started_native_oauth

  status, headers, body = @app.call(env)

  if on_oauth_path && redirect?(status) && (started_native_oauth || read_cookie(request))
    relax_cookie_samesite!(headers)
  end

  if started_native_oauth && callback_scheme.present? && redirect?(status)
    Rails.logger.debug { "[RubyNative] OAuth started for #{request.path}, setting tracking cookie" }
    set_cookie(headers, callback_scheme)
  end

  stored_scheme = permitted_scheme(read_cookie(request))

  if stored_scheme && redirect?(status)
    location = headers["location"] || headers["Location"]

    if auth_failure?(location)
      Rails.logger.info { "[RubyNative] OAuth failed, redirecting to native app" }
      delete_cookie(headers)
      return redirect_to_native(stored_scheme, error: true)
    end

    if internal_redirect?(request, location)
      token = build_token(headers, location)
      Rails.logger.info { "[RubyNative] OAuth succeeded, redirecting to native app" }
      delete_cookie(headers)
      return redirect_to_native(stored_scheme, token: token)
    end
  end

  [status, headers, body]
end