minato-rails-auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f230a5cc5a2ed2896b958555a3d1df46afdb1ff882d7a781b98415e91c47bf31
4
+ data.tar.gz: c0cda43ffd8b1385efb1fcfba78fd04dece987e19c9882b3b82b9e75b9096fa3
5
+ SHA512:
6
+ metadata.gz: 889f2d69ba35ebdea6e1ff24f2d278977ba907b294678955afc2af748092716c361311914ef2f8e4617e0846d09f405a1675832da9c222ff6989dc3bf78f2da8
7
+ data.tar.gz: aaeb0db38de916e67427c617325ff610020155ec4a659ccb6de393f79a2fc4d11d81151efb53bc418f27922a7b15fa8391878982f639910162b8a78b30f3f9ff
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,40 @@
1
+ require:
2
+ - rubocop-rails
3
+ - rubocop-rspec
4
+ - rubocop-rake
5
+
6
+ AllCops:
7
+ DisplayCopNames: true
8
+ DisplayStyleGuide: true
9
+ ExtraDetails: false
10
+ NewCops: enable
11
+ TargetRubyVersion: 2.7.3
12
+
13
+ Metrics/BlockLength:
14
+ AllowedMethods: ['describe', 'it', 'context']
15
+ Max: 50
16
+
17
+ RSpec/EmptyExampleGroup:
18
+ Enabled: false
19
+
20
+ RSpec/ExampleLength:
21
+ Max: 50
22
+
23
+ RSpec/ImplicitExpect:
24
+ Enabled: false
25
+
26
+ RSpec/MultipleExpectations:
27
+ Enabled: false
28
+
29
+ RSpec/VerifiedDoubleReference:
30
+ EnforcedStyle: string
31
+
32
+ Style/Documentation:
33
+ Enabled: false
34
+
35
+ Layout/HashAlignment:
36
+ EnforcedColonStyle: table
37
+ EnforcedHashRocketStyle: table
38
+
39
+ RSpec/MultipleMemoizedHelpers:
40
+ Max: 7
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Minato::Rails::Auth
2
+
3
+ Minato Rails Auth is a library designed to be an authentication layer in Rails applications. It has a JWT module, that can be included in Rails Controllers to authenticate requests. And has a RSpec shared contexts, to simplify authentication in automated tests.
4
+
5
+ ## Installation
6
+
7
+ The recommended installation way is by [Gem](https://guides.rubygems.org/rubygems-basics/#installing-gems) or [Bundler](https://bundler.io/):
8
+
9
+ ### By Gem
10
+
11
+ $ gem install minato-rails-auth
12
+
13
+ ### By Bundler
14
+
15
+ $ bundle add minato-rails-auth
16
+
17
+ Or just add the following line on your `Gemfile` file:
18
+
19
+ gem 'minato-rails-auth'
20
+
21
+ And you can specify the version:
22
+
23
+ gem 'minato-rails-auth', '~> 0.1.0'
24
+
25
+ ## Configuration
26
+
27
+ To configure the Minato Rails Auth lib on your Rails application, is necessary to follow the steps:
28
+
29
+ 1. Create a configuration file:
30
+
31
+ [rails-app]/config/initializers/minato_rails_auth.rb
32
+
33
+ 2. Add the following content in the file:
34
+
35
+ ```ruby
36
+ require 'minato/rails/auth'
37
+
38
+ Minato::Rails::Auth.configure do |conf|
39
+ conf.jwt.issuer = Settings.api.secure.issuer
40
+ conf.jwt.jwks_uri = Settings.api.secure.jwks_uri
41
+ end
42
+ ```
43
+
44
+ - Each option specs:
45
+
46
+ - `conf.jwt`
47
+ - stores all jwt configuration
48
+ - `conf.jwt.issuer`
49
+ - example: 'http://localhost'
50
+ - is the jwt issuer, when the system uses Ory, it's the gateway host
51
+ - `conf.jwt.jwks_uri`
52
+ - example: 'http://localhost:4456/.well-known/jwks.json'
53
+ - is the jwks obtetion uri, when the system uses Ory, it's the direct gateway host
54
+ - `conf.jwt.jwks`
55
+ - default: nil
56
+ - this is the jwks data, if this is specified it will not be necessary the conf.jwt.jwks_uri attribute
57
+ - `conf.jwt.algorithms`
58
+ - default: 'RS256'
59
+ - alghorithm used to decode the jwt
60
+ - `conf.jwt.verify_iss`
61
+ - default: true
62
+ - this specify if the issuer should be checked
63
+ - `conf.jwt.verify_token_signature`
64
+ - default: true
65
+ - this specify if the token signature should be checked
66
+ - `conf.jwt.hmac_secret`
67
+ - default: nil
68
+ - hmac secret, is necessary only in HMAC algorithms
69
+ - `conf.jwt.additional_params`
70
+ - default: {}
71
+ - is JWT.decode additional options
72
+ - docs: https://github.com/jwt/ruby-jwt/blob/main/README.md
73
+
74
+ And now, the library was configured in your Rails application.
75
+
76
+ ## Usage
77
+
78
+ ### Auth system
79
+
80
+ To add the JWT authentication system in your Rails application, just follow the steps:
81
+
82
+ 1. Include the JWT module in your `ApplicationController`:
83
+
84
+ ```ruby
85
+ class ApplicationController < ActionController::API
86
+ include Minato::Rails::Auth::JWT
87
+ end
88
+ ```
89
+
90
+ 2. Now it is available the method `authenticate_request!` in your controllers, and you can call it when you want to auth a controller:
91
+
92
+ ```ruby
93
+ class FooController < ApplicationController
94
+ before_action :authenticate_request!
95
+ end
96
+ ```
97
+
98
+ 3. Now the variable `current_user` is availabe in your controller, containing the following methods:
99
+
100
+ - `.machine?`
101
+ - This will return a boolean value that will tell you if the client is a machine or not
102
+
103
+ - `.human?`
104
+ - This works like the `.machine?` method, but tells you if the client is human.
105
+
106
+ - `.session`
107
+ - This returns a hash with the JWT payload, if the client is human.
108
+
109
+ - `.machine`
110
+ - This returns a string with the machine name, when the client is a machine.
111
+
112
+ ### Testing tools
113
+
114
+ The Minato Rails Auth makes available the RSpec shared contexts, to make it available you can include the module in your `[rails-app]/spec/spec_helper.rb`:
115
+
116
+ ```ruby
117
+ require 'minato/rails/auth/spec'
118
+ ```
119
+
120
+ And now it will be available the following shared contexts, that you can use following [the docs](https://rspec.info/features/3-12/rspec-core/example-groups/shared-context/):
121
+
122
+ - `with jwt authentication`:
123
+ - Just create a jwt with empty payload. To set the payload you should create a `jwt_payload` variable using `let`.
124
+ - `with human jwt authentication`:
125
+ - Creates a jwt with human payload. You set the user attributes with variables `jwt_email`, `jwt_account_id`, `jwt_session_id`, `jwt_identity_id` and `jwt_payload`.
126
+ - `with machine jwt authentication`:
127
+ - Creates a jwt with machine payload. You can set the machine name with the variable `jwt_machine`.
128
+ - `with missing jwt authentication`:
129
+ - Raise a `::JWT::VerificationError`, simulating when the user is not authenticated.
130
+
131
+ ## Development
132
+
133
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
134
+
135
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
+
137
+ ## Contributing
138
+
139
+ Bug reports and pull requests are welcome on GitLab at https://gitlab.com/ferreri/minato/minato-rails-auth.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/compose.yml ADDED
@@ -0,0 +1,12 @@
1
+ services:
2
+ minato-rails-auth:
3
+ environment:
4
+ GITLAB_AUTH_TOKEN: ${GITLAB_AUTH_TOKEN}
5
+ image: ruby:2.7.3
6
+ working_dir: /usr/src/api
7
+ volumes:
8
+ - .:/usr/src/api:z
9
+ - bundle:/usr/local/bundle
10
+
11
+ volumes:
12
+ bundle:
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jwt/configuration'
4
+ require 'active_support'
5
+
6
+ module Minato
7
+ module Rails
8
+ module Auth
9
+ class Configuration
10
+ attr_accessor :jwt
11
+
12
+ def initialize(jwt = Auth::JWT::Configuration.new)
13
+ @jwt = jwt
14
+ end
15
+
16
+ def configure
17
+ yield(self) if block_given?
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'user'
4
+ require 'active_support'
5
+
6
+ module Minato
7
+ module Rails
8
+ module Auth
9
+ module JWT
10
+ class Configuration
11
+ attr_accessor :issuer, :jwks_uri, :verify_token_signature, :algorithms, :verify_iss, :hmac_secret,
12
+ :additional_params
13
+
14
+ def initialize
15
+ @verify_token_signature = true
16
+ @algorithms = 'RS256'
17
+ @verify_iss = true
18
+ @hmac_secret = nil
19
+ @decode_params = nil
20
+ @additional_params = {}
21
+ @jwks = nil
22
+ end
23
+
24
+ attr_writer :decode_params, :jwks
25
+
26
+ def decode_params
27
+ return @decode_params.merge(additional_params || {}) if @decode_params.present?
28
+
29
+ {
30
+ algorithms: algorithms,
31
+ iss: issuer,
32
+ verify_iss: verify_iss,
33
+ jwks: jwks
34
+ }.merge(additional_params || {})
35
+ end
36
+
37
+ def configure
38
+ yield(self) if block_given?
39
+ end
40
+
41
+ def jwks
42
+ return @jwks if @jwks.present?
43
+
44
+ jwks_raw = Net::HTTP.get URI(jwks_uri)
45
+ JSON.parse(jwks_raw, symbolize_names: true)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minato
4
+ module Rails
5
+ module Auth
6
+ module JWT
7
+ class Decode
8
+ def self.call(jwt)
9
+ ::JWT.decode(jwt,
10
+ Auth::CONFIG.jwt.hmac_secret,
11
+ Auth::CONFIG.jwt.verify_token_signature,
12
+ Auth::CONFIG.jwt.decode_params).first
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minato
4
+ module Rails
5
+ module Auth
6
+ module JWT
7
+ module Contexts
8
+ RSpec.shared_context 'with jwt authentication', shared_context: :metadata do
9
+ let(:jwt_payload) do
10
+ {}
11
+ end
12
+ let(:Authorization) { 'Bearer token' }
13
+ before do
14
+ allow(Decode).to receive(:call).and_return(jwt_payload)
15
+ end
16
+ end
17
+
18
+ RSpec.shared_context 'with human jwt authentication', shared_context: :metadata do
19
+ let(:jwt_email) { 'test@email.com' }
20
+ let(:jwt_account_id) { '555' }
21
+ let(:jwt_session_id) { '67cf621f-4f49-4ca5-9365-62520849170b' }
22
+ let(:jwt_identity_id) { 'adb1e688-17c0-4241-bc24-912fa6b5f6c3' }
23
+ let(:jwt_payload) do
24
+ {
25
+ 'session' => {
26
+ 'id' => jwt_session_id,
27
+ 'expires_at' => '2021-09-29T20:15:44.731576Z',
28
+ 'identity' => {
29
+ 'id' => jwt_identity_id,
30
+ 'traits' => {
31
+ 'email' => jwt_email
32
+ },
33
+ 'metadata_public' => {
34
+ 'account_id' => jwt_account_id
35
+ }
36
+ }
37
+ }
38
+ }
39
+ end
40
+ let(:Authorization) { 'Bearer token' }
41
+ before do
42
+ allow(Decode).to receive(:call).and_return(jwt_payload)
43
+ end
44
+ end
45
+
46
+ RSpec.shared_context 'with machine jwt authentication', shared_context: :metadata do
47
+ let(:jwt_machine) { 'foo-svc' }
48
+ let(:jwt_payload) do
49
+ {
50
+ 'sub' => jwt_machine
51
+ }
52
+ end
53
+ let(:Authorization) { 'Bearer token' }
54
+ before do
55
+ allow(Decode).to receive(:call).and_return(jwt_payload)
56
+ end
57
+ end
58
+
59
+ RSpec.shared_context 'with missing jwt authentication', shared_context: :metadata do
60
+ let(:Authorization) { nil }
61
+ before do
62
+ allow(Decode).to receive(:call).and_raise(::JWT::VerificationError)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'object_hash'
4
+
5
+ module Minato
6
+ module Rails
7
+ module Auth
8
+ module JWT
9
+ class User
10
+ attr_reader :token_payload
11
+
12
+ def initialize(token_payload)
13
+ @token_payload = ObjectHash.new(token_payload)
14
+ end
15
+
16
+ def human?
17
+ @human ||= @token_payload.respond_to?(:session)
18
+ end
19
+
20
+ def machine?
21
+ @machine ||= !human?
22
+ end
23
+
24
+ def session
25
+ @token_payload.session
26
+ end
27
+
28
+ def subject
29
+ @token_payload.sub
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jwt/user'
4
+ require_relative 'configuration'
5
+ require_relative 'jwt/decode'
6
+ require 'active_support'
7
+ require 'net/http'
8
+ require 'uri'
9
+ require 'jwt'
10
+
11
+ module Minato
12
+ module Rails
13
+ module Auth
14
+ module JWT
15
+ extend ActiveSupport::Concern
16
+
17
+ private
18
+
19
+ def authenticate_request!
20
+ @current_user = User.new(auth_token)
21
+ end
22
+
23
+ def http_token
24
+ return nil if request.headers['Authorization'].blank?
25
+
26
+ request.headers['Authorization'].split.last
27
+ end
28
+
29
+ def auth_token
30
+ @auth_token ||= Decode.call(http_token)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jwt/spec/contexts'
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minato
4
+ module Rails
5
+ module Auth
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'auth/version'
4
+ require_relative 'auth/jwt'
5
+
6
+ module Minato
7
+ module Rails
8
+ module Auth
9
+ CONFIG = Configuration.new
10
+ class << self
11
+ def configure
12
+ yield(CONFIG) if block_given?
13
+ CONFIG
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ObjectHash
4
+ def initialize(hash)
5
+ @hash = hash
6
+ end
7
+
8
+ def method_missing(name)
9
+ find_hash_value(name)
10
+ end
11
+
12
+ def respond_to_missing?(name, include_private = false)
13
+ return true if @hash.keys.include?(name.to_s)
14
+ return true if @hash.keys.include?(name.to_sym)
15
+
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def find_hash_value(name)
22
+ value = @hash[name.to_s] || @hash[name.to_sym]
23
+ return ObjectHash.new(value) if value.instance_of?(Hash)
24
+
25
+ if value.instance_of?(Array)
26
+ return value.map do |item|
27
+ next ObjectHash.new(item) if item.instance_of?(Hash)
28
+
29
+ item
30
+ end
31
+ end
32
+
33
+ value
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/minato/rails/auth/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'minato-rails-auth'
7
+ spec.version = Minato::Rails::Auth::VERSION
8
+ spec.authors = ['Ferreri']
9
+ spec.email = ['contato@ferreri.co']
10
+
11
+ spec.summary = 'Minato Rails Auth'
12
+ spec.description = 'Library to create an authentication layer in Rails applications'
13
+ spec.homepage = 'https://gitlab.com/ferreri/minato/minato-rails-auth'
14
+ spec.required_ruby_version = '>= 2.7'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = spec.homepage
18
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/ferreri/minato/minato-rails-auth/-/commits/main?ref_type=heads'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (File.expand_path(f) == __FILE__) ||
25
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
26
+ end
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.add_dependency 'activesupport', '~> 7.0.4'
33
+ spec.add_dependency 'jwt', '~> 2.9'
34
+
35
+ # Uncomment to register a new dependency of your gem
36
+ # spec.add_dependency "example-gem", "~> 1.0"
37
+
38
+ # For more information and examples about making a new gem, check out our
39
+ # guide at: https://bundler.io/guides/creating_gem.html
40
+ spec.metadata['rubygems_mfa_required'] = 'true'
41
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minato-rails-auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ferreri
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: jwt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.9'
41
+ description: Library to create an authentication layer in Rails applications
42
+ email:
43
+ - contato@ferreri.co
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".rspec"
49
+ - ".rubocop.yml"
50
+ - README.md
51
+ - Rakefile
52
+ - compose.yml
53
+ - lib/minato/rails/auth.rb
54
+ - lib/minato/rails/auth/configuration.rb
55
+ - lib/minato/rails/auth/jwt.rb
56
+ - lib/minato/rails/auth/jwt/configuration.rb
57
+ - lib/minato/rails/auth/jwt/decode.rb
58
+ - lib/minato/rails/auth/jwt/spec/contexts.rb
59
+ - lib/minato/rails/auth/jwt/user.rb
60
+ - lib/minato/rails/auth/spec.rb
61
+ - lib/minato/rails/auth/version.rb
62
+ - lib/object_hash.rb
63
+ - minato-rails-auth.gemspec
64
+ homepage: https://gitlab.com/ferreri/minato/minato-rails-auth
65
+ licenses: []
66
+ metadata:
67
+ homepage_uri: https://gitlab.com/ferreri/minato/minato-rails-auth
68
+ source_code_uri: https://gitlab.com/ferreri/minato/minato-rails-auth
69
+ changelog_uri: https://gitlab.com/ferreri/minato/minato-rails-auth/-/commits/main?ref_type=heads
70
+ rubygems_mfa_required: 'true'
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '2.7'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubygems_version: 3.1.6
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Minato Rails Auth
90
+ test_files: []