atproto_auth 0.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/.rubocop.yml +16 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +179 -0
- data/Rakefile +16 -0
- data/examples/confidential_client/Gemfile +12 -0
- data/examples/confidential_client/Gemfile.lock +84 -0
- data/examples/confidential_client/README.md +110 -0
- data/examples/confidential_client/app.rb +136 -0
- data/examples/confidential_client/config/client-metadata.json +25 -0
- data/examples/confidential_client/config.ru +4 -0
- data/examples/confidential_client/public/client-metadata.json +24 -0
- data/examples/confidential_client/public/styles.css +70 -0
- data/examples/confidential_client/scripts/generate_keys.rb +15 -0
- data/examples/confidential_client/views/authorized.erb +29 -0
- data/examples/confidential_client/views/index.erb +44 -0
- data/examples/confidential_client/views/layout.erb +11 -0
- data/lib/atproto_auth/client.rb +410 -0
- data/lib/atproto_auth/client_metadata.rb +264 -0
- data/lib/atproto_auth/configuration.rb +17 -0
- data/lib/atproto_auth/dpop/client.rb +122 -0
- data/lib/atproto_auth/dpop/key_manager.rb +235 -0
- data/lib/atproto_auth/dpop/nonce_manager.rb +138 -0
- data/lib/atproto_auth/dpop/proof_generator.rb +112 -0
- data/lib/atproto_auth/errors.rb +47 -0
- data/lib/atproto_auth/http_client.rb +227 -0
- data/lib/atproto_auth/identity/document.rb +104 -0
- data/lib/atproto_auth/identity/resolver.rb +221 -0
- data/lib/atproto_auth/identity.rb +24 -0
- data/lib/atproto_auth/par/client.rb +203 -0
- data/lib/atproto_auth/par/client_assertion.rb +50 -0
- data/lib/atproto_auth/par/request.rb +140 -0
- data/lib/atproto_auth/par/response.rb +23 -0
- data/lib/atproto_auth/par.rb +40 -0
- data/lib/atproto_auth/pkce.rb +105 -0
- data/lib/atproto_auth/server_metadata/authorization_server.rb +175 -0
- data/lib/atproto_auth/server_metadata/origin_url.rb +51 -0
- data/lib/atproto_auth/server_metadata/resource_server.rb +71 -0
- data/lib/atproto_auth/server_metadata.rb +24 -0
- data/lib/atproto_auth/state/session.rb +117 -0
- data/lib/atproto_auth/state/session_manager.rb +75 -0
- data/lib/atproto_auth/state/token_set.rb +68 -0
- data/lib/atproto_auth/state.rb +54 -0
- data/lib/atproto_auth/version.rb +5 -0
- data/lib/atproto_auth.rb +56 -0
- data/sig/atproto_auth/client_metadata.rbs +95 -0
- data/sig/atproto_auth/dpop/client.rbs +38 -0
- data/sig/atproto_auth/dpop/key_manager.rbs +33 -0
- data/sig/atproto_auth/dpop/nonce_manager.rbs +48 -0
- data/sig/atproto_auth/dpop/proof_generator.rbs +42 -0
- data/sig/atproto_auth/http_client.rbs +58 -0
- data/sig/atproto_auth/identity/document.rbs +31 -0
- data/sig/atproto_auth/identity/resolver.rbs +41 -0
- data/sig/atproto_auth/par/client.rbs +31 -0
- data/sig/atproto_auth/par/request.rbs +73 -0
- data/sig/atproto_auth/par/response.rbs +17 -0
- data/sig/atproto_auth/pkce.rbs +24 -0
- data/sig/atproto_auth/server_metadata/authorization_server.rbs +69 -0
- data/sig/atproto_auth/server_metadata/origin_url.rbs +21 -0
- data/sig/atproto_auth/server_metadata/resource_server.rbs +27 -0
- data/sig/atproto_auth/state/session.rbs +50 -0
- data/sig/atproto_auth/state/session_manager.rbs +26 -0
- data/sig/atproto_auth/state/token_set.rbs +40 -0
- data/sig/atproto_auth/version.rbs +3 -0
- data/sig/atproto_auth.rbs +39 -0
- metadata +142 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AtprotoAuth
|
4
|
+
# Manages OAuth state for AT Protocol authorization flows. This module provides thread-safe
|
5
|
+
# storage and management of authorization session data, including tokens, PKCE values,
|
6
|
+
# and identity information.
|
7
|
+
#
|
8
|
+
# The module consists of three main components:
|
9
|
+
#
|
10
|
+
# 1. {TokenSet} - Represents OAuth tokens (access and refresh) with their metadata,
|
11
|
+
# including expiration times, scope, and the authenticated user's DID.
|
12
|
+
#
|
13
|
+
# 2. {Session} - Tracks the complete state of an authorization flow, including:
|
14
|
+
# - PKCE verifier/challenge pairs
|
15
|
+
# - State tokens for request verification
|
16
|
+
# - Authorization server information
|
17
|
+
# - Current tokens and user identity (DID)
|
18
|
+
#
|
19
|
+
# 3. {SessionManager} - Provides thread-safe storage and retrieval of active sessions,
|
20
|
+
# with support for lookup by session ID or state token.
|
21
|
+
#
|
22
|
+
# @example Creating and managing a session
|
23
|
+
# manager = AtprotoAuth::State::SessionManager.new
|
24
|
+
#
|
25
|
+
# # Create a new session
|
26
|
+
# session = manager.create_session(
|
27
|
+
# client_id: "https://myapp.com/client-metadata.json",
|
28
|
+
# scope: "atproto"
|
29
|
+
# )
|
30
|
+
#
|
31
|
+
# # Update session with tokens
|
32
|
+
# tokens = TokenSet.new(
|
33
|
+
# access_token: "...",
|
34
|
+
# token_type: "DPoP",
|
35
|
+
# expires_in: 3600,
|
36
|
+
# scope: "atproto",
|
37
|
+
# sub: "did:plc:abcdef123"
|
38
|
+
# )
|
39
|
+
# session.tokens = tokens
|
40
|
+
#
|
41
|
+
# # Lookup session later
|
42
|
+
# session = manager.get_session(session_id)
|
43
|
+
# if session.authorized?
|
44
|
+
# puts "Access token: #{session.tokens.access_token}"
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# All classes in this module are thread-safe and can be used in concurrent environments.
|
48
|
+
# The module handles secure generation and validation of state tokens, and ensures
|
49
|
+
# consistency of session data through synchronized access.
|
50
|
+
module State
|
51
|
+
# Error raised for session-related issues
|
52
|
+
class SessionError < AtprotoAuth::Error; end
|
53
|
+
end
|
54
|
+
end
|
data/lib/atproto_auth.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jose"
|
4
|
+
require "jwt"
|
5
|
+
|
6
|
+
require "atproto_auth/version"
|
7
|
+
|
8
|
+
require "atproto_auth/configuration"
|
9
|
+
require "atproto_auth/errors"
|
10
|
+
require "atproto_auth/client_metadata"
|
11
|
+
require "atproto_auth/http_client"
|
12
|
+
require "atproto_auth/pkce"
|
13
|
+
|
14
|
+
require "atproto_auth/server_metadata"
|
15
|
+
require "atproto_auth/server_metadata/origin_url"
|
16
|
+
require "atproto_auth/server_metadata/authorization_server"
|
17
|
+
require "atproto_auth/server_metadata/resource_server"
|
18
|
+
|
19
|
+
require "atproto_auth/dpop/key_manager"
|
20
|
+
require "atproto_auth/dpop/proof_generator"
|
21
|
+
require "atproto_auth/dpop/nonce_manager"
|
22
|
+
require "atproto_auth/dpop/client"
|
23
|
+
|
24
|
+
require "atproto_auth/state"
|
25
|
+
require "atproto_auth/state/token_set"
|
26
|
+
require "atproto_auth/state/session"
|
27
|
+
require "atproto_auth/state/session_manager"
|
28
|
+
|
29
|
+
require "atproto_auth/identity"
|
30
|
+
require "atproto_auth/identity/document"
|
31
|
+
require "atproto_auth/identity/resolver"
|
32
|
+
|
33
|
+
require "atproto_auth/par"
|
34
|
+
require "atproto_auth/par/client_assertion"
|
35
|
+
require "atproto_auth/par/request"
|
36
|
+
require "atproto_auth/par/response"
|
37
|
+
require "atproto_auth/par/client"
|
38
|
+
|
39
|
+
require "atproto_auth/client"
|
40
|
+
|
41
|
+
# AtprotoAuth is a Ruby library implementing the AT Protocol OAuth specification.
|
42
|
+
# It provides functionality for both client and server-side implementations of
|
43
|
+
# the AT Protocol OAuth flow, including support for DPoP, PAR, and dynamic client registration.
|
44
|
+
module AtprotoAuth
|
45
|
+
class << self
|
46
|
+
attr_writer :configuration
|
47
|
+
|
48
|
+
def configuration
|
49
|
+
@configuration ||= Configuration.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def configure
|
53
|
+
yield(configuration)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
# Handles and validates AT Protocol OAuth client metadata.
|
3
|
+
class ClientMetadata
|
4
|
+
@client_id: String
|
5
|
+
|
6
|
+
@grant_types: Array[String]
|
7
|
+
|
8
|
+
@response_types: Array[String]
|
9
|
+
|
10
|
+
@redirect_uris: Array[String]
|
11
|
+
|
12
|
+
@scope: String
|
13
|
+
|
14
|
+
@client_name: String?
|
15
|
+
|
16
|
+
@application_type: String
|
17
|
+
|
18
|
+
@client_uri: String?
|
19
|
+
|
20
|
+
@logo_uri: String?
|
21
|
+
|
22
|
+
@tos_uri: String?
|
23
|
+
|
24
|
+
@policy_uri: String?
|
25
|
+
|
26
|
+
@token_endpoint_auth_method: String?
|
27
|
+
|
28
|
+
@jwks: Hash[String, untyped]?
|
29
|
+
|
30
|
+
@jwks_uri: String?
|
31
|
+
|
32
|
+
attr_reader client_id: String
|
33
|
+
attr_reader grant_types: Array[String]
|
34
|
+
attr_reader response_types: Array[String]
|
35
|
+
attr_reader redirect_uris: Array[String]
|
36
|
+
attr_reader scope: String
|
37
|
+
attr_reader client_name: String?
|
38
|
+
attr_reader client_uri: String?
|
39
|
+
attr_reader logo_uri: String?
|
40
|
+
attr_reader tos_uri: String?
|
41
|
+
attr_reader policy_uri: String?
|
42
|
+
attr_reader token_endpoint_auth_method: String?
|
43
|
+
attr_reader jwks: Hash[String, untyped]?
|
44
|
+
attr_reader jwks_uri: String?
|
45
|
+
attr_reader application_type: String
|
46
|
+
|
47
|
+
def initialize: (Hash[String, untyped] metadata) -> void
|
48
|
+
|
49
|
+
def self.from_url: (String url) -> ClientMetadata
|
50
|
+
|
51
|
+
def confidential?: () -> bool
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def validate_and_set_metadata!: (Hash[String, untyped] metadata) -> void
|
56
|
+
|
57
|
+
def validate_client_id!: (String client_id) -> String
|
58
|
+
|
59
|
+
def validate_grant_types!: (Array[String] grant_types) -> Array[String]
|
60
|
+
|
61
|
+
def validate_response_types!: (Array[String] response_types) -> Array[String]
|
62
|
+
|
63
|
+
def validate_redirect_uris!: (Array[String] uris) -> Array[String]
|
64
|
+
|
65
|
+
def validate_redirect_uri!: (URI uri) -> void
|
66
|
+
|
67
|
+
def validate_redirect_uri_origin!: (URI uri) -> void
|
68
|
+
|
69
|
+
def validate_native_redirect_uri!: (URI uri) -> void
|
70
|
+
|
71
|
+
def validate_custom_scheme!: (URI uri) -> void
|
72
|
+
|
73
|
+
def validate_scope!: (String scope) -> String
|
74
|
+
|
75
|
+
def validate_offline_access_scope!: (Array[String] scope_values) -> void
|
76
|
+
|
77
|
+
def validate_application_type: (String? type) -> String
|
78
|
+
|
79
|
+
def validate_client_uri: (String? uri) -> String?
|
80
|
+
|
81
|
+
def validate_https_uri: (String? uri) -> String?
|
82
|
+
|
83
|
+
def validate_auth_methods!: (Hash[String, untyped] metadata) -> void
|
84
|
+
|
85
|
+
def validate_dpop!: (Hash[String, untyped] metadata) -> void
|
86
|
+
|
87
|
+
def self.validate_url!: (String url) -> void
|
88
|
+
|
89
|
+
def self.fetch_metadata: (String url) -> Hash[Symbol, untyped]
|
90
|
+
|
91
|
+
def self.parse_metadata: (String body) -> Hash[String, untyped]
|
92
|
+
|
93
|
+
def self.validate_client_id!: (String metadata_client_id, String url) -> void
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module DPoP
|
3
|
+
class Client
|
4
|
+
@key_manager: untyped
|
5
|
+
|
6
|
+
@nonce_manager: untyped
|
7
|
+
|
8
|
+
@proof_generator: untyped
|
9
|
+
|
10
|
+
class Error < AtprotoAuth::Error
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader key_manager: untyped
|
14
|
+
|
15
|
+
attr_reader proof_generator: untyped
|
16
|
+
|
17
|
+
attr_reader nonce_manager: untyped
|
18
|
+
|
19
|
+
def initialize: (?key_manager: untyped?, ?nonce_ttl: untyped?) -> void
|
20
|
+
|
21
|
+
def generate_proof: (http_method: untyped, http_uri: untyped, ?access_token: untyped?) -> untyped
|
22
|
+
|
23
|
+
def process_response: (untyped response_headers, untyped server_url) -> untyped
|
24
|
+
|
25
|
+
def request_headers: (untyped proof) -> { "DPoP" => untyped }
|
26
|
+
|
27
|
+
def public_key: () -> untyped
|
28
|
+
|
29
|
+
def export_key: (?include_private: bool) -> untyped
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def extract_nonce: (untyped headers) -> untyped
|
34
|
+
|
35
|
+
def origin_for_uri: (untyped uri) -> untyped
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module DPoP
|
3
|
+
class KeyManager
|
4
|
+
@keypair: JOSE::JWK
|
5
|
+
|
6
|
+
class KeyError < AtprotoAuth::Error
|
7
|
+
end
|
8
|
+
|
9
|
+
CURVE: String
|
10
|
+
ALGORITHM: String
|
11
|
+
|
12
|
+
attr_reader keypair: JOSE::JWK
|
13
|
+
|
14
|
+
def initialize: (?JOSE::JWK keypair) -> void
|
15
|
+
|
16
|
+
def generate_keypair: () -> JOSE::JWK
|
17
|
+
|
18
|
+
def public_jwk: () -> Hash[String, untyped]
|
19
|
+
|
20
|
+
def sign: (String data) -> String
|
21
|
+
|
22
|
+
def verify: (String signature, String data) -> bool
|
23
|
+
|
24
|
+
def to_jwk: (?include_private: bool) -> Hash[String, untyped]
|
25
|
+
|
26
|
+
def self.from_jwk: (Hash[String, untyped] jwk) -> AtprotoAuth::DPoP::KeyManager
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validate_keypair!: () -> void
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module DPoP
|
3
|
+
class NonceManager
|
4
|
+
@ttl: Integer
|
5
|
+
@nonces: Hash[String, AtprotoAuth::DPoP::NonceManager::StoredNonce]
|
6
|
+
@monitor: Monitor
|
7
|
+
|
8
|
+
class NonceError < AtprotoAuth::Error
|
9
|
+
end
|
10
|
+
|
11
|
+
class StoredNonce
|
12
|
+
@value: String
|
13
|
+
@server_url: String
|
14
|
+
@timestamp: Integer
|
15
|
+
|
16
|
+
attr_reader value: String
|
17
|
+
attr_reader timestamp: Integer
|
18
|
+
attr_reader server_url: String
|
19
|
+
|
20
|
+
def initialize: (String value, String server_url) -> void
|
21
|
+
|
22
|
+
def expired?: (?Integer ttl) -> bool
|
23
|
+
end
|
24
|
+
|
25
|
+
DEFAULT_TTL: Integer
|
26
|
+
|
27
|
+
def initialize: (?ttl: Integer) -> void
|
28
|
+
|
29
|
+
def update: (nonce: String, server_url: String) -> void
|
30
|
+
|
31
|
+
def get: (String server_url) -> (nil | String)
|
32
|
+
|
33
|
+
def clear: (String server_url) -> void
|
34
|
+
|
35
|
+
def clear_all: () -> void
|
36
|
+
|
37
|
+
def server_urls: () -> Array[String]
|
38
|
+
|
39
|
+
def valid_nonce?: (String server_url) -> bool
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def validate_inputs!: (String nonce, String server_url) -> void
|
44
|
+
|
45
|
+
def validate_server_url!: (String server_url) -> void
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module DPoP
|
3
|
+
class ProofGenerator
|
4
|
+
@key_manager: AtprotoAuth::DPoP::KeyManager
|
5
|
+
|
6
|
+
class ProofError < AtprotoAuth::Error
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader key_manager: AtprotoAuth::DPoP::KeyManager
|
10
|
+
|
11
|
+
def initialize: (AtprotoAuth::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
|
+
) -> String
|
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[Symbol, untyped]
|
34
|
+
|
35
|
+
def normalize_uri: (String uri) -> String
|
36
|
+
|
37
|
+
def generate_access_token_hash: (String access_token) -> String
|
38
|
+
|
39
|
+
def encode_jwt_segments: (Hash[Symbol, untyped] header, Hash[Symbol, untyped] payload) -> String
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
class HttpClient
|
3
|
+
@timeout: Integer
|
4
|
+
|
5
|
+
@verify_ssl: bool
|
6
|
+
|
7
|
+
FORBIDDEN_IP_RANGES: ::Array[IPAddr]
|
8
|
+
|
9
|
+
ALLOWED_SCHEMES: ::Array[String]
|
10
|
+
|
11
|
+
DEFAULT_TIMEOUT: Integer
|
12
|
+
|
13
|
+
MAX_REDIRECTS: Integer
|
14
|
+
|
15
|
+
MAX_RESPONSE_SIZE: Integer
|
16
|
+
|
17
|
+
class SSRFError < Error
|
18
|
+
end
|
19
|
+
|
20
|
+
class HttpError < Error
|
21
|
+
@response: ::Net::HTTPResponse?
|
22
|
+
|
23
|
+
attr_reader response: ::Net::HTTPResponse?
|
24
|
+
|
25
|
+
def initialize: (String message, ::Net::HTTPResponse? response) -> void
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize: (?timeout: Integer, ?verify_ssl: bool) -> void
|
29
|
+
|
30
|
+
def get: (String url, ?::Hash[String, String] headers) -> { status: Integer, headers: ::Hash[String, String], body: String }
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validate_uri!: (String url) -> URI
|
35
|
+
|
36
|
+
def validate_ip!: (URI uri) -> nil
|
37
|
+
|
38
|
+
def resolve_ip: (String hostname) -> IPAddr
|
39
|
+
|
40
|
+
def forbidden_ip?: (IPAddr ip) -> bool
|
41
|
+
|
42
|
+
def make_request: (URI uri, ?::Hash[String, String] headers) -> ::Net::HTTPResponse
|
43
|
+
|
44
|
+
def configure_http_client!: (::Net::HTTP http) -> void
|
45
|
+
|
46
|
+
def add_security_headers!: (::Net::HTTP::Get request, ::Hash[String, String] headers) -> void
|
47
|
+
|
48
|
+
def handle_redirect: (URI original_uri, ::Net::HTTPResponse response, ::Hash[String, String] headers, ?::Integer redirect_count) -> ::Net::HTTPResponse
|
49
|
+
|
50
|
+
def validate_response!: (::Net::HTTPResponse response) -> void
|
51
|
+
|
52
|
+
def check_success_status!: (::Net::HTTPResponse response) -> nil
|
53
|
+
|
54
|
+
def check_content_length!: (::Net::HTTPResponse response) -> nil
|
55
|
+
|
56
|
+
def check_content_type!: (::Net::HTTPResponse response) -> nil
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module Identity
|
3
|
+
class Document
|
4
|
+
@did: String
|
5
|
+
@rotation_keys: Array[String]
|
6
|
+
@also_known_as: Array[String]
|
7
|
+
@services: Array[Hash[String, untyped]]
|
8
|
+
@pds: String
|
9
|
+
|
10
|
+
attr_reader did: String
|
11
|
+
attr_reader rotation_keys: Array[String]
|
12
|
+
attr_reader also_known_as: Array[String]
|
13
|
+
attr_reader services: Array[Hash[String, untyped]]
|
14
|
+
attr_reader pds: String
|
15
|
+
|
16
|
+
def initialize: (Hash[String, untyped] data) -> void
|
17
|
+
|
18
|
+
def has_handle?: (String handle) -> bool
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def validate_document!: (Hash[String, untyped] data) -> void
|
23
|
+
|
24
|
+
def validate_did!: (String did) -> void
|
25
|
+
|
26
|
+
def validate_services!: (Array[Hash[String, untyped]]? services) -> void
|
27
|
+
|
28
|
+
def extract_pds!: (Hash[String, untyped] data) -> String
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module Identity
|
3
|
+
class Resolver
|
4
|
+
@plc_directory: String
|
5
|
+
|
6
|
+
PLC_DIRECTORY_URL: String
|
7
|
+
DID_PLC_PREFIX: String
|
8
|
+
HANDLE_REGEX: ::Regexp
|
9
|
+
|
10
|
+
def initialize: (?plc_directory: String?) -> void
|
11
|
+
|
12
|
+
def resolve_handle: (String handle) -> Hash[Symbol, untyped]
|
13
|
+
|
14
|
+
def get_did_info: (String did) -> Hash[Symbol, untyped]
|
15
|
+
|
16
|
+
def verify_pds_binding: (String did, String pds_url) -> bool
|
17
|
+
|
18
|
+
def verify_issuer_binding: (String did, String issuer) -> bool
|
19
|
+
|
20
|
+
def verify_handle_binding: (String handle, String did) -> bool
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate_handle!: (String handle) -> void
|
25
|
+
|
26
|
+
def validate_did!: (String did) -> void
|
27
|
+
|
28
|
+
def normalize_handle: (String handle) -> String
|
29
|
+
|
30
|
+
def resolve_handle_dns: (String _handle) -> nil
|
31
|
+
|
32
|
+
def resolve_handle_http: (String handle) -> Hash[Symbol, untyped]
|
33
|
+
|
34
|
+
def fetch_did_document: (String did) -> Hash[String, untyped]
|
35
|
+
|
36
|
+
def validate_pds_url!: (String url) -> void
|
37
|
+
|
38
|
+
def normalize_url: (String url) -> String
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module PAR
|
3
|
+
class Client
|
4
|
+
@endpoint: String
|
5
|
+
|
6
|
+
attr_reader endpoint: String
|
7
|
+
|
8
|
+
def initialize: (endpoint: String) -> void
|
9
|
+
|
10
|
+
def submit: (Request request) -> Response
|
11
|
+
|
12
|
+
def authorization_url: (
|
13
|
+
authorize_endpoint: String,
|
14
|
+
request_uri: String,
|
15
|
+
client_id: String
|
16
|
+
) -> String
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def validate_endpoint!: () -> void
|
21
|
+
|
22
|
+
def build_headers: (Request request) -> Hash[String, String]
|
23
|
+
|
24
|
+
def make_request: (Request request, Hash[String, String] headers) -> Hash[Symbol, untyped]
|
25
|
+
|
26
|
+
def process_response: (Hash[Symbol, untyped] response) -> Response
|
27
|
+
|
28
|
+
def encode_params: (Hash[String, String]) -> String
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module AtprotoAuth
|
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) -> void } -> 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!: () -> 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 AtprotoAuth
|
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) -> void
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def validate!: () -> void
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module AtprotoAuth
|
2
|
+
module PKCE
|
3
|
+
class Error < AtprotoAuth::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
|
+
def self.validate_verifier_length!: (Integer length) -> void
|
19
|
+
|
20
|
+
def self.validate_verifier!: (String verifier) -> void
|
21
|
+
|
22
|
+
def self.secure_compare: (String str1, String str2) -> bool
|
23
|
+
end
|
24
|
+
end
|