epb-auth-tools 1.0.1
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 +7 -0
- data/lib/epb_auth_tools.rb +10 -0
- data/lib/errors.rb +36 -0
- data/lib/http_client.rb +76 -0
- data/lib/sinatra/conditional.rb +14 -0
- data/lib/token.rb +41 -0
- data/lib/token_processor.rb +42 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 782e5a8d8575cc614987e76c1475c6e3bb21126a5f1d69d35534151d9812a861
|
4
|
+
data.tar.gz: 3cb5ed246374496bc8170503bc77c0557c80cb7b6d8f44e57c09548555b9ef6b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dc06e83c6313dccbae073da2adbc20d694279b7a99ae7e4232b4dad54ca9fa07411e6bc78cfa9c23a3de78ec8285df89da31d7b3c585eafc5312bc44fe7ef85e
|
7
|
+
data.tar.gz: e9899d2035a1955efb170ad2379207d9b3fdd69126f8f7a2d5655719cdb712a163987756332c7172e84234f2cde1c59957b82f210659ebb3d741aea640e1718a
|
data/lib/errors.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auth
|
4
|
+
module Errors
|
5
|
+
class Error < RuntimeError; end
|
6
|
+
|
7
|
+
class Processor < Auth::Errors::Error; end
|
8
|
+
class ProcessorHasNoSecret < Auth::Errors::Error; end
|
9
|
+
class ProcessorHasNoIssuer < Auth::Errors::Error; end
|
10
|
+
|
11
|
+
class Token < Auth::Errors::Error; end
|
12
|
+
|
13
|
+
class TokenMissing < Auth::Errors::Token; end
|
14
|
+
class TokenPayloadError < Auth::Errors::Token; end
|
15
|
+
class TokenExpired < Auth::Errors::TokenPayloadError; end
|
16
|
+
class TokenNotYetValid < Auth::Errors::TokenPayloadError; end
|
17
|
+
class TokenHasNoIssuer < Auth::Errors::TokenPayloadError; end
|
18
|
+
class TokenHasNoSubject < Auth::Errors::TokenPayloadError; end
|
19
|
+
class TokenHasNoIssuedAt < Auth::Errors::TokenPayloadError; end
|
20
|
+
class TokenHasNoExpiry < Auth::Errors::TokenPayloadError; end
|
21
|
+
class TokenIssuerIncorrect < Auth::Errors::TokenPayloadError; end
|
22
|
+
|
23
|
+
class TokenDecodeError < Auth::Errors::Token; end
|
24
|
+
class TokenTamperDetected < Auth::Errors::TokenDecodeError; end
|
25
|
+
|
26
|
+
class Client < Auth::Errors::Error; end
|
27
|
+
|
28
|
+
class ClientHasNoAuthServer < Auth::Errors::Client; end
|
29
|
+
class ClientHasNoClientId < Auth::Errors::Client; end
|
30
|
+
class ClientHasNoClientSecret < Auth::Errors::Client; end
|
31
|
+
class ClientHasNoBaseUri < Auth::Errors::Client; end
|
32
|
+
|
33
|
+
class Network < Auth::Errors::Error; end
|
34
|
+
class NetworkConnectionFailed < Auth::Errors::Network; end
|
35
|
+
end
|
36
|
+
end
|
data/lib/http_client.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'oauth2'
|
4
|
+
|
5
|
+
module Auth
|
6
|
+
class HttpClient
|
7
|
+
attr_reader :authenticated_client
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
client_id = nil,
|
11
|
+
client_secret = nil,
|
12
|
+
auth_server = nil,
|
13
|
+
base_uri = nil,
|
14
|
+
auth_client = OAuth2::Client
|
15
|
+
)
|
16
|
+
raise Auth::Errors::ClientHasNoClientId if client_id.nil?
|
17
|
+
raise Auth::Errors::ClientHasNoClientSecret if client_secret.nil?
|
18
|
+
raise Auth::Errors::ClientHasNoAuthServer if auth_server.nil?
|
19
|
+
raise Auth::Errors::ClientHasNoBaseUri if base_uri.nil?
|
20
|
+
|
21
|
+
@authenticated_client = nil
|
22
|
+
|
23
|
+
site_url = URI.parse(auth_server)
|
24
|
+
token_url = site_url.path + '/oauth/token'
|
25
|
+
authorisation_url = site_url.path + '/oauth/token'
|
26
|
+
site_url = "#{site_url.scheme}://#{site_url.host}:#{site_url.port}"
|
27
|
+
|
28
|
+
|
29
|
+
@base_uri = base_uri
|
30
|
+
@client =
|
31
|
+
auth_client.new client_id,
|
32
|
+
client_secret,
|
33
|
+
site: site_url,
|
34
|
+
token_url: token_url,
|
35
|
+
authorisation_url: authorisation_url,
|
36
|
+
raise_errors: false
|
37
|
+
end
|
38
|
+
|
39
|
+
def refresh
|
40
|
+
@authenticated_client = @client.client_credentials.get_token
|
41
|
+
end
|
42
|
+
|
43
|
+
def refresh?
|
44
|
+
@authenticated_client.nil? || @authenticated_client.expired?
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.delegate(*methods)
|
48
|
+
methods.each do |method_name|
|
49
|
+
define_method(method_name) do |*args, &block|
|
50
|
+
request method_name, *args, &block
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
delegate :get, :post, :put
|
56
|
+
|
57
|
+
def request(method_name, *args, &block)
|
58
|
+
refresh? && refresh
|
59
|
+
|
60
|
+
args[0] = @base_uri + args[0]
|
61
|
+
|
62
|
+
if @authenticated_client.respond_to? method_name
|
63
|
+
response = @authenticated_client.send method_name, *args, &block
|
64
|
+
if response.body.is_a?(::Hash) &&
|
65
|
+
response.body[:error] == 'Auth::Errors::TokenExpired'
|
66
|
+
refresh
|
67
|
+
response = @authenticated_client.send method_name, *args, &block
|
68
|
+
end
|
69
|
+
|
70
|
+
response
|
71
|
+
end
|
72
|
+
rescue Faraday::ConnectionFailed
|
73
|
+
raise Auth::Errors::NetworkConnectionFailed
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auth
|
4
|
+
module Sinatra
|
5
|
+
class Conditional
|
6
|
+
def self.process_request(env)
|
7
|
+
jwt_token = env.fetch('HTTP_AUTHORIZATION', '').slice(7..-1)
|
8
|
+
processor =
|
9
|
+
Auth::TokenProcessor.new ENV['JWT_SECRET'], ENV['JWT_ISSUER']
|
10
|
+
processor.process jwt_token
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/token.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auth
|
4
|
+
class Token
|
5
|
+
def initialize(payload)
|
6
|
+
@payload = JSON.parse payload.to_json
|
7
|
+
validate_payload
|
8
|
+
end
|
9
|
+
|
10
|
+
def scope?(scope)
|
11
|
+
@payload['scopes'].include? scope
|
12
|
+
end
|
13
|
+
|
14
|
+
def scopes?(scopes)
|
15
|
+
scopes.all? { |scope| @payload['scopes'].include? scope }
|
16
|
+
end
|
17
|
+
|
18
|
+
def supplemental(property = nil)
|
19
|
+
unless property.nil? || @payload['sup'][property].nil?
|
20
|
+
return @payload['sup'][property]
|
21
|
+
end
|
22
|
+
|
23
|
+
@payload['sup']
|
24
|
+
end
|
25
|
+
|
26
|
+
def encode(jwt_secret)
|
27
|
+
JWT.encode @payload, jwt_secret, 'HS256'
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def validate_payload
|
33
|
+
raise Auth::Errors::TokenHasNoIssuer unless @payload.key?('iss')
|
34
|
+
raise Auth::Errors::TokenHasNoIssuedAt unless @payload.key?('iat')
|
35
|
+
unless @payload['iat'] <= Time.now.to_i
|
36
|
+
raise Auth::Errors::TokenNotYetValid
|
37
|
+
end
|
38
|
+
raise Auth::Errors::TokenHasNoSubject unless @payload.key?('sub')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jwt'
|
4
|
+
|
5
|
+
module Auth
|
6
|
+
class TokenProcessor
|
7
|
+
def initialize(jwt_secret = nil, jwt_issuer = nil)
|
8
|
+
raise Auth::Errors::ProcessorHasNoSecret if jwt_secret.nil?
|
9
|
+
raise Auth::Errors::ProcessorHasNoIssuer if jwt_issuer.nil?
|
10
|
+
|
11
|
+
@jwt_secret = jwt_secret
|
12
|
+
@jwt_issuer = jwt_issuer
|
13
|
+
end
|
14
|
+
|
15
|
+
def process(token = nil)
|
16
|
+
raise Auth::Errors::TokenMissing if token.nil?
|
17
|
+
|
18
|
+
payload, _header = jwt_process token
|
19
|
+
|
20
|
+
raise Auth::Errors::TokenHasNoIssuer unless payload.key?('iss')
|
21
|
+
unless payload['iss'] == @jwt_issuer
|
22
|
+
raise Auth::Errors::TokenIssuerIncorrect
|
23
|
+
end
|
24
|
+
|
25
|
+
Auth::Token.new payload
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def jwt_process(token)
|
31
|
+
options = { algorithm: 'HS256', iss: @jwt_issuer }
|
32
|
+
|
33
|
+
JWT.decode token, @jwt_secret, true, options
|
34
|
+
rescue JWT::ExpiredSignature
|
35
|
+
raise Auth::Errors::TokenExpired
|
36
|
+
rescue JWT::VerificationError
|
37
|
+
raise Auth::Errors::TokenTamperDetected
|
38
|
+
rescue JWT::DecodeError
|
39
|
+
raise Auth::Errors::TokenDecodeError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: epb-auth-tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lawrence Goldstien <lawrence.goldstien@madetech.com>
|
8
|
+
- Yusuf Sheikh <yusuf@madetech.com>
|
9
|
+
- Jaseera <jaseera@madetech.com>
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2020-03-11 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: jwt
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '2.2'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: oauth2
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.4'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.4'
|
43
|
+
description:
|
44
|
+
email:
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- lib/epb_auth_tools.rb
|
50
|
+
- lib/errors.rb
|
51
|
+
- lib/http_client.rb
|
52
|
+
- lib/sinatra/conditional.rb
|
53
|
+
- lib/token.rb
|
54
|
+
- lib/token_processor.rb
|
55
|
+
homepage: https://github.com/communitiesuk/epb-auth-tools
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.0.6
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Tools for authentication and authorisation with JWTs and OAuth
|
78
|
+
test_files: []
|