Module: Everywhere::Config::Auth

Included in:
Everywhere::Config
Defined in:
lib/everywhere/config/auth.rb

Constant Summary collapse

DEFAULT_OAUTH_PATHS =

Paths that begin a third-party auth flow, as regex source strings matched against the request path. Declaring auth: at all opts in, defaulting to OmniAuth's /auth/… convention.

["^/auth/"].freeze
URL_SCHEME =

The custom URL scheme ASWebAuthenticationSession returns through. Defaults to the iOS bundle id — the convention, and already unique per app. Only the characters RFC 3986 allows in a scheme.

/\A[a-zA-Z][a-zA-Z0-9+.-]*\z/

Instance Method Summary collapse

Instance Method Details

#authObject

Third-party sign-in (Sign in with Apple / Google / GitHub / anything OmniAuth speaks). Providers refuse to run — or run badly — inside an app's web view, so the mobile shell hands these paths to an ASWebAuthenticationSession instead, and the gem bridges the resulting session back into the app (see AuthHandoff).

auth:
oauth_paths:                # paths that begin a provider flow
  - ^/auth/                 # unanchored regexes, like `rules:`
scheme: com.example.app     # callback scheme (default: the iOS bundle id)
cookies:                    # which cookies cross back into the app
  except: ["_ga"]           #   (default: all of them)

The app keeps its own auth: auth: declares nothing about providers, only which paths the shell must not open in its web view.



21
# File 'lib/everywhere/config/auth.rb', line 21

def auth = @data.fetch("auth", nil) || {}

#auth_cookie?(name) ⇒ Boolean

Cookie names to carry from the auth browser back into the app's web view, filtered by an optional allow/deny list. Everything the flow set is carried by default: the gem can't know which cookie an app's auth library signs its session with.

Returns:

  • (Boolean)


67
68
69
70
71
72
73
74
75
# File 'lib/everywhere/config/auth.rb', line 67

def auth_cookie?(name)
  cookies = auth["cookies"]
  return true unless cookies.is_a?(Hash)

  only = Array(cookies["only"]).map(&:to_s)
  return only.include?(name.to_s) unless only.empty?

  !Array(cookies["except"]).map(&:to_s).include?(name.to_s)
end

#auth_errorsObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/everywhere/config/auth.rb', line 81

def auth_errors
  return [] unless @data.key?("auth")

  errors = oauth_paths.filter_map do |pattern|
    Regexp.new(pattern)
    nil
  rescue RegexpError => e
    "auth.oauth_paths: #{pattern.inspect} is not a valid pattern (#{e.message})"
  end

  unless auth_scheme.match?(URL_SCHEME)
    errors << "auth.scheme #{auth_scheme.inspect} is not a URL scheme (letters, digits, +, -, .)"
  end
  errors
end

#auth_schemeObject



58
59
60
61
# File 'lib/everywhere/config/auth.rb', line 58

def auth_scheme
  explicit = auth["scheme"].to_s.strip
  explicit.empty? ? bundle_id(target: "ios") : explicit
end

#auth_shell_hashObject

The auth subset the shell needs: which visits to divert into the auth session, and the scheme to bring the answer back through. nil (absent from everywhere.json) when the app declares no auth: — a shell that can't divert anything is a shell with no new surface.



101
102
103
104
105
# File 'lib/everywhere/config/auth.rb', line 101

def auth_shell_hash
  return nil unless oauth?

  { "oauth_paths" => oauth_paths, "scheme" => auth_scheme }
end

#auth_token_ttlObject

How long a minted handoff token stays valid. It travels device-locally (browser sheet → shell → web view), so seconds are plenty.



79
# File 'lib/everywhere/config/auth.rb', line 79

def auth_token_ttl = [(auth["token_ttl"] || 60).to_i, 5].max

#oauth?Boolean

Returns:

  • (Boolean)


35
# File 'lib/everywhere/config/auth.rb', line 35

def oauth?  = !oauth_paths.empty?

#oauth_path?(path) ⇒ Boolean

Whether a request path starts a provider flow. Both halves of the system ask this — the middleware per request, the shell per visit proposal — so the patterns are compiled the same way on both sides (unanchored regex).

Returns:

  • (Boolean)


40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/everywhere/config/auth.rb', line 40

def oauth_path?(path)
  # Compiled once per Config, not once per request: the middleware calls
  # this on every navigation, and the instance it holds lives until
  # everywhere.yml changes on disk. An unparseable pattern matches nothing
  # (auth_errors is what reports it).
  @oauth_regexps ||= oauth_paths.filter_map do |pattern|
    Regexp.new(pattern)
  rescue RegexpError
    nil
  end
  @oauth_regexps.any? { |regexp| regexp.match?(path.to_s) }
end

#oauth_pathsObject



28
29
30
31
32
33
# File 'lib/everywhere/config/auth.rb', line 28

def oauth_paths
  return [] unless @data.key?("auth")

  paths = Array(auth["oauth_paths"]).map { |p| p.to_s.strip }.reject(&:empty?)
  paths.empty? ? DEFAULT_OAUTH_PATHS.dup : paths
end