nulogy_sso 0.4.0 → 0.5.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 +4 -4
- data/README.md +6 -6
- data/app/controllers/nulogy_sso/authentication_controller.rb +18 -18
- data/app/services/nulogy_sso/authenticator.rb +4 -4
- data/lib/nulogy_sso/controller_helper.rb +1 -1
- data/lib/nulogy_sso/engine.rb +2 -2
- data/lib/nulogy_sso/test_utilities/jwt_test_helper.rb +2 -2
- data/lib/nulogy_sso/version.rb +1 -1
- data/lib/nulogy_sso.rb +11 -11
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/config/{auth_sso.yml → sso.yml} +0 -0
- data/spec/features/nulogy_sso/sso_login_spec.rb +1 -1
- data/spec/integration/services/nulogy_sso/authenticator_spec.rb +3 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4d1f9f535ed99d1575c31fded6cc2712f9bc22f4e19809236ec478a57715792
|
4
|
+
data.tar.gz: 779c7dcb20b58215370b4bae528862c12ce8e528d5f1da9830fafb5f27699c3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b8b7bb65b92718bb6a699e085a00161a1e7895c76b1f339fb0bf4e7962d5708bde3657b24ee5073c8afe1a6adf3dac5eb90eb7f32aedce209c88c7fde3d8af1
|
7
|
+
data.tar.gz: 0676e085475f7915b3f7fff3e6197d27d87856c0aa4853f7fc5c5417dea8e6b7655c88ceccb878a263e4eae64ee59d32d96254be5c1d27467b2e018f1db09670
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# NulogySSO
|
2
2
|
|
3
|
-

|
3
|
+
[](https://rubygems.org/gems/nulogy_sso "View this project in Rubygems")
|
4
4
|
|
5
5
|
**This repo is still under heavy initial development and is not ready to be used by any other product besides CPI. This status will be changed very shortly.**
|
6
6
|
|
@@ -32,12 +32,12 @@ get "logout", to: redirect("sso/logout")
|
|
32
32
|
|
33
33
|
The engine now needs to be configured. First create a YAML config file, perhaps named `config/auth_sso.yml`, to configure your app's Auth0 settings. This assumes that the necessary Auth0 applications have been created in the correct Auth0 tenants. The [CPI auth_sso.yml file](https://github.com/nulogy/Common-Platform-Interface/blob/master/config/auth_sso.yml) is a good starting place.
|
34
34
|
|
35
|
-
With that available, you can configure the engine with an initializer file. This is where _NulogySSO_ can be customized according to your application's needs. Put the below code into `config/initializers/nulogy_sso.rb`, with the appropriate modifications implemented. For `
|
35
|
+
With that available, you can configure the engine with an initializer file. This is where _NulogySSO_ can be customized according to your application's needs. Put the below code into `config/initializers/nulogy_sso.rb`, with the appropriate modifications implemented. For `sso_config`, refer to [nulogy_sso.rb](lib/nulogy_sso.rb) for a list of required keys and [sso_config.yml](spec/dummy/config/sso_config.yml) for an example config file.
|
36
36
|
|
37
37
|
```ruby
|
38
38
|
# Compiles config/auth_sso.yml into a Ruby object. An error is thrown if required keys are missing.
|
39
39
|
# See lib/nulogy_sso.rb for required keys.
|
40
|
-
NulogySSO.
|
40
|
+
NulogySSO.sso_config = Rails::Application.config_for(:sso)
|
41
41
|
|
42
42
|
# Return the user matching the provided email, or nil if not found.
|
43
43
|
NulogySSO.find_user_by_email = ->(email) { nil }
|
@@ -48,7 +48,7 @@ NulogySSO.find_user_by_email = ->(email) { nil }
|
|
48
48
|
NulogySSO.handle_sso_error = ->(controller) { }
|
49
49
|
```
|
50
50
|
|
51
|
-
The app is now ready to authenticate a user with Auth0! With
|
51
|
+
The app is now ready to authenticate a user with Auth0! With NulogySSO and Auth0, the user's identity is maintained across requests (and apps!) via a [JWT](https://auth0.com/docs/jwt) stored as a browser cookie. Add this code to the `ApplicationController`:
|
52
52
|
|
53
53
|
```ruby
|
54
54
|
class ApplicationController < ActionController::Base
|
@@ -79,9 +79,9 @@ docker-compose up -d
|
|
79
79
|
|
80
80
|
### Testing
|
81
81
|
|
82
|
-
There are multiple helpers made available via the `
|
82
|
+
There are multiple helpers made available via the `NulogySSO::TestUtilities` module. These are helpful for doing things such as grabbing test JWT values and interacting with a [Mockserver](https://github.com/jamesdbloom/mockserver) mock of the Auth0 API.
|
83
83
|
|
84
|
-
It is a common use case for a Rails app to switch from Devise-powered authentication to Auth0. Here's a pattern that could be applied around a feature flag (e.g. environment variable) to switch between Devise user authentication test helpers and
|
84
|
+
It is a common use case for a Rails app to switch from Devise-powered authentication to Auth0. Here's a pattern that could be applied around a feature flag (e.g. environment variable) to switch between Devise user authentication test helpers and NulogySSO test helpers: _(TODO: insert link to CPI `ControllerIntegrationSpecMacros`)_
|
85
85
|
|
86
86
|
### Contributing
|
87
87
|
|
@@ -11,17 +11,17 @@ module NulogySSO
|
|
11
11
|
|
12
12
|
def initialize
|
13
13
|
# These instance variables have to be set in order for the HTTPProxy mixin to work.
|
14
|
-
@base_uri =
|
14
|
+
@base_uri = sso_config.base_uri
|
15
15
|
@headers = { content_type: "application/json" }
|
16
16
|
end
|
17
17
|
|
18
18
|
def login
|
19
|
-
raw_access_token = cookies[NulogySSO.
|
19
|
+
raw_access_token = cookies[NulogySSO.sso_cookie_key]
|
20
20
|
|
21
21
|
authenticator.validate_token(
|
22
22
|
raw_access_token,
|
23
23
|
on_success: method(:on_authentication_success),
|
24
|
-
on_invalid_token: -> { redirect_to
|
24
|
+
on_invalid_token: -> { redirect_to auth0_authorize_path }
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
@@ -41,18 +41,18 @@ module NulogySSO
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def logout
|
44
|
-
cookies.delete(NulogySSO.
|
44
|
+
cookies.delete(NulogySSO.sso_cookie_key, domain: :all)
|
45
45
|
|
46
46
|
query_params = {
|
47
|
-
returnTo:
|
48
|
-
client_id:
|
47
|
+
returnTo: sso_config.redirect_uri, # Yes, this must be camelCased
|
48
|
+
client_id: sso_config.client_id
|
49
49
|
}
|
50
|
-
redirect_to "#{
|
50
|
+
redirect_to "#{sso_config.base_uri}/v2/logout?#{query_params.to_query}"
|
51
51
|
end
|
52
52
|
|
53
53
|
private
|
54
54
|
|
55
|
-
delegate :
|
55
|
+
delegate :sso_config, to: :NulogySSO
|
56
56
|
|
57
57
|
def sso_error
|
58
58
|
NulogySSO.handle_sso_error.call(self)
|
@@ -65,20 +65,20 @@ module NulogySSO
|
|
65
65
|
def on_authentication_success(access_token)
|
66
66
|
respond_with_cookies(access_token)
|
67
67
|
|
68
|
-
redirect_to params["origin"].presence ||
|
68
|
+
redirect_to params["origin"].presence || sso_config.redirect_uri
|
69
69
|
end
|
70
70
|
|
71
71
|
def token_response(code)
|
72
72
|
exchange_auth_code_for_tokens(
|
73
73
|
code,
|
74
|
-
redirect_uri:
|
75
|
-
client_id:
|
76
|
-
client_secret:
|
74
|
+
redirect_uri: sso_config.login_uri,
|
75
|
+
client_id: sso_config.client_id,
|
76
|
+
client_secret: sso_config.client_secret
|
77
77
|
)
|
78
78
|
end
|
79
79
|
|
80
80
|
def respond_with_cookies(access_token_value)
|
81
|
-
cookies[NulogySSO.
|
81
|
+
cookies[NulogySSO.sso_cookie_key] = {
|
82
82
|
value: access_token_value,
|
83
83
|
domain: :all,
|
84
84
|
expires: 36_000.seconds, # TODO: Fetch this value from the JWT
|
@@ -87,16 +87,16 @@ module NulogySSO
|
|
87
87
|
}
|
88
88
|
end
|
89
89
|
|
90
|
-
def
|
90
|
+
def auth0_authorize_path
|
91
91
|
query_params = {
|
92
|
-
audience:
|
93
|
-
client_id:
|
92
|
+
audience: sso_config.audience,
|
93
|
+
client_id: sso_config.client_id,
|
94
94
|
response_type: "code",
|
95
95
|
scope: "openid email",
|
96
|
-
redirect_uri: "#{
|
96
|
+
redirect_uri: "#{sso_config.login_uri}?origin=#{session[:previous_request_url]}"
|
97
97
|
}
|
98
98
|
|
99
|
-
"#{
|
99
|
+
"#{sso_config.base_uri}/authorize?#{query_params.to_query}"
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -5,9 +5,9 @@ require "auth0_rs256_jwt_verifier"
|
|
5
5
|
module NulogySSO
|
6
6
|
class Authenticator
|
7
7
|
ACCESS_TOKEN_VERIFIER = Auth0RS256JWTVerifier.new(
|
8
|
-
issuer: "#{NulogySSO.
|
9
|
-
audience: NulogySSO.
|
10
|
-
jwks_url: "#{NulogySSO.
|
8
|
+
issuer: "#{NulogySSO.sso_config.base_uri}/", # Auth0 requires a backslash on the Issuer
|
9
|
+
audience: NulogySSO.sso_config.audience,
|
10
|
+
jwks_url: "#{NulogySSO.sso_config.base_uri}/.well-known/jwks.json"
|
11
11
|
)
|
12
12
|
|
13
13
|
def initialize(verifier: ACCESS_TOKEN_VERIFIER, find_user_by_email: NulogySSO.find_user_by_email)
|
@@ -15,7 +15,7 @@ module NulogySSO
|
|
15
15
|
@find_user_by_email = find_user_by_email
|
16
16
|
end
|
17
17
|
|
18
|
-
#
|
18
|
+
# Validated the provided JWT, ensuring that an authenticated Auth0 user can be associated to the token and matches an existing app user
|
19
19
|
def validate_token(raw_access_token, on_success:, on_invalid_token:)
|
20
20
|
access_token = decoded_validated_access_token(raw_access_token)
|
21
21
|
|
@@ -16,7 +16,7 @@ module NulogySSO
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def authenticate_sso_user
|
19
|
-
raw_token = cookies[NulogySSO.
|
19
|
+
raw_token = cookies[NulogySSO.sso_cookie_key]
|
20
20
|
return redirect_to nulogy_sso.login_path if raw_token.blank?
|
21
21
|
|
22
22
|
@current_user = Authenticator.new.authenticated_user(raw_token)
|
data/lib/nulogy_sso/engine.rb
CHANGED
@@ -13,8 +13,8 @@ module NulogySSO
|
|
13
13
|
Bundler.require(*Rails.groups)
|
14
14
|
|
15
15
|
config.after_initialize do
|
16
|
-
if NulogySSO.
|
17
|
-
raise "Missing
|
16
|
+
if NulogySSO.sso_config.blank?
|
17
|
+
raise "Missing sso_config config object. Consider using config_for() to load a YAML config file."
|
18
18
|
end
|
19
19
|
|
20
20
|
if NulogySSO.find_user_by_email.blank?
|
@@ -20,9 +20,9 @@ module NulogySSO
|
|
20
20
|
def jwt(email, overrides = {})
|
21
21
|
claim = {
|
22
22
|
NulogySSO::JWT_EMAIL_KEY => email,
|
23
|
-
"iss" => "#{NulogySSO.
|
23
|
+
"iss" => "#{NulogySSO.sso_config.base_uri}/",
|
24
24
|
"sub" => "MOCK",
|
25
|
-
"aud" => [NulogySSO.
|
25
|
+
"aud" => [NulogySSO.sso_config.audience],
|
26
26
|
"exp" => (Time.now + 1.day).to_i
|
27
27
|
}.merge(overrides)
|
28
28
|
|
data/lib/nulogy_sso/version.rb
CHANGED
data/lib/nulogy_sso.rb
CHANGED
@@ -5,23 +5,23 @@ require "immutable-struct"
|
|
5
5
|
|
6
6
|
module NulogySSO
|
7
7
|
# Config variables for the engine
|
8
|
-
mattr_accessor :
|
8
|
+
mattr_accessor :sso_config, :find_user_by_email, :handle_sso_error
|
9
9
|
|
10
10
|
# Public Constants
|
11
11
|
JWT_EMAIL_KEY = "https://nulogy.net/email"
|
12
12
|
|
13
|
-
def self.
|
14
|
-
raise "
|
13
|
+
def self.sso_config=(sso_config_hash)
|
14
|
+
raise "sso_config must be a Hash" unless sso_config_hash.is_a? Hash
|
15
15
|
|
16
|
-
missing_keys =
|
16
|
+
missing_keys = REQUIRED_SSO_CONFIG_KEYS - sso_config_hash.symbolize_keys.keys
|
17
17
|
if missing_keys.present?
|
18
|
-
raise "Missing required
|
18
|
+
raise "Missing required sso_config keys ['#{missing_keys.join("', '")}']"
|
19
19
|
end
|
20
20
|
|
21
|
-
@@
|
21
|
+
@@sso_config = SSOConfig.new(**sso_config_hash.symbolize_keys)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
REQUIRED_SSO_CONFIG_KEYS = %i[
|
25
25
|
audience
|
26
26
|
base_uri
|
27
27
|
client_id
|
@@ -30,11 +30,11 @@ module NulogySSO
|
|
30
30
|
login_uri
|
31
31
|
redirect_uri
|
32
32
|
]
|
33
|
-
private_constant :
|
33
|
+
private_constant :REQUIRED_SSO_CONFIG_KEYS
|
34
34
|
|
35
|
-
|
35
|
+
SSOConfig = ImmutableStruct.new(*REQUIRED_SSO_CONFIG_KEYS)
|
36
36
|
|
37
|
-
def self.
|
38
|
-
"#{
|
37
|
+
def self.sso_cookie_key
|
38
|
+
"#{sso_config.cookie_prefix}_access_token"
|
39
39
|
end
|
40
40
|
end
|
@@ -33,7 +33,7 @@ module Dummy
|
|
33
33
|
|
34
34
|
# Load required NulogySSO config so that the dummy can boot up without error.
|
35
35
|
# These functions are mostly used for testing.
|
36
|
-
NulogySSO.
|
36
|
+
NulogySSO.sso_config = config_for(:sso)
|
37
37
|
NulogySSO.find_user_by_email = ->(email) { User.find_by(email: email) }
|
38
38
|
NulogySSO.handle_sso_error = ->(controller) { controller.render plain: "An SSO error has occurred :(" }
|
39
39
|
end
|
File without changes
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
module NulogySSO
|
4
4
|
RSpec.describe Authenticator do
|
5
|
-
let(:
|
5
|
+
let(:sso_config) { NulogySSO.sso_config }
|
6
6
|
let(:verifier) do
|
7
7
|
MockAuth0Verifier.new(
|
8
|
-
issuer: "#{
|
9
|
-
audience:
|
8
|
+
issuer: "#{sso_config.base_uri}/",
|
9
|
+
audience: sso_config.audience,
|
10
10
|
jwks: jwt_test_helper.jwks_json
|
11
11
|
)
|
12
12
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nulogy_sso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nulogy Corporation
|
@@ -197,7 +197,6 @@ files:
|
|
197
197
|
- spec/dummy/bin/yarn
|
198
198
|
- spec/dummy/config.ru
|
199
199
|
- spec/dummy/config/application.rb
|
200
|
-
- spec/dummy/config/auth_sso.yml
|
201
200
|
- spec/dummy/config/boot.rb
|
202
201
|
- spec/dummy/config/cable.yml
|
203
202
|
- spec/dummy/config/database.yml
|
@@ -217,6 +216,7 @@ files:
|
|
217
216
|
- spec/dummy/config/puma.rb
|
218
217
|
- spec/dummy/config/routes.rb
|
219
218
|
- spec/dummy/config/spring.rb
|
219
|
+
- spec/dummy/config/sso.yml
|
220
220
|
- spec/dummy/config/storage.yml
|
221
221
|
- spec/dummy/db/migrate/20190912211120_create_users.rb
|
222
222
|
- spec/dummy/db/schema.rb
|
@@ -372,9 +372,9 @@ test_files:
|
|
372
372
|
- spec/dummy/bin/yarn
|
373
373
|
- spec/dummy/bin/rails
|
374
374
|
- spec/dummy/config/routes.rb
|
375
|
-
- spec/dummy/config/auth_sso.yml
|
376
375
|
- spec/dummy/config/locales/en.yml
|
377
376
|
- spec/dummy/config/cable.yml
|
377
|
+
- spec/dummy/config/sso.yml
|
378
378
|
- spec/dummy/config/environments/production.rb
|
379
379
|
- spec/dummy/config/environments/development.rb
|
380
380
|
- spec/dummy/config/environments/test.rb
|