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,44 @@
|
|
|
1
|
+
<div class="flex flex-col items-center md:flex md:flex-row md:justify-stretch md:items-center min-h-screen min-w-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
|
2
|
+
<div class="px-6 pt-4 md:max-w-lg md:grid md:content-center md:justify-items-end md:self-stretch md:w-1/2 md:max-w-fix md:p-4 md:text-right md:dark:border-r md:dark:border-gray-700 md:bg-gray-100 md:dark:bg-gray-800">
|
|
3
|
+
<h1 class="text-xl md:text-2xl lg:text-5xl md:mt-4 mb-4 font-semibold text-brand">Sign in</h1>
|
|
4
|
+
<p class="hidden md:block max-w-xs text-gray-500 dark:text-gray-500">Enter your AT Protocol handle</p>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="w-full px-6 md:max-w-3xl md:px-12">
|
|
7
|
+
<form method="post" action="/auth" class="flex flex-col py-4 space-y-4">
|
|
8
|
+
<div class="space-y-4">
|
|
9
|
+
<% if session[:error] %>
|
|
10
|
+
<div class="mb-8 bg-red-50 border-red-700 text-red-700 p-4 rounded-md">
|
|
11
|
+
<%= session[:error] %>
|
|
12
|
+
<% session[:error] = nil %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% if session[:notice] %>
|
|
16
|
+
<div class="mb-8 bg-yellow-50 border-yellow-700 text-yellow-700 p-4 rounded-md">
|
|
17
|
+
<%= session[:notice] %>
|
|
18
|
+
<% session[:notice] = nil %>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
<fieldset>
|
|
22
|
+
<p class="mb-1 text-gray-600 dark:text-gray-400 text-sm font-medium">Handle</p>
|
|
23
|
+
<div class="flex flex-col space-y-4">
|
|
24
|
+
<div class="pl-1 pr-2 min-h-12 flex items-center justify-stretch rounded-lg text-gray-700 dark:text-gray-100 bg-gray-100 has-[:focus]:bg-gray-200 dark:bg-gray-800 dark:has-[:focus]:bg-gray-700 outline-none border-solid border-2 border-transparent hover:border-gray-400 has-[:focus]:border-brand hover:has-[:focus]:border-brand dark:hover:border-gray-500">
|
|
25
|
+
<div class="self-start shrink-0 grow-0 w-8 h-12 flex items-center justify-center text-gray-500">
|
|
26
|
+
<div>
|
|
27
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-5">
|
|
28
|
+
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M12 4a8 8 0 1 0 4.21 14.804 1 1 0 0 1 1.054 1.7A9.958 9.958 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 1.104-.27 2.31-.949 3.243-.716.984-1.849 1.6-3.331 1.465a4.207 4.207 0 0 1-2.93-1.585c-.94 1.21-2.388 1.94-3.985 1.715-2.53-.356-4.04-2.91-3.682-5.458.358-2.547 2.514-4.586 5.044-4.23.905.127 1.68.536 2.286 1.126a1 1 0 0 1 1.964.368l-.515 3.545v.002a2.222 2.222 0 0 0 1.999 2.526c.75.068 1.212-.21 1.533-.65.358-.493.566-1.245.566-2.067a8 8 0 0 0-8-8Zm-.112 5.13c-1.195-.168-2.544.819-2.784 2.529-.24 1.71.784 3.03 1.98 3.198 1.195.168 2.543-.819 2.784-2.529.24-1.71-.784-3.03-1.98-3.198Z"></path>
|
|
29
|
+
</svg>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="relative">
|
|
33
|
+
<input class="w-full bg-transparent bg-clip-padding text-base text-inherit outline-none dark:placeholder-gray-500" name="handle" type="text" placeholder="handle.bsky.social" aria-label="Handle" autocapitalize="none" autocorrect="off" autocomplete="username" spellcheck="false" dir="auto" enterkeyhint="next" required title="valid handle">
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</fieldset>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="pt-4">
|
|
40
|
+
<button role="Button" type="submit" aria-label="Next" class="py-2 px-6 rounded-lg truncate cursor-pointer touch-manipulation tracking-wide overflow-hidden bg-blue-400 text-white">Next</button>
|
|
41
|
+
</div>
|
|
42
|
+
</form>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>AT Protocol OAuth Example</title>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<%= yield %>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: ../..
|
|
3
|
+
specs:
|
|
4
|
+
authproto (0.2.5)
|
|
5
|
+
didkit (~> 0.5.0)
|
|
6
|
+
jose (~> 1.2)
|
|
7
|
+
jwt (~> 3.3)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
base58 (0.2.3)
|
|
13
|
+
base64 (0.3.0)
|
|
14
|
+
concurrent-ruby (1.3.8)
|
|
15
|
+
didkit (0.5.0)
|
|
16
|
+
base58 (~> 0.2)
|
|
17
|
+
dotenv (3.2.0)
|
|
18
|
+
erb (6.0.7)
|
|
19
|
+
immutable-ruby (0.2.0)
|
|
20
|
+
concurrent-ruby (~> 1.1)
|
|
21
|
+
sorted_set (~> 1.0)
|
|
22
|
+
io-console (0.9.4)
|
|
23
|
+
irb (1.18.0)
|
|
24
|
+
pp (>= 0.6.0)
|
|
25
|
+
prism (>= 1.3.0)
|
|
26
|
+
rdoc (>= 4.0.0)
|
|
27
|
+
reline (>= 0.4.2)
|
|
28
|
+
jose (1.2.0)
|
|
29
|
+
base64
|
|
30
|
+
immutable-ruby
|
|
31
|
+
jwt (3.3.0)
|
|
32
|
+
base64
|
|
33
|
+
logger (1.7.0)
|
|
34
|
+
minisky (0.5.1)
|
|
35
|
+
base64 (~> 0.1)
|
|
36
|
+
mustermann (3.1.1)
|
|
37
|
+
nio4r (2.7.5)
|
|
38
|
+
pp (0.6.4)
|
|
39
|
+
prettyprint
|
|
40
|
+
prettyprint (0.2.1)
|
|
41
|
+
prism (2.0.0)
|
|
42
|
+
prism (2.0.0-arm64-darwin)
|
|
43
|
+
puma (8.0.2)
|
|
44
|
+
nio4r (~> 2.0)
|
|
45
|
+
rack (3.2.7)
|
|
46
|
+
rack-protection (4.2.1)
|
|
47
|
+
base64 (>= 0.1.0)
|
|
48
|
+
logger (>= 1.6.0)
|
|
49
|
+
rack (>= 3.0.0, < 4)
|
|
50
|
+
rack-session (2.1.2)
|
|
51
|
+
base64 (>= 0.1.0)
|
|
52
|
+
rack (>= 3.0.0)
|
|
53
|
+
rackup (2.3.1)
|
|
54
|
+
rack (>= 3)
|
|
55
|
+
rbs (4.2.0)
|
|
56
|
+
logger
|
|
57
|
+
prism (>= 1.6.0)
|
|
58
|
+
tsort
|
|
59
|
+
rbtree (0.4.7)
|
|
60
|
+
rdoc (8.1.0)
|
|
61
|
+
erb
|
|
62
|
+
prism (>= 1.6.0)
|
|
63
|
+
rbs (>= 4.0.0)
|
|
64
|
+
tsort
|
|
65
|
+
reline (0.7.0)
|
|
66
|
+
io-console (~> 0.5)
|
|
67
|
+
sinatra (4.2.1)
|
|
68
|
+
logger (>= 1.6.0)
|
|
69
|
+
mustermann (~> 3.0)
|
|
70
|
+
rack (>= 3.0.0, < 4)
|
|
71
|
+
rack-protection (= 4.2.1)
|
|
72
|
+
rack-session (>= 2.0.0, < 3)
|
|
73
|
+
tilt (~> 2.0)
|
|
74
|
+
sorted_set (1.1.0)
|
|
75
|
+
rbtree
|
|
76
|
+
sqlite3 (2.10.2-arm64-darwin)
|
|
77
|
+
tilt (2.9.0)
|
|
78
|
+
tsort (0.2.0)
|
|
79
|
+
|
|
80
|
+
PLATFORMS
|
|
81
|
+
arm64-darwin-24
|
|
82
|
+
|
|
83
|
+
DEPENDENCIES
|
|
84
|
+
authproto!
|
|
85
|
+
dotenv
|
|
86
|
+
irb
|
|
87
|
+
minisky
|
|
88
|
+
puma
|
|
89
|
+
rackup
|
|
90
|
+
sinatra
|
|
91
|
+
sqlite3
|
|
92
|
+
|
|
93
|
+
CHECKSUMS
|
|
94
|
+
authproto (0.2.5)
|
|
95
|
+
base58 (0.2.3) sha256=fda5c5d3bda8b429ad0d1a5309c2d1e488ba46f0a2ab99a639890f26c61bf3be
|
|
96
|
+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
97
|
+
bundler (4.0.23) sha256=407ba8a1d928f412847f819aa5f8b6aef7c59fb51feb517112af8a165487287b
|
|
98
|
+
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
|
|
99
|
+
didkit (0.5.0) sha256=8b24320fbf137cd4095b7fc7cc8a1dbee9b1aafa49fa80b9aac2902f4c282463
|
|
100
|
+
dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d
|
|
101
|
+
erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
|
|
102
|
+
immutable-ruby (0.2.0) sha256=1ab896e0d9c8c3fe21ddc3dd29be87511da2f6b0970c5a1c36838a9a4a4605e7
|
|
103
|
+
io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
|
|
104
|
+
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
105
|
+
jose (1.2.0) sha256=c6e7061b71d2efc5aa70875c203f10595c67a0a48a58012527844c045ac2cbee
|
|
106
|
+
jwt (3.3.0) sha256=44cc34fbc341c148233cf66641522b4bce0dfb1ce55e8073f05c56f44f4ee546
|
|
107
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
108
|
+
minisky (0.5.1) sha256=47a7d63a1ca16c786e3bd3d4ddb15c5d585165efd1dfbe2be9bf47335fe19d4f
|
|
109
|
+
mustermann (3.1.1) sha256=4c6170c7234d5499c345562ba7c7dfe73e1754286dcc1abb053064d66a127198
|
|
110
|
+
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
|
|
111
|
+
pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
|
|
112
|
+
prettyprint (0.2.1) sha256=d555d57ea6d7cf001d4774b693546a6bd88bec1e778ddffb5c60b68ceec4b7d4
|
|
113
|
+
prism (2.0.0) sha256=fd551446f0c6ca71455ef8d1d40bd2ca1538044834fa22df8837e5095f3a5a39
|
|
114
|
+
prism (2.0.0-arm64-darwin) sha256=edebbfcc16f6a879f47dac9501b20a7cd95abe96397bb485106d6670541abeb1
|
|
115
|
+
puma (8.0.2) sha256=c8ed871dfbbe66448ea9ffd46692342d9804d4071522b52b5331b7b6e7b686fb
|
|
116
|
+
rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7
|
|
117
|
+
rack-protection (4.2.1) sha256=cf6e2842df8c55f5e4d1a4be015e603e19e9bc3a7178bae58949ccbb58558bac
|
|
118
|
+
rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
|
|
119
|
+
rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
|
|
120
|
+
rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
|
|
121
|
+
rbtree (0.4.7) sha256=1efabbcb3fd5f12249c9c8a610a765074868164eed0c50c9db2531c00ed161cb
|
|
122
|
+
rdoc (8.1.0) sha256=befc37880a5cfd36dcb176c748b5fd5ee39dd3536a75e81824e4972ce230f60a
|
|
123
|
+
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
|
|
124
|
+
sinatra (4.2.1) sha256=b7aeb9b11d046b552972ade834f1f9be98b185fa8444480688e3627625377080
|
|
125
|
+
sorted_set (1.1.0) sha256=bac01eb16b6bcfcdb7d732a28e87422be8b5c9fdcfeadbe9072682407524ee83
|
|
126
|
+
sqlite3 (2.10.2-arm64-darwin) sha256=5b0d235ea037e6dcd1b8387a19d73be571917db9f93044d35741cf933a21a37d
|
|
127
|
+
tilt (2.9.0) sha256=da5735d0280bba96e9a91041bb14aee435ccad5c17b0fa519249ae543d9aa3a5
|
|
128
|
+
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
129
|
+
|
|
130
|
+
BUNDLED WITH
|
|
131
|
+
4.0.23
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Minisky OAuth Console Example
|
|
2
|
+
|
|
3
|
+
This is an example showing how to do an (experimental) OAuth client based on Minisky from the `irb` level, both locally and against the client metadata hosted on a production server. Both configurations are *public* clients.
|
|
4
|
+
|
|
5
|
+
Note: both variants receive and save the access tokens locally on your machine, and in both you perform requests using the received token locally in `irb`. The production server code only serves the JSON file and prints the callback params, it does not use the `AuthProto` library itself.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- Ruby 3.3+
|
|
11
|
+
- Bundler
|
|
12
|
+
- A domain name for your application that matches your client metadata (required for production)
|
|
13
|
+
- SSL certificate for your domain (required for production)
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
1. Clone the repository and navigate to the example directory:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd examples/minisky_console
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Install dependencies:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bundle install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. Generate an encryption key:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
openssl rand -base64 32
|
|
33
|
+
|
|
34
|
+
cat > .env
|
|
35
|
+
ATPROTO_MASTER_KEY=<value from above>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## Localhost use
|
|
40
|
+
|
|
41
|
+
Run `bin/console`, then:
|
|
42
|
+
|
|
43
|
+
```rb
|
|
44
|
+
require_relative 'localhost'
|
|
45
|
+
|
|
46
|
+
client = create_client('your.pds.hostname.here')
|
|
47
|
+
authorize(client)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This will print:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Open this URL in the browser and complete authorization:
|
|
54
|
+
https://....
|
|
55
|
+
|
|
56
|
+
Waiting for OAuth callback at http://127.0.0.1:9292/callback (Ctrl-C to cancel)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Open this URL and authorize the app. The example starts a temporary inline HTTP server on the specified port on localhost, which only receives the callback and calls the provided block, which completes the authorization.
|
|
60
|
+
|
|
61
|
+
Once the browser redirects to the callback URL after your approval, the console should print:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Authentication complete ✓
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then, you can run `client.get_request(...)` and `client.post_request(...)` to perform authorized actions on the given account. By default, the example asks for permissions `repo:app.bsky.feed.post` and `rpc:app.bsky.feed.getTimeline?aud=*`, which allow calling `getTimeline` and creating/modifying posts.
|
|
68
|
+
|
|
69
|
+
(Note, the session is currently lost when you close the console.)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Production use
|
|
73
|
+
|
|
74
|
+
Deploy the code in `examples/minisky_console` to a production server using a method of your choice. The relevant part is in the `app.rb` file.
|
|
75
|
+
|
|
76
|
+
Create an `.env` file in the app folder with:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
SERVER_HOST=your.server.hostname
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Locally, run `bin/console`, then:
|
|
83
|
+
|
|
84
|
+
```rb
|
|
85
|
+
require_relative 'production'
|
|
86
|
+
|
|
87
|
+
client = create_client('your.server.hostname', 'your.pds.hostname')
|
|
88
|
+
authorize(client)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This will print:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Open this URL in the browser and complete authorization:
|
|
95
|
+
https://....
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Open this URL and authorize the app. Once the browser redirects to the callback URL after your approval, the page you get redirected to will print the received params.
|
|
99
|
+
|
|
100
|
+
Copy the printed command back to the console:
|
|
101
|
+
|
|
102
|
+
```rb
|
|
103
|
+
handle_callback(client, { ... })
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
This will print:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Authentication complete ✓
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then, you can run `client.get_request(...)` and `client.post_request(...)` to perform authorized actions on the given account.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dotenv/load'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'sinatra'
|
|
6
|
+
|
|
7
|
+
$server_host = ENV.fetch('SERVER_HOST')
|
|
8
|
+
|
|
9
|
+
$metadata = {
|
|
10
|
+
'application_type' => 'native',
|
|
11
|
+
'client_id' => "https://#{$server_host}/oauth-client-metadata.json",
|
|
12
|
+
'client_name' => 'Minisky OAuth Example App',
|
|
13
|
+
'redirect_uris' => ["https://#{$server_host}/callback"],
|
|
14
|
+
'grant_types' => ['authorization_code', 'refresh_token'],
|
|
15
|
+
'response_types' => ['code'],
|
|
16
|
+
'scope' => 'atproto repo:app.bsky.feed.post rpc:app.bsky.feed.getTimeline?aud=*',
|
|
17
|
+
'token_endpoint_auth_method' => 'none',
|
|
18
|
+
'dpop_bound_access_tokens' => true,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get "/" do
|
|
22
|
+
content_type 'text/plain'
|
|
23
|
+
|
|
24
|
+
"To start authentication, run this in bin/console:\n\n" +
|
|
25
|
+
"require_relative 'production'\n" +
|
|
26
|
+
"client = create_client('#{$server_host}', 'target.pds.hostname')\n" +
|
|
27
|
+
"authorize(client)\n\n" +
|
|
28
|
+
"Then open the printed URL and authorize the app. You will receive a hash with data, which you need to paste to:\n\n" +
|
|
29
|
+
"handle_callback(client, { ... })\n"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
get "/oauth-client-metadata.json" do
|
|
33
|
+
content_type :json
|
|
34
|
+
|
|
35
|
+
JSON.generate($metadata) + "\n"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
get "/callback" do
|
|
39
|
+
content_type 'text/plain'
|
|
40
|
+
|
|
41
|
+
if params[:error]
|
|
42
|
+
params.slice(:error, :error_description).to_json
|
|
43
|
+
elsif params[:code] && params[:state] && params[:iss]
|
|
44
|
+
"To complete authentication, run this in the console where you started the authentication flow " +
|
|
45
|
+
"(replace \"client\" with the local variable name you've used):\n\n" +
|
|
46
|
+
"handle_callback(client, #{params.slice(:code, :state, :iss).map { |k, v| [k.to_sym, v] }.to_h.inspect})\n"
|
|
47
|
+
else
|
|
48
|
+
"Missing params"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'authproto/minisky'
|
|
2
|
+
require 'dotenv/load'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
AuthProto.configure do |config|
|
|
6
|
+
config.http_client = AuthProto::InsecureHTTPClient.new(timeout: 10)
|
|
7
|
+
|
|
8
|
+
config.storage = AuthProto::Storage::SQLite.new(
|
|
9
|
+
path: File.join(__dir__, "db", "localhost_auth.sqlite3")
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
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
|
|
31
|
+
|
|
32
|
+
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
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def authorize(client)
|
|
41
|
+
auth = client.authorize(pds_url: "https://#{client.host}", scope: $metadata['scope'])
|
|
42
|
+
|
|
43
|
+
puts "Open this URL in the browser and complete authorization:"
|
|
44
|
+
puts auth[:url]
|
|
45
|
+
puts
|
|
46
|
+
|
|
47
|
+
AuthProto::LocalhostServer.start($metadata) do |json|
|
|
48
|
+
client.handle_callback(**json)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts "Authentication complete ✓"
|
|
52
|
+
puts "Your session id: #{auth[:session_id]}"
|
|
53
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'authproto/minisky'
|
|
2
|
+
require 'dotenv/load'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
AuthProto.configure do |config|
|
|
6
|
+
config.http_client = AuthProto::HTTPClient.new(timeout: 10)
|
|
7
|
+
|
|
8
|
+
config.storage = AuthProto::Storage::SQLite.new(
|
|
9
|
+
path: File.join(__dir__, "db", "production_auth.sqlite3")
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create_client(server_hostname, pds)
|
|
14
|
+
Minisky::OAuthClient.new(pds,
|
|
15
|
+
client_id: "https://#{server_hostname}/oauth-client-metadata.json",
|
|
16
|
+
redirect_uri: "https://#{server_hostname}/callback"
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def authorize(client)
|
|
21
|
+
auth = client.authorize(pds_url: "https://#{client.host}", scope: client.oauth_client.client_metadata.scope)
|
|
22
|
+
|
|
23
|
+
puts "Open this URL in the browser and complete authorization:"
|
|
24
|
+
puts auth[:url]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def handle_callback(client, json)
|
|
28
|
+
result = client.handle_callback(**json)
|
|
29
|
+
|
|
30
|
+
puts "Authentication complete ✓"
|
|
31
|
+
puts "Your session id: #{result[:session_id]}"
|
|
32
|
+
end
|