hanko-ruby 0.1.4 → 0.1.5

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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +21 -0
  3. data/README.md +325 -0
  4. data/lib/hanko/version.rb +1 -1
  5. metadata +5 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d356c77086f8405e77f5d44376ba9ec93a84888d0a34205c682c4861134ebdf2
4
- data.tar.gz: 2f4863a050500f0caede6de8d9e4bd74a10314eeb4c482046368279225520af3
3
+ metadata.gz: 67831cf7bac615f17229e1d326efc82c4cfc46ae431b95cbc085069e63b8f4cf
4
+ data.tar.gz: 1b409320ba0a9db76e4076b193deb145c42f92b49669e13d8735464bb27df425
5
5
  SHA512:
6
- metadata.gz: 749becb96adbd06cb8e57e9b63227452459535b92d2637595e5f8772e15af87acbe0ed3dabac9c616f525e8a0377fd0a29b12ffddcaf5bb360c78fbe9700c5bc
7
- data.tar.gz: dbf7dedd41d916df414b8239898d48c1f4534301ec920410006bfbc6a40f4745951bb1c3d585cd1f1e334ac9905d0499a752354b0900f4466ac5225c6d8c1cde
6
+ metadata.gz: c36b02e8c618559ee176c9ef4953bfb88e980cdf705da5f304d7f70a452a1bb59f42829f5c68c31afd7811531e9f4b195b54f1bd386ee6a4f2cdf967cd84d2e3
7
+ data.tar.gz: '0180da35d71c1991b35f3efa776a77e2a58728b9beee0ab95298e01c479d62605d779602b8b0acb6b553a0b23ac833b185055bca7d0f1020499e28aa39ac46a2'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Fernando Ruiz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,325 @@
1
+ # hanko-ruby
2
+
3
+ [![CI](https://github.com/fruizg0302/hanko-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/fruizg0302/hanko-ruby/actions)
4
+ [![Gem Version](https://badge.fury.io/rb/hanko-ruby.svg)](https://rubygems.org/gems/hanko-ruby)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Framework-agnostic Ruby SDK for the [Hanko](https://www.hanko.io/) authentication platform. Verify sessions, manage users via the Admin API, drive login/registration flows, and handle webhooks.
8
+
9
+ > **Looking for Rails integration?** See [hanko-rails](https://rubygems.org/gems/hanko-rails) for middleware, controller helpers, and generators.
10
+
11
+ ## Installation
12
+
13
+ ```ruby
14
+ # Gemfile
15
+ gem 'hanko-ruby'
16
+ ```
17
+
18
+ ```sh
19
+ bundle install
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ Verify a Hanko session token in three lines:
25
+
26
+ ```ruby
27
+ require 'hanko'
28
+
29
+ Hanko.configure do |config|
30
+ config.api_url = 'https://your-instance.hanko.io'
31
+ end
32
+
33
+ client = Hanko::Client.new
34
+ result = client.public.sessions.validate_token(session_token)
35
+ puts result['user_id']
36
+ ```
37
+
38
+ ## Configuration
39
+
40
+ ### Global configuration
41
+
42
+ Set defaults that apply to every `Hanko::Client` instance:
43
+
44
+ ```ruby
45
+ Hanko.configure do |config|
46
+ config.api_url = ENV.fetch('HANKO_API_URL')
47
+ config.api_key = ENV.fetch('HANKO_API_KEY') # required for Admin API
48
+ config.timeout = 5 # request timeout in seconds (default: 5)
49
+ config.open_timeout = 2 # connection open timeout (default: 2)
50
+ config.retry_count = 1 # number of retries (default: 1)
51
+ config.clock_skew = 0 # allowed JWT clock skew in seconds (default: 0)
52
+ config.jwks_cache_ttl = 3600 # JWKS cache lifetime in seconds (default: 3600)
53
+ config.logger = Logger.new($stdout)
54
+ config.log_level = :info # default: :info
55
+ end
56
+ ```
57
+
58
+ ### Per-client overrides
59
+
60
+ Override any setting on a specific client instance:
61
+
62
+ ```ruby
63
+ admin_client = Hanko::Client.new(
64
+ api_url: 'https://your-instance.hanko.io',
65
+ api_key: ENV.fetch('HANKO_API_KEY'),
66
+ timeout: 10
67
+ )
68
+ ```
69
+
70
+ Global and per-client settings can be mixed; per-client values take precedence.
71
+
72
+ ## Session Verification
73
+
74
+ ### Via the Public API
75
+
76
+ ```ruby
77
+ client = Hanko::Client.new
78
+
79
+ # Validate using a session token string
80
+ result = client.public.sessions.validate_token(session_token)
81
+
82
+ # Validate using cookie/header (server-side forwarding)
83
+ result = client.public.sessions.validate
84
+ ```
85
+
86
+ ### Via WebhookVerifier (standalone)
87
+
88
+ For lightweight verification without a full client:
89
+
90
+ ```ruby
91
+ payload = Hanko::WebhookVerifier.verify(
92
+ token,
93
+ jwks_url: 'https://your-instance.hanko.io/.well-known/jwks.json'
94
+ )
95
+
96
+ puts payload['sub'] # user ID
97
+ ```
98
+
99
+ The verifier pins to RS256 and raises `Hanko::InvalidTokenError` or `Hanko::ExpiredTokenError` on failure.
100
+
101
+ ## Admin API
102
+
103
+ The Admin API requires an API key. All responses are wrapped in `Hanko::Resource` objects that support both hash-style (`resource['field']`) and method-style (`resource.field`) access.
104
+
105
+ ### Users
106
+
107
+ ```ruby
108
+ client = Hanko::Client.new
109
+
110
+ # List users
111
+ users = client.admin.users.list
112
+ users.each { |u| puts u.id }
113
+
114
+ # Get a specific user
115
+ user = client.admin.users.get('user-uuid')
116
+
117
+ # Create a user
118
+ user = client.admin.users.create(email: 'alice@example.com')
119
+
120
+ # Delete a user
121
+ client.admin.users.delete('user-uuid')
122
+ ```
123
+
124
+ ### User-scoped resources
125
+
126
+ Access emails, passwords, sessions, WebAuthn credentials, and metadata through a user context:
127
+
128
+ ```ruby
129
+ user_ctx = client.admin.users('user-uuid')
130
+
131
+ # Emails
132
+ emails = user_ctx.emails.list
133
+ user_ctx.emails.create(address: 'new@example.com')
134
+ user_ctx.emails.make_primary('email-uuid')
135
+ user_ctx.emails.delete('email-uuid')
136
+
137
+ # Passwords
138
+ user_ctx.passwords.create(password: 'new-password')
139
+ user_ctx.passwords.get
140
+ user_ctx.passwords.update(password: 'updated-password')
141
+ user_ctx.passwords.delete
142
+
143
+ # Sessions
144
+ sessions = user_ctx.sessions.list
145
+ user_ctx.sessions.delete('session-uuid')
146
+
147
+ # WebAuthn credentials
148
+ creds = user_ctx.webauthn_credentials.list
149
+ user_ctx.webauthn_credentials.delete('credential-uuid')
150
+
151
+ # Metadata
152
+ meta = user_ctx.metadata.get
153
+ user_ctx.metadata.update(custom_key: 'custom_value')
154
+ ```
155
+
156
+ ### Webhooks
157
+
158
+ ```ruby
159
+ # List webhooks
160
+ webhooks = client.admin.webhooks.list
161
+
162
+ # Create a webhook
163
+ webhook = client.admin.webhooks.create(
164
+ callback: 'https://example.com/webhooks/hanko',
165
+ events: ['user.create', 'user.delete']
166
+ )
167
+
168
+ # Delete a webhook
169
+ client.admin.webhooks.delete('webhook-uuid')
170
+ ```
171
+
172
+ ### Audit Logs
173
+
174
+ ```ruby
175
+ logs = client.admin.audit_logs.list
176
+ logs.each { |log| puts "#{log.type} at #{log.created_at}" }
177
+ ```
178
+
179
+ ## Flow API
180
+
181
+ Drive Hanko login and registration flows server-side. Returns `Hanko::FlowResponse` objects.
182
+
183
+ ```ruby
184
+ client = Hanko::Client.new
185
+
186
+ # Start a login flow
187
+ response = client.public.flow.login
188
+ puts response.status # :completed, :error, etc.
189
+ puts response.actions # available next actions
190
+
191
+ # Start a registration flow
192
+ response = client.public.flow.registration
193
+
194
+ # Profile flow
195
+ response = client.public.flow.profile
196
+
197
+ # Check flow state
198
+ if response.completed?
199
+ puts "User ID: #{response.user_id}"
200
+ puts "Session token: #{response.session_token}"
201
+ elsif response.error?
202
+ puts "Flow failed"
203
+ end
204
+ ```
205
+
206
+ ## Well-Known Endpoints
207
+
208
+ ```ruby
209
+ client = Hanko::Client.new
210
+
211
+ # Fetch JWKS
212
+ jwks = client.public.well_known.jwks
213
+
214
+ # Fetch Hanko configuration
215
+ config = client.public.well_known.config
216
+ ```
217
+
218
+ ## Webhook Verification
219
+
220
+ Verify incoming webhook payloads from Hanko:
221
+
222
+ ```ruby
223
+ token = request.headers['X-Hanko-Webhook-Token']
224
+ payload = Hanko::WebhookVerifier.verify(
225
+ token,
226
+ jwks_url: "#{ENV.fetch('HANKO_API_URL')}/.well-known/jwks.json"
227
+ )
228
+
229
+ case payload['evt']
230
+ when 'user.create'
231
+ User.create!(hanko_id: payload['sub'])
232
+ when 'user.delete'
233
+ User.find_by(hanko_id: payload['sub'])&.destroy
234
+ end
235
+ ```
236
+
237
+ ## Testing
238
+
239
+ The test helper is **opt-in** — require it explicitly:
240
+
241
+ ```ruby
242
+ require 'hanko/test_helper'
243
+ ```
244
+
245
+ ### Available helpers
246
+
247
+ ```ruby
248
+ # Generate a signed JWT for testing
249
+ token = Hanko::TestHelper.generate_test_token(
250
+ sub: 'user-uuid',
251
+ exp: Time.now.to_i + 3600
252
+ )
253
+
254
+ # Get a JWKS response body (matches the test signing key)
255
+ jwks_json = Hanko::TestHelper.test_jwks_response
256
+
257
+ # Stub the JWKS endpoint (requires WebMock)
258
+ Hanko::TestHelper.stub_jwks(api_url: 'https://your-instance.hanko.io')
259
+
260
+ # Create a stub verifier (no HTTP calls)
261
+ verifier = Hanko::TestHelper.stub_session(
262
+ sub: 'user-uuid',
263
+ exp: Time.now.to_i + 3600
264
+ )
265
+ payload = verifier.verify('any-token')
266
+ puts payload['sub'] # => 'user-uuid'
267
+ ```
268
+
269
+ ## Error Handling
270
+
271
+ All errors inherit from `Hanko::Error`:
272
+
273
+ ```
274
+ Hanko::Error
275
+ ├── Hanko::ConfigurationError # missing or invalid configuration
276
+ ├── Hanko::InvalidTokenError # JWT signature invalid or malformed
277
+ ├── Hanko::ExpiredTokenError # JWT has expired
278
+ ├── Hanko::JwksError # failed to fetch or parse JWKS
279
+ ├── Hanko::ConnectionError # network-level failure
280
+ └── Hanko::ApiError # HTTP 4xx/5xx response
281
+ ├── Hanko::AuthenticationError # 401 Unauthorized
282
+ ├── Hanko::NotFoundError # 404 Not Found
283
+ └── Hanko::RateLimitError # 429 Too Many Requests
284
+ ```
285
+
286
+ ```ruby
287
+ begin
288
+ client.admin.users.get('nonexistent-uuid')
289
+ rescue Hanko::NotFoundError => e
290
+ puts "User not found (HTTP #{e.status})"
291
+ rescue Hanko::RateLimitError => e
292
+ puts "Rate limited. Retry after #{e.retry_after} seconds"
293
+ rescue Hanko::AuthenticationError
294
+ puts "Invalid API key"
295
+ rescue Hanko::ApiError => e
296
+ puts "API error: #{e.message} (HTTP #{e.status})"
297
+ rescue Hanko::Error => e
298
+ puts "Hanko error: #{e.message}"
299
+ end
300
+ ```
301
+
302
+ ## Security
303
+
304
+ - **Algorithm pinning** — All JWT verification is pinned to RS256. No algorithm negotiation or `none` algorithm is accepted.
305
+ - **Credential redaction** — `Hanko::Client#inspect` and `Hanko::Configuration#inspect` redact the API key so credentials are never leaked to logs.
306
+ - **JWKS trust** — The SDK only fetches JWKS from the configured `api_url` domain. Webhook verification requires an explicit `jwks_url` parameter.
307
+
308
+ ## Requirements
309
+
310
+ - Ruby >= 3.1
311
+
312
+ ## Contributing
313
+
314
+ 1. Fork the repository
315
+ 2. Create your feature branch (`git checkout -b feature/my-feature`)
316
+ 3. Write tests for your changes
317
+ 4. Ensure all tests pass: `bundle exec rspec`
318
+ 5. Ensure linting passes: `bundle exec rubocop`
319
+ 6. Commit your changes (`git commit -m 'Add my feature'`)
320
+ 7. Push to the branch (`git push origin feature/my-feature`)
321
+ 8. Open a Pull Request
322
+
323
+ ## License
324
+
325
+ Released under the [MIT License](LICENSE.txt).
data/lib/hanko/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hanko
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanko-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fernando Ruiz
@@ -59,6 +59,8 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - LICENSE.txt
63
+ - README.md
62
64
  - lib/hanko.rb
63
65
  - lib/hanko/api/admin.rb
64
66
  - lib/hanko/api/admin/audit_logs.rb
@@ -91,6 +93,8 @@ metadata:
91
93
  homepage_uri: https://github.com/fruizg0302/hanko-ruby
92
94
  source_code_uri: https://github.com/fruizg0302/hanko-ruby/tree/main/hanko
93
95
  changelog_uri: https://github.com/fruizg0302/hanko-ruby/blob/main/CHANGELOG.md
96
+ bug_tracker_uri: https://github.com/fruizg0302/hanko-ruby/issues
97
+ documentation_uri: https://rubydoc.info/gems/hanko-ruby
94
98
  rubygems_mfa_required: 'true'
95
99
  rdoc_options: []
96
100
  require_paths: