atlas-auth 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80f3e5a3fa6f404e3416096b1315e90babd798263583e3f61a47a33c59bf21b6
4
- data.tar.gz: 9f243710f91725c971972e8216e54348dd23009bb60ec0189929b168d7f0775c
3
+ metadata.gz: 5d81044a8a8e996684c431cc963aa400821b8891b8eba14414b149230d5b7f9a
4
+ data.tar.gz: 437fbfbf228baacec51ce97db494bd080237d9d2bc96f31e895afa1ce0c78c7e
5
5
  SHA512:
6
- metadata.gz: 00ed1612a4c79bc241ec64c2d1a9d49cd637292c1da84baaf8efbb480ed1afcf344f1ad556aca5825a0ddb629da8f6ad095f1dc6fa90f1303e710ad79c23f4c7
7
- data.tar.gz: 4e11a62f2eb27a06a15f8512d4d0a9bc85083fc7cdbf058754113b1112affb1be4dc37345c494625cb514ab57a48cd9bd01782b1178e77c13a973ad87c96e3af
6
+ metadata.gz: b233a0b594a57f68abcb5722d22ec4685fb2436c65c033e8dd083b1f28a4c222eb67579fa61b812a366200b2d5f8928bf017bafe00911e0885f2ed049af6ee03
7
+ data.tar.gz: aac3d1478f65863e359c4bbeeedafcaeabfc4c0afa0e5164f26d0b99a0e924789ece9b42a5ad28ae8a59ea751fe595ffeb9142f4631d1e6ac7bc9987a570ba6d
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Atlas
6
+ module Rails
7
+ # Controller mix-in exposing the request's Atlas session.
8
+ #
9
+ # The {Atlas::Rails::Middleware} has already verified the token by the time
10
+ # an action runs, so these helpers only read +env["atlas.auth"]+ — they
11
+ # never touch crypto.
12
+ #
13
+ # class ApplicationController < ActionController::Base
14
+ # include Atlas::Rails::Controller
15
+ # end
16
+ #
17
+ # class BillingController < ApplicationController
18
+ # before_action :require_atlas_auth!
19
+ # before_action -> { atlas_authorize!(permission: "org:billing:manage") }
20
+ #
21
+ # def show
22
+ # render json: { user: current_atlas_user_id }
23
+ # end
24
+ # end
25
+ module Controller
26
+ extend ActiveSupport::Concern
27
+
28
+ # The verified {Atlas::VerifyResult} for this request (ok or failed), or a
29
+ # failure result when the middleware is not in the stack.
30
+ # @return [Atlas::VerifyResult]
31
+ def current_atlas_session
32
+ request.env[Atlas::Rails::Middleware::ENV_KEY] ||
33
+ Atlas::VerifyResult.failure(:invalid)
34
+ end
35
+
36
+ # The verified claims hash (string-keyed), or nil when unauthenticated.
37
+ # @return [Hash, nil]
38
+ def current_atlas_claims
39
+ session = current_atlas_session
40
+ session.ok? ? session.claims : nil
41
+ end
42
+
43
+ # The authenticated user id (+sub+), or nil.
44
+ # @return [String, nil]
45
+ def current_atlas_user_id
46
+ current_atlas_claims&.fetch("sub", nil)
47
+ end
48
+
49
+ # True when this request carries a valid Atlas session.
50
+ def atlas_authenticated?
51
+ current_atlas_session.ok?
52
+ end
53
+
54
+ # +before_action+ guard: render 401 and halt unless authenticated.
55
+ def require_atlas_auth!
56
+ return true if atlas_authenticated?
57
+
58
+ render_atlas_unauthorized
59
+ false
60
+ end
61
+
62
+ # +before_action+ guard for a specific authorization condition, e.g.
63
+ # +atlas_authorize!(permission: "org:billing:manage")+. Renders 401 when
64
+ # unauthenticated and 403 when authenticated but not permitted. Accepts the
65
+ # same conditions as {Atlas::VerifyResult#has?}
66
+ # (+:permission+, +:role+, +:any_permission+, +:all_permissions+).
67
+ def atlas_authorize!(**condition)
68
+ unless atlas_authenticated?
69
+ render_atlas_unauthorized
70
+ return false
71
+ end
72
+
73
+ unless current_atlas_session.has?(condition)
74
+ render_atlas_forbidden
75
+ return false
76
+ end
77
+
78
+ true
79
+ end
80
+
81
+ private
82
+
83
+ def render_atlas_unauthorized
84
+ render json: {
85
+ errors: [{ "code" => "UNAUTHENTICATED", "message" => "Authentication required." }],
86
+ }, status: :unauthorized
87
+ end
88
+
89
+ def render_atlas_forbidden
90
+ render json: {
91
+ errors: [{ "code" => "FORBIDDEN", "message" => "You do not have permission to do that." }],
92
+ }, status: :forbidden
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atlas
4
+ module Rails
5
+ # Rack middleware that authenticates every request and never blocks one.
6
+ #
7
+ # It reads the session JWT from the +Authorization: Bearer+ header (which
8
+ # wins) or the +__session+ cookie, verifies it through the gem's
9
+ # {Atlas::Backend}, and leaves the {Atlas::VerifyResult} at
10
+ # +env["atlas.auth"]+. Whether the token is present, absent or bad, the
11
+ # request passes through — enforcement is the controller's job
12
+ # ({Atlas::Rails::Controller#require_atlas_auth!}), so a public action and a
13
+ # protected one can share the same stack.
14
+ class Middleware
15
+ # The Rack env key the verified result is stashed under.
16
+ ENV_KEY = "atlas.auth"
17
+
18
+ # @param app [#call] the next Rack app.
19
+ # @param backend [Atlas::Backend, nil] an explicit verifier; when nil the
20
+ # shared {Atlas.configuration} backend is used (and built lazily).
21
+ # @param configuration [Atlas::Configuration, nil] an explicit config.
22
+ def initialize(app, backend: nil, configuration: nil)
23
+ @app = app
24
+ @backend = backend
25
+ @configuration = configuration
26
+ end
27
+
28
+ def call(env)
29
+ env[ENV_KEY] = authenticate(env)
30
+ @app.call(env)
31
+ end
32
+
33
+ private
34
+
35
+ # Always returns a {Atlas::VerifyResult}, never raises. A missing or
36
+ # unconfigured backend degrades to "unauthenticated", never a 500.
37
+ def authenticate(env)
38
+ verifier = backend
39
+ return Atlas::VerifyResult.failure(:invalid) if verifier.nil?
40
+
41
+ verifier.authenticate_request(
42
+ "authorization" => env["HTTP_AUTHORIZATION"],
43
+ "cookie" => env["HTTP_COOKIE"],
44
+ )
45
+ rescue StandardError
46
+ Atlas::VerifyResult.failure(:invalid)
47
+ end
48
+
49
+ def backend
50
+ @backend ||= (@configuration || Atlas.configuration).backend
51
+ rescue Atlas::ConfigurationError
52
+ nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defined only inside a Rails app. Requiring "atlas/rails" from a plain Ruby
4
+ # process (no Rails loaded) leaves this a no-op — the Railtie never comes into
5
+ # being, so nothing tries to touch a middleware stack that isn't there.
6
+ if defined?(::Rails::Railtie)
7
+ module Atlas
8
+ module Rails
9
+ # Wires the integration into the host Rails app with no manual steps:
10
+ # inserts the auth middleware and mixes the controller helpers into every
11
+ # controller once ActionController loads.
12
+ class Railtie < ::Rails::Railtie
13
+ initializer "atlas.middleware" do |app|
14
+ app.middleware.use Atlas::Rails::Middleware
15
+ end
16
+
17
+ initializer "atlas.controller" do
18
+ ActiveSupport.on_load(:action_controller) do
19
+ include Atlas::Rails::Controller
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../atlas"
4
+
5
+ # atlas-auth — Rails-native integration.
6
+ #
7
+ # This file is loaded ONLY when you ask for it:
8
+ #
9
+ # # Gemfile
10
+ # gem "atlas-auth", require: "atlas/rails"
11
+ #
12
+ # Requiring the base gem (+require "atlas"+) never pulls Rails in, so the SDK
13
+ # stays usable in a plain Ruby or non-Rails service. Loading this file in a
14
+ # Rails app registers {Atlas::Rails::Railtie}, which:
15
+ #
16
+ # * inserts {Atlas::Rails::Middleware} — it verifies the session JWT carried by
17
+ # the +Authorization: Bearer+ header (or the +__session+ cookie) and stashes
18
+ # the {Atlas::VerifyResult} in +env["atlas.auth"]+, and
19
+ # * mixes {Atlas::Rails::Controller} into every controller, giving you
20
+ # +current_atlas_session+, +current_atlas_user_id+, +atlas_authenticated?+,
21
+ # +require_atlas_auth!+ and +atlas_authorize!+.
22
+ #
23
+ # All verification is delegated to the gem's existing {Atlas::Backend} — this
24
+ # layer adds no crypto of its own. Configure it once:
25
+ #
26
+ # Atlas.configure do |c|
27
+ # c.jwks_url = "https://your-instance.atlasauth.net/v1/jwks"
28
+ # c.issuer = "https://your-instance.atlasauth.net"
29
+ # c.secret_key = Rails.application.credentials.dig(:atlas, :secret_key)
30
+ # end
31
+ #
32
+ # or leave it unset and let it read +ATLAS_JWKS_URL+ / +ATLAS_ISSUER+ /
33
+ # +ATLAS_SECRET_KEY+ from the environment or Rails credentials (+atlas:+).
34
+ module Atlas
35
+ # Process-wide configuration for the Rails integration. A single
36
+ # {Atlas::Backend} is built from it, lazily, and reused across requests
37
+ # (its JWKS cache is the whole point of keeping one instance).
38
+ class Configuration
39
+ # @return [String, nil] the instance JWKS URL (+ATLAS_JWKS_URL+).
40
+ attr_accessor :jwks_url
41
+ # @return [String, nil] expected +iss+ (+ATLAS_ISSUER+).
42
+ attr_accessor :issuer
43
+ # @return [String, nil] +sk_+ key, only needed for +verify_online+ (+ATLAS_SECRET_KEY+).
44
+ attr_accessor :secret_key
45
+ # @return [String, nil] BAPI base URL, only needed for +verify_online+.
46
+ attr_accessor :bapi_base_url
47
+ # @return [Array<String>, nil] optional +azp+ allowlist.
48
+ attr_accessor :authorized_parties
49
+
50
+ def initialize
51
+ @jwks_url = resolve("ATLAS_JWKS_URL", :jwks_url)
52
+ @issuer = resolve("ATLAS_ISSUER", :issuer)
53
+ @secret_key = resolve("ATLAS_SECRET_KEY", :secret_key)
54
+ @bapi_base_url = resolve("ATLAS_BAPI_BASE_URL", :bapi_base_url)
55
+ @authorized_parties = nil
56
+ @backend = nil
57
+ end
58
+
59
+ # True once the minimum needed for local verification is present.
60
+ def configured?
61
+ !blank?(jwks_url) && !blank?(issuer)
62
+ end
63
+
64
+ # The shared, memoized verifier. Raises {Atlas::ConfigurationError} rather
65
+ # than silently building a backend that can never verify anything.
66
+ # @return [Atlas::Backend]
67
+ def backend
68
+ unless configured?
69
+ raise Atlas::ConfigurationError,
70
+ "Atlas Rails integration needs jwks_url and issuer. Set them via " \
71
+ "Atlas.configure, or ATLAS_JWKS_URL / ATLAS_ISSUER (env or Rails " \
72
+ "credentials under `atlas:`)."
73
+ end
74
+
75
+ @backend ||= Atlas::Backend.new(
76
+ jwks_url: jwks_url,
77
+ issuer: issuer,
78
+ authorized_parties: authorized_parties,
79
+ secret_key: secret_key,
80
+ bapi_base_url: bapi_base_url,
81
+ )
82
+ end
83
+
84
+ # Drop the memoized backend so the next request rebuilds it. Called after
85
+ # {Atlas.configure} so edited values (and a fresh JWKS cache) take effect.
86
+ def reset_backend!
87
+ @backend = nil
88
+ end
89
+
90
+ private
91
+
92
+ def blank?(value)
93
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
94
+ end
95
+
96
+ # ENV first, then Rails credentials under the +atlas:+ namespace.
97
+ def resolve(env_key, cred_key)
98
+ value = ENV[env_key]
99
+ return value unless blank?(value)
100
+
101
+ rails_credential(cred_key)
102
+ end
103
+
104
+ def rails_credential(key)
105
+ return nil unless defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application
106
+
107
+ atlas = ::Rails.application.credentials.respond_to?(:atlas) ? ::Rails.application.credentials.atlas : nil
108
+ return nil if atlas.nil?
109
+
110
+ atlas.respond_to?(:[]) ? (atlas[key] || atlas[key.to_s]) : nil
111
+ rescue StandardError
112
+ nil
113
+ end
114
+ end
115
+
116
+ class << self
117
+ # The current {Atlas::Configuration}, created on first use.
118
+ def configuration
119
+ @configuration ||= Configuration.new
120
+ end
121
+
122
+ # Configure the Rails integration.
123
+ #
124
+ # Atlas.configure do |c|
125
+ # c.jwks_url = "https://your-instance.atlasauth.net/v1/jwks"
126
+ # c.issuer = "https://your-instance.atlasauth.net"
127
+ # end
128
+ #
129
+ # @yieldparam config [Atlas::Configuration]
130
+ # @return [Atlas::Configuration]
131
+ def configure
132
+ yield(configuration) if block_given?
133
+ configuration.reset_backend!
134
+ configuration
135
+ end
136
+
137
+ # Forget all configuration. Primarily for tests.
138
+ def reset_configuration!
139
+ @configuration = nil
140
+ end
141
+ end
142
+ end
143
+
144
+ require_relative "rails/middleware"
145
+ require_relative "rails/controller"
146
+ require_relative "rails/railtie"
data/lib/atlas/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Atlas
4
4
  # The gem version. Tracks the TypeScript/Python backend SDK line.
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atlas-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Atlas
@@ -51,6 +51,34 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rails
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '6.1'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '6.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rack-test
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '1.1'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '1.1'
54
82
  description: |
55
83
  A typed Ruby client over the Atlas Backend API (BAPI), the Ruby peer of the
56
84
  TypeScript @atlasauth/backend and Python atlas-backend SDKs. Covers the full
@@ -72,6 +100,10 @@ files:
72
100
  - lib/atlas/handshake.rb
73
101
  - lib/atlas/jwks_cache.rb
74
102
  - lib/atlas/pagination.rb
103
+ - lib/atlas/rails.rb
104
+ - lib/atlas/rails/controller.rb
105
+ - lib/atlas/rails/middleware.rb
106
+ - lib/atlas/rails/railtie.rb
75
107
  - lib/atlas/resources/actions.rb
76
108
  - lib/atlas/resources/actor_tokens.rb
77
109
  - lib/atlas/resources/api_keys.rb
@@ -124,7 +156,7 @@ licenses:
124
156
  - MIT
125
157
  metadata:
126
158
  homepage_uri: https://atlasauth.net
127
- source_code_uri: https://github.com/atlas-auth/atlas-ruby
159
+ source_code_uri: https://github.com/alphajew420/ssoly/tree/main/sdks/ruby
128
160
  documentation_uri: https://atlasauth.net/docs/sdks/ruby
129
161
  rubygems_mfa_required: 'true'
130
162
  rdoc_options: []