authproto 0.3.0.beta1 → 0.3.0.beta2

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/AGENTS.md +80 -0
  4. data/CHANGELOG.md +48 -7
  5. data/README.md +20 -0
  6. data/examples/confidential_client/.gitignore +2 -1
  7. data/examples/confidential_client/Gemfile.lock +5 -1
  8. data/examples/confidential_client/README.md +2 -2
  9. data/examples/confidential_client/app.rb +17 -17
  10. data/examples/confidential_client/config/{client-metadata.example.json → oauth-client-metadata.example.json} +0 -1
  11. data/examples/confidential_client/scripts/generate_keys.rb +15 -9
  12. data/examples/minisky_console/Gemfile.lock +2 -2
  13. data/examples/minisky_console/localhost.rb +8 -25
  14. data/examples/minisky_console/production.rb +1 -1
  15. data/lib/authproto/client.rb +150 -201
  16. data/lib/authproto/dpop/client.rb +12 -60
  17. data/lib/authproto/dpop/key_manager.rb +17 -17
  18. data/lib/authproto/dpop/nonce_manager.rb +5 -19
  19. data/lib/authproto/dpop/proof_generator.rb +27 -23
  20. data/lib/authproto/errors.rb +1 -1
  21. data/lib/authproto/http_client.rb +17 -31
  22. data/lib/authproto/localhost_server.rb +15 -6
  23. data/lib/authproto/metadata/authorization_server.rb +358 -0
  24. data/lib/authproto/metadata/client_metadata.rb +574 -0
  25. data/lib/authproto/metadata/resource_server.rb +137 -0
  26. data/lib/authproto/{server_metadata.rb → metadata.rb} +7 -7
  27. data/lib/authproto/minisky.rb +8 -10
  28. data/lib/authproto/par/client.rb +51 -55
  29. data/lib/authproto/par/client_assertion.rb +58 -15
  30. data/lib/authproto/par/request.rb +58 -140
  31. data/lib/authproto/par/response.rb +9 -10
  32. data/lib/authproto/par.rb +3 -5
  33. data/lib/authproto/pkce.rb +29 -106
  34. data/lib/authproto/serialization/session.rb +11 -11
  35. data/lib/authproto/serialization/token_set.rb +6 -6
  36. data/lib/authproto/state/session.rb +27 -31
  37. data/lib/authproto/state/session_manager.rb +2 -2
  38. data/lib/authproto/state/token_set.rb +4 -4
  39. data/lib/authproto/state.rb +3 -3
  40. data/lib/authproto/token/refresh.rb +35 -43
  41. data/lib/authproto/validated_uri.rb +57 -0
  42. data/lib/authproto/version.rb +1 -1
  43. data/lib/authproto.rb +1 -0
  44. data/misc/yard_attributes.rb +34 -0
  45. data/sig/authproto/client.rbs +9 -10
  46. data/sig/authproto/dpop/client.rbs +1 -8
  47. data/sig/authproto/dpop/nonce_manager.rbs +0 -1
  48. data/sig/authproto/dpop/proof_generator.rbs +5 -5
  49. data/sig/authproto/http_client.rbs +2 -2
  50. data/sig/authproto/{server_metadata → metadata}/authorization_server.rbs +18 -12
  51. data/sig/authproto/metadata/client_metadata.rbs +118 -0
  52. data/sig/authproto/metadata/resource_server.rbs +27 -0
  53. data/sig/authproto/minisky_oauth.rbs +5 -3
  54. data/sig/authproto/par/client.rbs +4 -6
  55. data/sig/authproto/par/client_assertion.rbs +2 -1
  56. data/sig/authproto/par/request.rbs +5 -19
  57. data/sig/authproto/par/response.rbs +1 -5
  58. data/sig/authproto/pkce.rbs +6 -16
  59. data/sig/authproto/serialization/session.rbs +2 -2
  60. data/sig/authproto/state/session.rbs +4 -7
  61. data/sig/authproto/state/session_manager.rbs +2 -2
  62. data/sig/authproto/token/refresh.rbs +4 -4
  63. data/sig/authproto/validated_uri.rbs +36 -0
  64. data/sig/authproto.rbs +5 -3
  65. metadata +13 -11
  66. data/lib/authproto/client_metadata.rb +0 -478
  67. data/lib/authproto/server_metadata/authorization_server.rb +0 -267
  68. data/lib/authproto/server_metadata/origin_url.rb +0 -71
  69. data/lib/authproto/server_metadata/resource_server.rb +0 -98
  70. data/sig/authproto/client_metadata.rbs +0 -113
  71. data/sig/authproto/server_metadata/origin_url.rbs +0 -21
  72. data/sig/authproto/server_metadata/resource_server.rbs +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77c4682515836e03bc41e1d03724f5ca2a85e65a11655b2af8a8ba09049a73fc
4
- data.tar.gz: 0ba9433f0ee89b8617354d1e0245644f421fc1e7264597391b2efa5ebc015f79
3
+ metadata.gz: a24a4c3d46d68006268cf34364953c33cd23225f2203f2054e7167f8f1470970
4
+ data.tar.gz: 80287bbe86fc764f9c8b272d090b8736a17ab76470b5ac0fa32903b94dc64cce
5
5
  SHA512:
6
- metadata.gz: ebbd4f4c9df22f002bbe9be7c02b502bf4fcf5c056d4d0e9290a2c91dde8f03b4756e3b3db9f306cc1b9d5be6be183b88ace24ddb195733c37e92823ec0d5d78
7
- data.tar.gz: 3b96e49a8ea9bbb555dab5996c0e6a53f0894616928c4416199f00561f2d9603cf327d0c868cf66501fd7b5982a8ec985d9e27e5032de274ef8daaccad58dbb4
6
+ metadata.gz: cb4ddce58f52416d16f92dff7f6caf0212c5770addb626610c9459d90894e1c9da4cc664122d359f3a4cdfe572000841cdd3b9daf1ac3defbf472cb3b7fd2474
7
+ data.tar.gz: 3dd080bf47dc27d1b318784aad020263ab362d2e49f4fbfd4e2716029841de99f0eb7199ac5ba1483208fe1993967b574793ad01fdd41240fd2e5b37d56ddc7f
data/.yardopts CHANGED
@@ -1,4 +1,5 @@
1
1
  --protected
2
2
  --no-private
3
3
  --markup markdown
4
+ --load misc/yard_attributes.rb
4
5
  lib/**/*.rb
data/AGENTS.md ADDED
@@ -0,0 +1,80 @@
1
+ ### Ruby code:
2
+
3
+ - always add `# frozen_string_literal: true` at the beginning of Ruby files (both library and tests)
4
+ - keep `require`s and `require_relative`s organized in separate blocks, ordered alphabetically in each:
5
+
6
+ ```
7
+ require 'ipaddr'
8
+ require 'socket'
9
+ require 'timeout'
10
+ require 'uri'
11
+
12
+ require_relative 'client_metadata'
13
+ require_relative 'validated_uri'
14
+ ```
15
+
16
+ - wrap lines longer than 120 characters
17
+ - for multi-line `if ... end` / `unless ... end` blocks, keep one empty line above/below separating them from the rest of the code in the method, unless it's at the beginning/end of the method, e.g.:
18
+
19
+ ```
20
+ def validate_grant_types(grant_types)
21
+ unless grant_types.include?('authorization_code')
22
+ raise InvalidClientMetadata, "grant_types must include authorization_code"
23
+ end
24
+
25
+ valid_types = %w[authorization_code refresh_token]
26
+ ```
27
+
28
+ - prefer single-quote strings for strings containing identifiers without spaces, like `'code'`, `'access_token'`, and for `require`s
29
+ - prefer double-quote strings for strings containing messages with spaces meant to be shown to the user, e.g. `puts "grant_types must include authorization_code"`
30
+
31
+ ### Unit tests:
32
+
33
+ - use the classic RSpec `foo.should be_...` matcher style instead of `expect()`
34
+ - except for calling matchers on blocks as in `expect { ... }.to(not) raise_error...`
35
+ - use the classic `it "should do this"` naming for test cases instead of `it "does this"`
36
+ - use raw `describe` instead of `RSpec.describe` at the top level
37
+
38
+ ### Documentation:
39
+
40
+ - use YARD with Markdown formatting
41
+ - for class comments, keep one empty # line at the beginning and end, and one empty line above and below that, like:
42
+
43
+ ```
44
+
45
+ #
46
+ # OAuth client implementation class.
47
+ #
48
+
49
+ class ...
50
+ ```
51
+
52
+ - for method comments, leave one empty line above, no empty # starting line, and one empty # trailing line that is immediately followed by the method definition, like:
53
+
54
+ ```
55
+
56
+ # Reads tokens for a session with a currently valid access token.
57
+ #
58
+ # @param session_id [String] session identifier
59
+ # @return [Hash{Symbol => String, Integer, nil}, nil] token details, or `nil` for missing/unauthorized sessions
60
+ #
61
+ def get_tokens(session_id)
62
+ ```
63
+
64
+ - for properties, and also for one-liner classes like error types, use a compact style like:
65
+
66
+ ```
67
+
68
+ # Identifier of the session associated with the failure, if known.
69
+ # @return [String, nil]
70
+ attr_reader :session_id
71
+
72
+ # Error raised when token operations fail
73
+ class TokenError < Error; end
74
+
75
+ ```
76
+
77
+
78
+ ### Running code:
79
+
80
+ - make sure you don't use the ancient system Ruby 2.6, but a version supported by the library (my installation uses `asdf`)
data/CHANGELOG.md CHANGED
@@ -1,34 +1,75 @@
1
- ## [0.2.5] - 2024-12-13
1
+ ## [0.3.0.beta2] – 2026-10-31 👻
2
+
3
+ - private keys for signing are now passed separately as `signing_key:` instead of being taken from the same `jwks` in the metadata that holds public keys
4
+ - generate localhost metadata from `client_id` params
5
+ - `confidential_client`: validate `SESSION_SECRET` before start and throw an error early
6
+ - additional validations for URLs, resource server and authorization server metadata, and PAR request and response
7
+ - set `code_challenge_method` in the PAR request automatically to `S256`
8
+ - various renames, refactors, cleanups
9
+ - encode '*' in the authorization URL – unencoded asterisk might break click-to-open-URL in some terminals
10
+ - strip query part from URLs in `htu` in the DPoP proof generator
11
+ - keep existing query part in the PAR authorization endpoint when adding params to it
12
+ - verify that all scopes passed during authorization are declared in client metadata
13
+ - don't reuse client assertion JWT on retried requests
14
+ - POST redirects are not allowed, GET redirects are disabled by default
15
+
16
+ ## [0.3.0.beta1], as AuthProto – 2026-10-28
17
+
18
+ - modernized code
19
+ - various renames, refactors, cleanups
20
+ - removed some unused code
21
+ - removed strict dependency on the Redis gem
22
+ - added alternative SQLite storage
23
+ - added HTTP client version that allows `http://` connections
24
+ - added experimental Minisky adapter and `minisky_console` example
25
+ - added temporary in-process `localhost` HTTP server for receiving callbacks inside `irb`
26
+ - additional validations for request IPs and URLs, responses, and client metadata
27
+ - fixed `confidential_example` running on localhost
28
+ - updated `confidential_example` with a UI for making a test post to Bluesky
29
+ - updated examples to use `oauth-client-metadata.json` instead of `client-metadata.json`
30
+ - converted test suite to RSpec
31
+ - replaced `Identity` module with DIDKit
32
+ - fixed randomly generated encryption key not being shared between different parts of the app
33
+ - fixed some token refresh code failing due to wrong exception names
34
+ - don't try to add client assertions for public clients
35
+ - switched to JWT gem version 3.x
36
+
37
+
38
+ ---
39
+
40
+ # Earlier releases as `atproto_auth`:
41
+
42
+ ## [0.2.5] – 2024-12-13
2
43
 
3
44
  Fixed:
4
45
 
5
46
  - Additional token refresh edge case handling
6
47
 
7
- ## [0.2.4] - 2024-12-13
48
+ ## [0.2.4] – 2024-12-13
8
49
 
9
50
  Fixed:
10
51
 
11
52
  - Token refresh now correctly handles expired tokens
12
53
 
13
- ## [0.2.2] - 2024-12-12
54
+ ## [0.2.2] – 2024-12-12
14
55
 
15
56
  Added:
16
57
 
17
58
  - Added support for did:web users
18
59
 
19
- ## [0.2.1] - 2024-12-11
60
+ ## [0.2.1] – 2024-12-11
20
61
 
21
62
  Fixed:
22
63
 
23
64
  - Fixed a bug with access token hash generation
24
65
 
25
- ## [0.2.0] - 2024-12-10
66
+ ## [0.2.0] – 2024-12-10
26
67
 
27
68
  Added:
28
69
 
29
70
  - Client#handle_callback now returns session did as part of the response
30
71
 
31
- ## [0.1.0] - 2024-12-07
72
+ ## [0.1.0] – 2024-12-07
32
73
 
33
74
  Added:
34
75
 
@@ -47,6 +88,6 @@ Changed:
47
88
  - Session and token management now use configured storage backend
48
89
  - Improved thread safety for all storage operations
49
90
 
50
- ## [0.0.1] - 2024-12-05
91
+ ## [0.0.1] – 2024-12-05
51
92
 
52
93
  - Initial release
data/README.md CHANGED
@@ -103,6 +103,13 @@ Client metadata must explicitly set `token_endpoint_auth_method` to `none` for
103
103
  public clients or `private_key_jwt` for confidential clients. Omitting it defaults
104
104
  to `client_secret_basic` in OAuth metadata, which this library does not support.
105
105
 
106
+ For localhost development, `Metadata::ClientMetadata.from_url` generates virtual metadata
107
+ without making an HTTP request. It defaults to native public authentication,
108
+ the `atproto` scope, and redirects `http://127.0.0.1/` and `http://[::1]/`.
109
+ The client ID query can specify one `scope` parameter and multiple `redirect_uri`
110
+ parameters, encoded using `URI.encode_www_form`. These values are decoded and
111
+ validated; the original client ID is preserved exactly.
112
+
106
113
  ### Storage Backends
107
114
 
108
115
  The library supports multiple storage backends for managing OAuth state:
@@ -190,11 +197,24 @@ end
190
197
 
191
198
  Here's a basic example of using the library in a confidential client application:
192
199
 
200
+ Supply the private EC P-256 client-authentication JWK through `signing_key:`,
201
+ separately from metadata. It must have a nonempty `kid` and permit ES256 signing.
202
+ Metadata's `jwks` contains only public keys; the client checks that the matching
203
+ public key and `kid` are advertised and active. A public key may declare
204
+ `use: "sig"` or `key_ops: ["verify"]`.
205
+ Other advertised public keys may serve other purposes, such as encryption;
206
+ the matching authentication key must permit signature verification.
207
+
208
+ With `jwks_uri`, the client still uses your separate private signing key. It does
209
+ not fetch that URL or check the advertised key locally; the authorization server
210
+ fetches it to verify assertions.
211
+
193
212
  ```ruby
194
213
  # Initialize client with metadata
195
214
  client = AuthProto::Client.new(
196
215
  client_id: "https://app.example.com/oauth-client-metadata.json",
197
216
  redirect_uri: "https://app.example.com/callback",
217
+ signing_key: JSON.parse(File.read("config/keys.json")).fetch("keys").first,
198
218
  metadata: {
199
219
  # Your client metadata...
200
220
  }
@@ -1,5 +1,6 @@
1
1
  .env
2
2
  /config/client-metadata.json
3
+ /config/oauth-client-metadata.json
3
4
  /config/keys.json
5
+ /config/public-keys.json
4
6
  /db/
5
- /public/client-metadata.json
@@ -1,16 +1,20 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- authproto (0.2.5)
4
+ authproto (0.3.0.beta1)
5
+ didkit (~> 0.5.0)
5
6
  jose (~> 1.2)
6
7
  jwt (~> 3.3)
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
12
+ base58 (0.2.3)
11
13
  base64 (0.3.0)
12
14
  concurrent-ruby (1.3.8)
13
15
  connection_pool (3.0.2)
16
+ didkit (0.5.0)
17
+ base58 (~> 0.2)
14
18
  dotenv (3.2.0)
15
19
  faraday (2.14.3)
16
20
  faraday-net_http (>= 2.0, < 3.5)
@@ -43,7 +43,7 @@ bundle install
43
43
  3. Generate EC keys for client authentication:
44
44
 
45
45
  ```bash
46
- bundle exec ruby scripts/generate_keys.rb > config/keys.json
46
+ bundle exec ruby scripts/generate_keys.rb
47
47
  ```
48
48
 
49
49
  4. Generate session & encryption keys:
@@ -64,7 +64,7 @@ SESSION_SECRET=<hex value from second command>
64
64
  ```
65
65
  - Set the correct `client_id` URL where your metadata will be hosted
66
66
  - Configure valid `redirect_uris` for your application
67
- - Add your generated keys from step 3 to the `jwks` field
67
+ - Copy the public JWKS from `config/public-keys.json` (not `config/keys.json`, which is the private keys) into the `jwks` field in oauth-client-metadata.json.
68
68
  - For localhost testing, set `client_id` to `http://localhost/` and `redirect_uris` to `["http://127.0.0.1:9292/callback"]`
69
69
 
70
70
  6. Set up environment variables:
@@ -12,6 +12,9 @@ require 'faraday'
12
12
  require 'sinatra/base'
13
13
  require 'sinatra/reloader'
14
14
 
15
+ session_secret = ENV.fetch("SESSION_SECRET") { SecureRandom.hex(32) }
16
+ raise "SESSION_SECRET must be at least 64 characters long (got: #{session_secret})" unless session_secret.length >= 64
17
+
15
18
  # Main app entry point
16
19
  class ExampleApp < Sinatra::Base
17
20
  def check_stored_session(session_id)
@@ -54,20 +57,24 @@ class ExampleApp < Sinatra::Base
54
57
  metadata_path = File.join(__dir__, "config", "oauth-client-metadata.json")
55
58
  metadata = JSON.parse(File.read(metadata_path))
56
59
 
57
- client_id = metadata["client_id"]
60
+ client_id = metadata['client_id']
58
61
 
59
62
  if client_id.chomp("/") == "http://localhost"
60
- scope = CGI.escape(metadata["scope"])
61
- redirect_uri = CGI.escape(metadata["redirect_uris"][0])
63
+ scope = CGI.escape(metadata['scope'])
64
+ redirect_uri = CGI.escape(metadata['redirect_uris'][0])
62
65
  client_id += "?scope=#{scope}&redirect_uri=#{redirect_uri}"
63
66
  end
64
67
 
68
+ # Private client-authentication keys are kept outside public metadata.
69
+ signing_key = JSON.parse(File.read(File.join(__dir__, "config", "keys.json")))["keys"].first
70
+
65
71
  # Create OAuth client
66
72
  set :oauth_client, AuthProto::Client.new(
67
73
  client_id: client_id,
68
- redirect_uri: metadata["redirect_uris"][0],
74
+ redirect_uri: metadata['redirect_uris'][0],
69
75
  metadata: metadata,
70
- dpop_key: metadata["jwks"]["keys"][0]
76
+ signing_key: signing_key,
77
+ dpop_key: signing_key
71
78
  )
72
79
  end
73
80
 
@@ -78,7 +85,7 @@ class ExampleApp < Sinatra::Base
78
85
  use Rack::Session::Cookie,
79
86
  key: "atproto.session",
80
87
  expire_after: 86_400, # 1 day in seconds
81
- secret: ENV.fetch("SESSION_SECRET") { SecureRandom.hex(32) },
88
+ secret: session_secret,
82
89
  secure: ENV.fetch("RACK_ENV", "development") == "production", # Only send over HTTPS
83
90
  httponly: true, # Not accessible via JavaScript
84
91
  same_site: :lax # CSRF protection
@@ -115,14 +122,7 @@ class ExampleApp < Sinatra::Base
115
122
  metadata_path = File.join(__dir__, "config", "oauth-client-metadata.json")
116
123
  metadata = JSON.parse(File.read(metadata_path))
117
124
 
118
- # Strip private key 'd' component from each key in the JWKS
119
- if metadata["jwks"] && metadata["jwks"]["keys"]
120
- metadata["jwks"]["keys"] = metadata["jwks"]["keys"].map do |key|
121
- key.except("d")
122
- end
123
- end
124
-
125
- # Return sanitized metadata
125
+ # This file contains only the public counterparts of config/keys.json.
126
126
  JSON.generate(metadata)
127
127
  end
128
128
 
@@ -190,7 +190,7 @@ class ExampleApp < Sinatra::Base
190
190
  # Get auth headers for request
191
191
  headers = settings.oauth_client.auth_headers(
192
192
  session_id: session_id,
193
- method: "GET",
193
+ method: 'GET',
194
194
  url: "https://api.bsky.app/xrpc/com.atproto.identity.resolveHandle"
195
195
  )
196
196
 
@@ -236,7 +236,7 @@ class ExampleApp < Sinatra::Base
236
236
 
237
237
  headers = settings.oauth_client.auth_headers(
238
238
  session_id: session_id,
239
- method: "POST",
239
+ method: 'POST',
240
240
  url: endpoint
241
241
  )
242
242
 
@@ -273,7 +273,7 @@ class ExampleApp < Sinatra::Base
273
273
  get "/signout" do
274
274
  if session[:oauth_session_id]
275
275
  # Clean up stored session
276
- settings.oauth_client.remove_session(session[:oauth_session_id])
276
+ settings.oauth_client.delete_session(session[:oauth_session_id])
277
277
  end
278
278
 
279
279
  session.clear
@@ -16,7 +16,6 @@
16
16
  "kid": "key-1",
17
17
  "x": "...",
18
18
  "crv": "P-256",
19
- "d": "...",
20
19
  "kty": "EC",
21
20
  "y": "..."
22
21
  }
@@ -1,15 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'jose'
4
+ require 'json'
4
5
 
5
- # Generate a new EC key using P-256 curve
6
- keypair = JOSE::JWK.generate_key([:ec, "P-256"])
6
+ key = JOSE::JWK.generate_key([:ec, "P-256"])
7
7
 
8
- # Get the key as a hash (need to convert from JOSE::Map)
9
- jwk = keypair.to_map.to_h
8
+ private_jwk = key.to_map.to_h.merge("kid" => "key-1", "use" => "sig")
9
+ public_jwk = key.to_public.to_map.to_h.merge("kid" => "key-1", "use" => "sig")
10
10
 
11
- # Pretty print the JWK to stdout
12
- require 'json'
13
- puts JSON.pretty_generate({
14
- keys: [jwk]
15
- })
11
+ config_dir = File.expand_path('../config', __dir__)
12
+ private_path = File.join(config_dir, 'keys.json')
13
+
14
+ File.open(private_path, File::WRONLY | File::CREAT | File::TRUNC, 0o600) do |file|
15
+ file.chmod(0o600)
16
+ file.write(JSON.pretty_generate('keys' => [private_jwk]) + "\n")
17
+ end
18
+
19
+ File.write(File.join(config_dir, 'public-keys.json'), JSON.pretty_generate('keys' => [public_jwk]) + "\n")
20
+
21
+ puts "Private keys saved to config/keys.json; publish only config/public-keys.json in metadata."
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- authproto (0.2.5)
4
+ authproto (0.3.0.beta1)
5
5
  didkit (~> 0.5.0)
6
6
  jose (~> 1.2)
7
7
  jwt (~> 3.3)
@@ -91,7 +91,7 @@ DEPENDENCIES
91
91
  sqlite3
92
92
 
93
93
  CHECKSUMS
94
- authproto (0.2.5)
94
+ authproto (0.3.0.beta1)
95
95
  base58 (0.2.3) sha256=fda5c5d3bda8b429ad0d1a5309c2d1e488ba46f0a2ab99a639890f26c61bf3be
96
96
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
97
97
  bundler (4.0.23) sha256=407ba8a1d928f412847f819aa5f8b6aef7c59fb51feb517112af8a165487287b
@@ -1,3 +1,5 @@
1
+ require 'authproto/insecure_http_client'
2
+ require 'authproto/localhost_server'
1
3
  require 'authproto/minisky'
2
4
  require 'dotenv/load'
3
5
  require 'uri'
@@ -10,41 +12,22 @@ AuthProto.configure do |config|
10
12
  )
11
13
  end
12
14
 
13
- $metadata = {
14
- 'application_type' => 'native',
15
- 'client_id' => 'http://localhost/',
16
- 'client_name' => 'AT Protocol OAuth Ruby Example',
17
- 'redirect_uris' => ['http://127.0.0.1:9292/callback'],
18
- 'grant_types' => ['authorization_code', 'refresh_token'],
19
- 'response_types' => ['code'],
20
- 'scope' => 'atproto repo:app.bsky.feed.post rpc:app.bsky.feed.getTimeline?aud=*',
21
- 'token_endpoint_auth_method' => 'none',
22
- 'dpop_bound_access_tokens' => true,
23
- }
24
-
25
- $client_id = $metadata['client_id'] + '?' + URI.encode_www_form({
26
- redirect_uri: $metadata['redirect_uris'][0],
27
- scope: $metadata['scope']
28
- })
29
-
30
- $metadata['client_id'] = $client_id
15
+ REDIRECT_URI = 'http://127.0.0.1:9292/callback'
16
+ SCOPE = 'atproto repo:app.bsky.feed.post rpc:app.bsky.feed.getTimeline?aud=*'
17
+ CLIENT_ID = 'http://localhost/?' + URI.encode_www_form({ redirect_uri: REDIRECT_URI, scope: SCOPE })
31
18
 
32
19
  def create_client(pds)
33
- Minisky::OAuthClient.new(pds,
34
- client_id: $client_id,
35
- redirect_uri: $metadata['redirect_uris'][0],
36
- metadata: $metadata
37
- )
20
+ Minisky::OAuthClient.new(pds, client_id: CLIENT_ID, redirect_uri: REDIRECT_URI)
38
21
  end
39
22
 
40
23
  def authorize(client)
41
- auth = client.authorize(pds_url: "https://#{client.host}", scope: $metadata['scope'])
24
+ auth = client.authorize(pds: "https://#{client.host}", scope: SCOPE)
42
25
 
43
26
  puts "Open this URL in the browser and complete authorization:"
44
27
  puts auth[:url]
45
28
  puts
46
29
 
47
- AuthProto::LocalhostServer.start($metadata) do |json|
30
+ AuthProto::LocalhostServer.start(client.oauth_client.client_metadata) do |json|
48
31
  client.handle_callback(**json)
49
32
  end
50
33
 
@@ -18,7 +18,7 @@ def create_client(server_hostname, pds)
18
18
  end
19
19
 
20
20
  def authorize(client)
21
- auth = client.authorize(pds_url: "https://#{client.host}", scope: client.oauth_client.client_metadata.scope)
21
+ auth = client.authorize(pds: "https://#{client.host}", scope: client.oauth_client.client_metadata.scope)
22
22
 
23
23
  puts "Open this URL in the browser and complete authorization:"
24
24
  puts auth[:url]