authproto 0.3.0.beta1
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/.rspec +1 -0
- data/.rubocop.yml +31 -0
- data/.tm_properties +1 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +52 -0
- data/MIT-LICENSE.txt +21 -0
- data/README.md +304 -0
- data/Rakefile +8 -0
- data/SECURITY.md +16 -0
- data/examples/confidential_client/.gitignore +5 -0
- data/examples/confidential_client/Gemfile +14 -0
- data/examples/confidential_client/Gemfile.lock +88 -0
- data/examples/confidential_client/README.md +226 -0
- data/examples/confidential_client/app.rb +290 -0
- data/examples/confidential_client/config/client-metadata.example.json +25 -0
- data/examples/confidential_client/config.ru +4 -0
- data/examples/confidential_client/public/styles.css +70 -0
- data/examples/confidential_client/screenshots/screenshot-1-sign-in.png +0 -0
- data/examples/confidential_client/screenshots/screenshot-2-success.png +0 -0
- data/examples/confidential_client/scripts/generate_keys.rb +15 -0
- data/examples/confidential_client/views/authorized.erb +46 -0
- data/examples/confidential_client/views/index.erb +44 -0
- data/examples/confidential_client/views/layout.erb +11 -0
- data/examples/minisky_console/.gitignore +2 -0
- data/examples/minisky_console/Gemfile +12 -0
- data/examples/minisky_console/Gemfile.lock +131 -0
- data/examples/minisky_console/README.md +112 -0
- data/examples/minisky_console/app.rb +50 -0
- data/examples/minisky_console/bin/console +6 -0
- data/examples/minisky_console/config.ru +5 -0
- data/examples/minisky_console/localhost.rb +53 -0
- data/examples/minisky_console/production.rb +32 -0
- data/lib/authproto/client.rb +583 -0
- data/lib/authproto/client_metadata.rb +478 -0
- data/lib/authproto/configuration.rb +139 -0
- data/lib/authproto/dpop/client.rb +161 -0
- data/lib/authproto/dpop/key_manager.rb +223 -0
- data/lib/authproto/dpop/nonce_manager.rb +145 -0
- data/lib/authproto/dpop/proof_generator.rb +124 -0
- data/lib/authproto/dpop/stored_nonce.rb +37 -0
- data/lib/authproto/encryption.rb +250 -0
- data/lib/authproto/errors.rb +75 -0
- data/lib/authproto/http_client.rb +320 -0
- data/lib/authproto/insecure_http_client.rb +24 -0
- data/lib/authproto/localhost_server.rb +274 -0
- data/lib/authproto/minisky.rb +273 -0
- data/lib/authproto/par/client.rb +240 -0
- data/lib/authproto/par/client_assertion.rb +69 -0
- data/lib/authproto/par/request.rb +243 -0
- data/lib/authproto/par/response.rb +43 -0
- data/lib/authproto/par.rb +48 -0
- data/lib/authproto/pkce.rb +125 -0
- data/lib/authproto/serialization/base.rb +258 -0
- data/lib/authproto/serialization/dpop_key.rb +38 -0
- data/lib/authproto/serialization/session.rb +88 -0
- data/lib/authproto/serialization/stored_nonce.rb +46 -0
- data/lib/authproto/serialization/token_set.rb +54 -0
- data/lib/authproto/server_metadata/authorization_server.rb +267 -0
- data/lib/authproto/server_metadata/origin_url.rb +71 -0
- data/lib/authproto/server_metadata/resource_server.rb +98 -0
- data/lib/authproto/server_metadata.rb +28 -0
- data/lib/authproto/state/session.rb +168 -0
- data/lib/authproto/state/session_manager.rb +162 -0
- data/lib/authproto/state/token_set.rb +101 -0
- data/lib/authproto/state.rb +61 -0
- data/lib/authproto/storage/interface.rb +121 -0
- data/lib/authproto/storage/key_builder.rb +76 -0
- data/lib/authproto/storage/memory.rb +222 -0
- data/lib/authproto/storage/redis.rb +156 -0
- data/lib/authproto/storage/sqlite.rb +281 -0
- data/lib/authproto/token/refresh.rb +414 -0
- data/lib/authproto/version.rb +8 -0
- data/lib/authproto.rb +18 -0
- data/sig/authproto/client.rbs +43 -0
- data/sig/authproto/client_metadata.rbs +113 -0
- data/sig/authproto/dependencies.rbs +19 -0
- data/sig/authproto/dpop/client.rbs +23 -0
- data/sig/authproto/dpop/key_manager.rbs +26 -0
- data/sig/authproto/dpop/nonce_manager.rbs +23 -0
- data/sig/authproto/dpop/proof_generator.rbs +40 -0
- data/sig/authproto/dpop/stored_nonce.rbs +10 -0
- data/sig/authproto/encryption.rbs +42 -0
- data/sig/authproto/http_client.rbs +45 -0
- data/sig/authproto/insecure_http_client.rbs +8 -0
- data/sig/authproto/localhost_server.rbs +30 -0
- data/sig/authproto/minisky_oauth.rbs +22 -0
- data/sig/authproto/par/client.rbs +25 -0
- data/sig/authproto/par/client_assertion.rbs +10 -0
- data/sig/authproto/par/request.rbs +73 -0
- data/sig/authproto/par/response.rbs +17 -0
- data/sig/authproto/par.rbs +7 -0
- data/sig/authproto/pkce.rbs +24 -0
- data/sig/authproto/serialization/base.rbs +36 -0
- data/sig/authproto/serialization/dpop_key.rbs +14 -0
- data/sig/authproto/serialization/session.rbs +19 -0
- data/sig/authproto/serialization/stored_nonce.rbs +14 -0
- data/sig/authproto/serialization/token_set.rbs +14 -0
- data/sig/authproto/server_metadata/authorization_server.rbs +71 -0
- data/sig/authproto/server_metadata/origin_url.rbs +21 -0
- data/sig/authproto/server_metadata/resource_server.rbs +27 -0
- data/sig/authproto/state/session.rbs +28 -0
- data/sig/authproto/state/session_manager.rbs +15 -0
- data/sig/authproto/state/token_set.rbs +40 -0
- data/sig/authproto/state.rbs +6 -0
- data/sig/authproto/storage/interface.rbs +20 -0
- data/sig/authproto/storage/key_builder.rbs +16 -0
- data/sig/authproto/storage/memory.rbs +22 -0
- data/sig/authproto/storage/redis.rbs +19 -0
- data/sig/authproto/storage/redis_client.rbs +11 -0
- data/sig/authproto/storage/sqlite.rbs +26 -0
- data/sig/authproto/token/refresh.rbs +47 -0
- data/sig/authproto/version.rbs +3 -0
- data/sig/authproto.rbs +54 -0
- metadata +200 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module DPoP
|
|
3
|
+
class ProofGenerator
|
|
4
|
+
@key_manager: AuthProto::DPoP::KeyManager
|
|
5
|
+
|
|
6
|
+
class ProofError < AuthProto::Error
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader key_manager: AuthProto::DPoP::KeyManager
|
|
10
|
+
|
|
11
|
+
def initialize: (AuthProto::DPoP::KeyManager key_manager) -> void
|
|
12
|
+
|
|
13
|
+
def generate: (
|
|
14
|
+
http_method: String,
|
|
15
|
+
http_uri: String,
|
|
16
|
+
?nonce: String?,
|
|
17
|
+
?access_token: String?,
|
|
18
|
+
?ath: bool?
|
|
19
|
+
) -> JOSE::SignedBinary
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def validate_inputs: (String http_method, String http_uri) -> void
|
|
24
|
+
|
|
25
|
+
def build_header: () -> { typ: "dpop+jwt", alg: "ES256", jwk: Hash[String, untyped] }
|
|
26
|
+
|
|
27
|
+
def build_payload: (
|
|
28
|
+
http_method: String,
|
|
29
|
+
http_uri: String,
|
|
30
|
+
?nonce: String?,
|
|
31
|
+
?access_token: String?,
|
|
32
|
+
?include_ath: bool?
|
|
33
|
+
) -> Hash[String, untyped]
|
|
34
|
+
|
|
35
|
+
def normalize_uri: (String uri) -> String
|
|
36
|
+
|
|
37
|
+
def generate_access_token_hash: (String access_token) -> String
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module DPoP
|
|
3
|
+
class StoredNonce
|
|
4
|
+
attr_reader value: String
|
|
5
|
+
attr_reader server_url: String
|
|
6
|
+
attr_reader timestamp: Integer | Time
|
|
7
|
+
def initialize: (String value, String server_url, ?timestamp: (Integer | Time)?) -> void
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module Encryption
|
|
3
|
+
type envelope = { version: Integer, iv: String, data: String, tag: String }
|
|
4
|
+
|
|
5
|
+
class Error < AuthProto::Error
|
|
6
|
+
end
|
|
7
|
+
class ConfigurationError < Error
|
|
8
|
+
end
|
|
9
|
+
class EncryptionError < Error
|
|
10
|
+
end
|
|
11
|
+
class DecryptionError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module HKDF
|
|
15
|
+
def self.derive: (_ToS secret, salt: String, info: String, length: Integer) -> String
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Service
|
|
19
|
+
CIPHER: String
|
|
20
|
+
VERSION: Integer
|
|
21
|
+
def initialize: () -> void
|
|
22
|
+
def encrypt: (_ToS data, context: String) -> envelope
|
|
23
|
+
def decrypt: (envelope encrypted, context: String) -> String
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
def validate_encryption_inputs: (untyped data, untyped context) -> void
|
|
27
|
+
def validate_decryption_inputs: (untyped encrypted, untyped context) -> void
|
|
28
|
+
def validate_encrypted_data: (untyped encrypted) -> void
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class KeyProvider
|
|
32
|
+
EPHEMERAL_KEY_MUTEX: Mutex
|
|
33
|
+
def self.ephemeral_master_key: () -> String
|
|
34
|
+
def initialize: () -> void
|
|
35
|
+
def key_for_context: (String context) -> String
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def load_master_key: () -> String
|
|
39
|
+
def salt_for_context: (String context) -> String
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
class HTTPClient
|
|
3
|
+
FORBIDDEN_IP_RANGES: Array[IPAddr]
|
|
4
|
+
ALLOWED_SCHEMES: Array[String]
|
|
5
|
+
DEFAULT_TIMEOUT: Integer
|
|
6
|
+
MAX_REDIRECTS: Integer
|
|
7
|
+
MAX_RESPONSE_SIZE: Integer
|
|
8
|
+
|
|
9
|
+
class SSRFError < AuthProto::Error
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class HTTPError < AuthProto::Error
|
|
13
|
+
attr_reader response: Net::HTTPResponse?
|
|
14
|
+
def initialize: (String message, Net::HTTPResponse? response) -> void
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class RedirectHandlerOptions < Data
|
|
18
|
+
attr_reader original_uri: URI::HTTP
|
|
19
|
+
attr_reader method: Symbol
|
|
20
|
+
attr_reader response: Net::HTTPResponse
|
|
21
|
+
attr_reader headers: Hash[String, String]
|
|
22
|
+
attr_reader redirect_count: Integer
|
|
23
|
+
attr_reader body: http_body
|
|
24
|
+
def self.new: (URI::HTTP original_uri, Symbol method, Net::HTTPResponse response, Hash[String, String] headers, Integer redirect_count, http_body body) -> instance
|
|
25
|
+
| (original_uri: URI::HTTP, method: Symbol, response: Net::HTTPResponse, headers: Hash[String, String], redirect_count: Integer, body: http_body) -> instance
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize: (?timeout: Numeric, ?verify_ssl: bool) -> void
|
|
29
|
+
def get: (String url, ?Hash[String, String] headers, ?follow_redirects: bool) -> http_response
|
|
30
|
+
def post: (String url, ?body: http_body, ?headers: Hash[String, String]) -> http_response
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
def validate_uri: (String url) -> URI::HTTP
|
|
34
|
+
def validate_ip: (URI::HTTP uri) -> void
|
|
35
|
+
def forbidden_ip?: (IPAddr ip) -> bool
|
|
36
|
+
def make_request: (URI::HTTP uri, ?Hash[String, String] headers, ?Integer redirect_count, ?follow_redirects: bool) -> Net::HTTPResponse
|
|
37
|
+
def make_post_request: (URI::HTTP uri, http_body body, ?Hash[String, String] headers, ?Integer redirect_count) -> Net::HTTPResponse
|
|
38
|
+
def configure_http_client: (Net::HTTP http, ?ssl: bool) -> void
|
|
39
|
+
def add_security_headers: (Net::HTTPRequest request, Hash[String, String] headers) -> void
|
|
40
|
+
def handle_redirect: (original_uri: URI::HTTP, response: Net::HTTPResponse, headers: Hash[String, String], redirect_count: Integer, ?method: Symbol, ?body: http_body) -> Net::HTTPResponse
|
|
41
|
+
def validate_response: (Net::HTTPResponse response) -> void
|
|
42
|
+
def check_success_status: (Net::HTTPResponse response) -> void
|
|
43
|
+
def check_content_length: (Net::HTTPResponse response) -> void
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
class LocalhostServer
|
|
3
|
+
type metadata = Hash[String | Symbol, untyped]
|
|
4
|
+
type callback_params = { code: String, state: String, iss: String }
|
|
5
|
+
|
|
6
|
+
MAX_HEADER_BYTES: Integer
|
|
7
|
+
REQUEST_TIMEOUT: Integer
|
|
8
|
+
LOOPBACK_RANGES: Array[IPAddr]
|
|
9
|
+
|
|
10
|
+
class CallbackError < StandardError
|
|
11
|
+
attr_reader error_code: String
|
|
12
|
+
attr_reader error_description: String?
|
|
13
|
+
def initialize: (String error_code, String? error_description) -> void
|
|
14
|
+
end
|
|
15
|
+
class InvalidRequest < StandardError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.start: [T] (metadata metadata, ?timeout: Numeric) { (callback_params) -> T } -> T
|
|
19
|
+
def initialize: (metadata metadata) -> void
|
|
20
|
+
def start: [T] (?timeout: Numeric) { (callback_params) -> T } -> T
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def loopback_host?: (String? host) -> bool
|
|
24
|
+
def handle_connection: [T] (TCPSocket socket) { (callback_params) -> T } -> [bool, T?]
|
|
25
|
+
def handle_request: (TCPSocket socket) -> Hash[String, String]?
|
|
26
|
+
def read_request: (TCPSocket socket) -> [String, String]
|
|
27
|
+
def extract_callback_params: (URI::Generic uri) -> Hash[String, String]
|
|
28
|
+
def respond: (TCPSocket socket, Integer status, String reason, String body) -> void
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Signatures for the methods defined by the optional Minisky adapter.
|
|
2
|
+
# Additional methods mixed in from Minisky::Requests belong to the minisky gem.
|
|
3
|
+
class MiniskyOAuth
|
|
4
|
+
MAX_NONCE_RETRIES: Integer
|
|
5
|
+
attr_reader host: String
|
|
6
|
+
attr_reader oauth_client: AuthProto::Client
|
|
7
|
+
attr_reader session_id: String?
|
|
8
|
+
|
|
9
|
+
def initialize: (String host, client_id: String, redirect_uri: String, ?metadata: AuthProto::json_object?, ?dpop_key: (AuthProto::json_object | JOSE::Map)?) -> void
|
|
10
|
+
def config: () -> nil
|
|
11
|
+
def authorize: (?handle: String?, ?pds_url: String?, ?scope: String) -> AuthProto::authorization_result
|
|
12
|
+
def handle_callback: (code: String, state: String, iss: String) -> AuthProto::callback_result
|
|
13
|
+
def get_request: (String | URI::HTTP method, ?Hash[untyped, untyped]? params, ?auth: bool, ?headers: Hash[String, String]?) -> untyped
|
|
14
|
+
def post_request: (String | URI::HTTP method, ?untyped data, ?auth: bool, ?headers: Hash[String, String]?, ?params: Hash[untyped, untyped]?) -> untyped
|
|
15
|
+
def log_in: (*untyped args) -> bot
|
|
16
|
+
def perform_token_refresh: () -> AuthProto::refreshed_tokens
|
|
17
|
+
def check_access: () -> (:ok | :refreshed)
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def oauth_request: (singleton(Net::HTTP::Get) | singleton(Net::HTTP::Post) request_class, URI::HTTP url, Hash[String, String] headers, ?body: String?) -> Net::HTTPResponse
|
|
21
|
+
def authentication_error: (Net::HTTPResponse response) -> String?
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module PAR
|
|
3
|
+
class Client
|
|
4
|
+
attr_reader endpoint: String
|
|
5
|
+
attr_reader dpop_client: DPoP::Client
|
|
6
|
+
attr_reader nonce_manager: DPoP::NonceManager
|
|
7
|
+
|
|
8
|
+
def initialize: (endpoint: String, dpop_client: DPoP::Client) -> void
|
|
9
|
+
def submit: (Request request) -> Response
|
|
10
|
+
def extract_nonce: (http_response response) -> String
|
|
11
|
+
def authorization_url: (authorize_endpoint: String, request_uri: String, client_id: String) -> String
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
def validate_endpoint: () -> void
|
|
15
|
+
def make_request: (Request request) -> http_response
|
|
16
|
+
def build_headers: (Request request, String dpop_proof) -> Hash[String, String]
|
|
17
|
+
def requires_nonce?: (http_response response) -> bool
|
|
18
|
+
def store_nonce: (String nonce) -> void
|
|
19
|
+
def server_origin: () -> String
|
|
20
|
+
def handle_error_response: (http_response response) -> bot
|
|
21
|
+
def process_response: (http_response response) -> Response
|
|
22
|
+
def encode_params: (Hash[String, String] params) -> String
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module PAR
|
|
3
|
+
class Request
|
|
4
|
+
@response_type: String
|
|
5
|
+
@client_id: String
|
|
6
|
+
@redirect_uri: String
|
|
7
|
+
@code_challenge: String
|
|
8
|
+
@code_challenge_method: String
|
|
9
|
+
@state: String
|
|
10
|
+
@scope: String
|
|
11
|
+
@login_hint: String?
|
|
12
|
+
@nonce: String?
|
|
13
|
+
@dpop_proof: String?
|
|
14
|
+
@client_assertion_type: String?
|
|
15
|
+
@client_assertion: String?
|
|
16
|
+
|
|
17
|
+
class Configuration
|
|
18
|
+
attr_accessor client_id: String?
|
|
19
|
+
attr_accessor redirect_uri: String?
|
|
20
|
+
attr_accessor code_challenge: String?
|
|
21
|
+
attr_accessor code_challenge_method: String?
|
|
22
|
+
attr_accessor state: String?
|
|
23
|
+
attr_accessor scope: String?
|
|
24
|
+
attr_accessor login_hint: String?
|
|
25
|
+
attr_accessor nonce: String?
|
|
26
|
+
attr_accessor dpop_proof: String?
|
|
27
|
+
attr_accessor client_assertion_type: String?
|
|
28
|
+
attr_accessor client_assertion: String?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_reader response_type: String
|
|
32
|
+
attr_reader client_id: String
|
|
33
|
+
attr_reader code_challenge: String
|
|
34
|
+
attr_reader code_challenge_method: String
|
|
35
|
+
attr_reader state: String
|
|
36
|
+
attr_reader redirect_uri: String
|
|
37
|
+
attr_reader scope: String
|
|
38
|
+
attr_reader login_hint: String?
|
|
39
|
+
attr_reader nonce: String?
|
|
40
|
+
attr_reader dpop_proof: String?
|
|
41
|
+
attr_reader client_assertion_type: String?
|
|
42
|
+
attr_reader client_assertion: String?
|
|
43
|
+
|
|
44
|
+
def self.build: () { (Configuration) -> untyped } -> Request
|
|
45
|
+
|
|
46
|
+
def initialize: (Configuration config) -> void
|
|
47
|
+
|
|
48
|
+
def to_form: () -> String
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def build_params: () -> Hash[String, String]
|
|
53
|
+
|
|
54
|
+
def add_optional_params: (Hash[String, String] params) -> void
|
|
55
|
+
|
|
56
|
+
def add_client_auth_params: (Hash[String, String] params) -> void
|
|
57
|
+
|
|
58
|
+
def validate_request: () -> void
|
|
59
|
+
|
|
60
|
+
def validate_required_params: () -> void
|
|
61
|
+
|
|
62
|
+
def validate_response_type: () -> void
|
|
63
|
+
|
|
64
|
+
def validate_code_challenge_method: () -> void
|
|
65
|
+
|
|
66
|
+
def validate_scope: () -> void
|
|
67
|
+
|
|
68
|
+
def validate_client_auth: () -> void
|
|
69
|
+
|
|
70
|
+
def encode_params: (Hash[String, String]) -> String
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module PAR
|
|
3
|
+
class Response
|
|
4
|
+
@request_uri: String
|
|
5
|
+
@expires_in: Integer
|
|
6
|
+
|
|
7
|
+
attr_reader request_uri: String
|
|
8
|
+
attr_reader expires_in: Integer
|
|
9
|
+
|
|
10
|
+
def initialize: (request_uri: String, expires_in: Integer | String) -> void
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def validate_response: () -> void
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module PKCE
|
|
3
|
+
class Error < AuthProto::Error
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
MIN_VERIFIER_LENGTH: Integer
|
|
7
|
+
MAX_VERIFIER_LENGTH: Integer
|
|
8
|
+
ALLOWED_VERIFIER_CHARS: ::Regexp
|
|
9
|
+
|
|
10
|
+
def self.generate_verifier: (?Integer length) -> String
|
|
11
|
+
|
|
12
|
+
def self.generate_challenge: (String verifier) -> String
|
|
13
|
+
|
|
14
|
+
def self.verify: (String challenge, String verifier) -> bool
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
private def self.validate_verifier_length: (Integer length) -> void
|
|
19
|
+
|
|
20
|
+
private def self.validate_verifier: (String verifier) -> void
|
|
21
|
+
|
|
22
|
+
private def self.secure_compare: (String str1, String str2) -> bool
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module Serialization
|
|
3
|
+
type data_hash = Hash[String | Symbol, untyped]
|
|
4
|
+
|
|
5
|
+
class Error < AuthProto::Error
|
|
6
|
+
end
|
|
7
|
+
class VersionError < Error
|
|
8
|
+
end
|
|
9
|
+
class TypeMismatchError < Error
|
|
10
|
+
end
|
|
11
|
+
class ValidationError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Base[T]
|
|
15
|
+
CURRENT_VERSION: Integer
|
|
16
|
+
SENSITIVE_FIELDS: Array[String]
|
|
17
|
+
def self.serialize: (untyped obj) -> String
|
|
18
|
+
def self.deserialize: (String data) -> untyped
|
|
19
|
+
def initialize: () -> void
|
|
20
|
+
def serialize: (T obj) -> String
|
|
21
|
+
def deserialize: (String data) -> T
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def encrypt_sensitive_fields: (data_hash data, ?path: Array[String | Symbol]) -> void
|
|
25
|
+
def decrypt_sensitive_fields: (data_hash data, ?path: Array[String | Symbol]) -> void
|
|
26
|
+
def sensitive_field?: (String | Symbol field) -> bool
|
|
27
|
+
def type_identifier: () -> String
|
|
28
|
+
def serialize_data: (T obj) -> data_hash
|
|
29
|
+
def deserialize_data: (json_object data) -> T
|
|
30
|
+
def validate_object: (T obj) -> void
|
|
31
|
+
def parse_json: (String data) -> untyped
|
|
32
|
+
def validate_serialized_data: (untyped data) -> void
|
|
33
|
+
def deserialize_version: (json_object data) -> T
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module Serialization
|
|
3
|
+
class DPoPKey < Base[DPoP::KeyManager]
|
|
4
|
+
def self.serialize: (DPoP::KeyManager obj) -> String
|
|
5
|
+
def self.deserialize: (String data) -> DPoP::KeyManager
|
|
6
|
+
def type_identifier: () -> String
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def validate_object: (DPoP::KeyManager obj) -> void
|
|
10
|
+
def serialize_data: (DPoP::KeyManager obj) -> json_object
|
|
11
|
+
def deserialize_data: (json_object data) -> DPoP::KeyManager
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module Serialization
|
|
3
|
+
class Session < Base[State::Session]
|
|
4
|
+
def self.serialize: (State::Session obj) -> String
|
|
5
|
+
def self.deserialize: (String data) -> State::Session
|
|
6
|
+
def type_identifier: () -> String
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def validate_object: (State::Session obj) -> void
|
|
10
|
+
def serialize_data: (State::Session obj) -> data_hash
|
|
11
|
+
def deserialize_data: (json_object data) -> State::Session
|
|
12
|
+
|
|
13
|
+
def serialize_token_set: (State::TokenSet? tokens) -> String?
|
|
14
|
+
def deserialize_token_set: (String? data) -> State::TokenSet?
|
|
15
|
+
def serialize_auth_server: (ServerMetadata::AuthorizationServer? server) -> Hash[Symbol, untyped]?
|
|
16
|
+
def deserialize_auth_server: (json_object? data) -> ServerMetadata::AuthorizationServer?
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module Serialization
|
|
3
|
+
class StoredNonce < Base[DPoP::StoredNonce]
|
|
4
|
+
def self.serialize: (DPoP::StoredNonce obj) -> String
|
|
5
|
+
def self.deserialize: (String data) -> DPoP::StoredNonce
|
|
6
|
+
def type_identifier: () -> String
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def validate_object: (DPoP::StoredNonce obj) -> void
|
|
10
|
+
def serialize_data: (DPoP::StoredNonce obj) -> data_hash
|
|
11
|
+
def deserialize_data: (json_object data) -> DPoP::StoredNonce
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module Serialization
|
|
3
|
+
class TokenSet < Base[State::TokenSet]
|
|
4
|
+
def self.serialize: (State::TokenSet obj) -> String
|
|
5
|
+
def self.deserialize: (String data) -> State::TokenSet
|
|
6
|
+
def type_identifier: () -> String
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
def validate_object: (State::TokenSet obj) -> void
|
|
10
|
+
def serialize_data: (State::TokenSet obj) -> data_hash
|
|
11
|
+
def deserialize_data: (json_object data) -> State::TokenSet
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module ServerMetadata
|
|
3
|
+
class AuthorizationServer
|
|
4
|
+
@issuer: String
|
|
5
|
+
@authorization_endpoint: String
|
|
6
|
+
@token_endpoint: String
|
|
7
|
+
@pushed_authorization_request_endpoint: String
|
|
8
|
+
@response_types_supported: Array[String]
|
|
9
|
+
@grant_types_supported: Array[String]
|
|
10
|
+
@code_challenge_methods_supported: Array[String]
|
|
11
|
+
@token_endpoint_auth_methods_supported: Array[String]
|
|
12
|
+
@token_endpoint_auth_signing_alg_values_supported: Array[String]
|
|
13
|
+
@scopes_supported: Array[String]
|
|
14
|
+
@dpop_signing_alg_values_supported: Array[String]
|
|
15
|
+
|
|
16
|
+
REQUIRED_FIELDS: ::Array["issuer" | "authorization_endpoint" | "token_endpoint" | "response_types_supported" | "grant_types_supported" | "code_challenge_methods_supported" | "token_endpoint_auth_methods_supported" | "token_endpoint_auth_signing_alg_values_supported" | "scopes_supported" | "dpop_signing_alg_values_supported" | "pushed_authorization_request_endpoint"]
|
|
17
|
+
|
|
18
|
+
attr_reader issuer: String
|
|
19
|
+
attr_reader authorization_endpoint: String
|
|
20
|
+
attr_reader token_endpoint: String
|
|
21
|
+
attr_reader pushed_authorization_request_endpoint: String
|
|
22
|
+
attr_reader response_types_supported: Array[String]
|
|
23
|
+
attr_reader grant_types_supported: Array[String]
|
|
24
|
+
attr_reader code_challenge_methods_supported: Array[String]
|
|
25
|
+
attr_reader token_endpoint_auth_methods_supported: Array[String]
|
|
26
|
+
attr_reader token_endpoint_auth_signing_alg_values_supported: Array[String]
|
|
27
|
+
attr_reader scopes_supported: Array[String]
|
|
28
|
+
attr_reader dpop_signing_alg_values_supported: Array[String]
|
|
29
|
+
|
|
30
|
+
def initialize: (Hash[String, untyped] metadata) -> void
|
|
31
|
+
|
|
32
|
+
# Fetches and validates Authorization Server metadata from an issuer URL
|
|
33
|
+
# @param issuer [String] Authorization Server issuer URL
|
|
34
|
+
# @return [AuthorizationServer] new instance with fetched metadata
|
|
35
|
+
# @raise [InvalidAuthorizationServer] if metadata is invalid
|
|
36
|
+
def self.from_issuer: (String issuer) -> AuthorizationServer
|
|
37
|
+
|
|
38
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def validate_and_set_metadata: (Hash[String, untyped] metadata) -> void
|
|
43
|
+
|
|
44
|
+
def validate_issuer: (String issuer) -> String
|
|
45
|
+
|
|
46
|
+
def validate_https_url: (String url) -> String
|
|
47
|
+
|
|
48
|
+
def validate_response_types: (Array[String] types) -> void
|
|
49
|
+
|
|
50
|
+
def validate_grant_types: (Array[String] types) -> void
|
|
51
|
+
|
|
52
|
+
def validate_code_challenge_methods: (Array[String] methods) -> void
|
|
53
|
+
|
|
54
|
+
def validate_token_endpoint_auth_methods: (Array[String] methods) -> void
|
|
55
|
+
|
|
56
|
+
def validate_token_endpoint_auth_signing_algs: (Array[String] algs) -> void
|
|
57
|
+
|
|
58
|
+
def validate_dpop_signing_algs: (Array[String] algs) -> void
|
|
59
|
+
|
|
60
|
+
def validate_scopes: (Array[String] scopes) -> void
|
|
61
|
+
|
|
62
|
+
def validate_boolean_field: (Hash[String, untyped] metadata, String field, bool required_value) -> void
|
|
63
|
+
|
|
64
|
+
private def self.fetch_metadata: (String issuer) -> http_response
|
|
65
|
+
|
|
66
|
+
private def self.parse_metadata: (String body) -> Hash[String, untyped]
|
|
67
|
+
|
|
68
|
+
private def self.validate_issuer: (String metadata_issuer, String request_issuer) -> void
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module ServerMetadata
|
|
3
|
+
class OriginURL
|
|
4
|
+
@url: String
|
|
5
|
+
@uri: URI::Generic
|
|
6
|
+
|
|
7
|
+
attr_reader url: String
|
|
8
|
+
attr_reader uri: URI::Generic
|
|
9
|
+
|
|
10
|
+
def initialize: (String url) -> void
|
|
11
|
+
|
|
12
|
+
def valid?: () -> bool
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def https_scheme?: () -> bool
|
|
17
|
+
def root_path?: () -> bool
|
|
18
|
+
def explicit_port?: () -> bool
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module ServerMetadata
|
|
3
|
+
class ResourceServer
|
|
4
|
+
@authorization_servers: Array[String]
|
|
5
|
+
|
|
6
|
+
attr_reader authorization_servers: Array[String]
|
|
7
|
+
|
|
8
|
+
def initialize: (Hash[String, untyped] metadata) -> void
|
|
9
|
+
|
|
10
|
+
def self.from_url: (String url) -> ResourceServer
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def validate_authorization_servers: (Array[String] servers) -> Array[String]
|
|
15
|
+
|
|
16
|
+
def ensure_servers_exist: (Array[String] | nil) -> void
|
|
17
|
+
|
|
18
|
+
def ensure_exactly_one_server: (Array[String]) -> void
|
|
19
|
+
|
|
20
|
+
def validate_server_url_format: (String server_url) -> void
|
|
21
|
+
|
|
22
|
+
private def self.fetch_metadata: (String url) -> http_response
|
|
23
|
+
|
|
24
|
+
private def self.parse_metadata: (String body) -> Hash[String, untyped]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module State
|
|
3
|
+
class Session
|
|
4
|
+
include MonitorMixin
|
|
5
|
+
|
|
6
|
+
attr_reader session_id: String
|
|
7
|
+
attr_reader state_token: String
|
|
8
|
+
attr_reader client_id: String
|
|
9
|
+
attr_reader scope: String
|
|
10
|
+
attr_reader pkce_verifier: String
|
|
11
|
+
attr_reader pkce_challenge: String
|
|
12
|
+
attr_reader auth_server: ServerMetadata::AuthorizationServer?
|
|
13
|
+
attr_reader did: String?
|
|
14
|
+
attr_reader tokens: TokenSet?
|
|
15
|
+
|
|
16
|
+
def initialize: (client_id: String, scope: String, ?auth_server: ServerMetadata::AuthorizationServer?, ?did: String?) -> void
|
|
17
|
+
def authorization_server=: (ServerMetadata::AuthorizationServer server) -> ServerMetadata::AuthorizationServer
|
|
18
|
+
def did=: (String did) -> String
|
|
19
|
+
def tokens=: (TokenSet tokens) -> String
|
|
20
|
+
def authorized?: () -> bool
|
|
21
|
+
def renewable?: () -> bool
|
|
22
|
+
def validate_state: (String? state) -> bool
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def secure_compare: (String str1, String str2) -> bool
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module AuthProto
|
|
2
|
+
module State
|
|
3
|
+
class SessionManager
|
|
4
|
+
@serializer: Serialization::Session
|
|
5
|
+
|
|
6
|
+
def initialize: () -> void
|
|
7
|
+
def create_session: (client_id: String, scope: String, ?auth_server: ServerMetadata::AuthorizationServer?, ?did: String?) -> Session
|
|
8
|
+
def update_session: (Session session) -> Session
|
|
9
|
+
def get_session: (String session_id) -> Session?
|
|
10
|
+
def get_session_by_state: (String? state) -> Session?
|
|
11
|
+
def remove_session: (String session_id) -> void
|
|
12
|
+
def cleanup_expired: () -> void
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|