passkeys-rails 0.3.0 → 0.3.2
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 +4 -4
- data/CHANGELOG.md +40 -28
- data/README.md +141 -106
- data/app/controllers/passkeys_rails/passkeys_controller.rb +12 -4
- data/app/interactors/passkeys_rails/begin_challenge.rb +2 -2
- data/app/interactors/passkeys_rails/begin_registration.rb +2 -0
- data/app/interactors/passkeys_rails/finish_registration.rb +6 -2
- data/lib/generators/passkeys_rails/templates/passkeys_rails_config.rb +41 -0
- data/lib/passkeys-rails.rb +25 -42
- data/lib/passkeys_rails/configuration.rb +62 -0
- data/lib/passkeys_rails/version.rb +1 -1
- metadata +105 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8aeb76eb5817dbc1b1fac8a6205b5154772bd04d91f45596952b9b10e319f997
|
4
|
+
data.tar.gz: 0f3eb68558500c17ea980a1a1e0b00db8ddf13dfead9ef8568fb8a8d295f96ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f136f27de81f280d9d8e55e96b2f7c4729ded92a7b9c0339f0c766d0b8604732de430dd5f6e082ed700dca9422cc3c1af975dfae74aa267757d955b3bc15733
|
7
|
+
data.tar.gz: 92587364bdb4f41aa35dfaeef432bab42e77d6dd4a8e49b6797b12bd2a42f4ef0ecd9ed6dca5575abf3e068c0005c998d863f3eb7b1443eb4d01ac2271f73205
|
data/CHANGELOG.md
CHANGED
@@ -1,53 +1,65 @@
|
|
1
|
-
### 0.3.
|
1
|
+
### 0.3.2 (2024/10/01)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
* Added notifications to certain controller actions.
|
6
|
-
* Improved spec error helper.
|
3
|
+
- Changed from unsigned cookies to short lived signed cookies
|
4
|
+
- Added RELEASING.md
|
7
5
|
|
8
|
-
### 0.
|
6
|
+
### 0.3.1 (2023/11/30)
|
7
|
+
|
8
|
+
- Fixed a bug in reading session/cookie variables
|
9
|
+
- Added webauthn configuration parameters to this gem's configuration
|
10
|
+
- Moved configuration to its own class
|
11
|
+
- Added more info to the README
|
12
|
+
|
13
|
+
### 0.3.0 (2023/08/01)
|
14
|
+
|
15
|
+
- Added debug_register endpoint.
|
16
|
+
- Fixed authenticatable_params for register enpoint.
|
17
|
+
- Added notifications to certain controller actions.
|
18
|
+
- Improved spec error helper.
|
19
|
+
|
20
|
+
### 0.2.1 (2023/07/29)
|
9
21
|
|
10
22
|
Added ability to pass either the auth token string or a request with one in the header to authenticate methods.
|
11
23
|
|
12
|
-
### 0.2.0
|
24
|
+
### 0.2.0 (2023/07/28)
|
13
25
|
|
14
|
-
|
26
|
+
- Added passkeys/debug_login functionality.
|
15
27
|
|
16
|
-
### 0.1.7
|
28
|
+
### 0.1.7 (2023/07/26)
|
17
29
|
|
18
|
-
|
19
|
-
|
20
|
-
|
30
|
+
- Added IntegrationHelpers to support client testing.
|
31
|
+
- Updated methods for interfacing with Rails client app.
|
32
|
+
- Changed route path added by the generator.
|
21
33
|
|
22
|
-
### 0.1.6
|
34
|
+
### 0.1.6 (2023/07/26)
|
23
35
|
|
24
|
-
|
36
|
+
- Added default_class and class_whitelist config parameters.
|
25
37
|
|
26
|
-
### 0.1.5
|
38
|
+
### 0.1.5 (2023/07/24)
|
27
39
|
|
28
|
-
|
40
|
+
- Updated validation to ensure the agent has completed registration to be considered valid.
|
29
41
|
|
30
|
-
### 0.1.4
|
42
|
+
### 0.1.4 (2023/07/23)
|
31
43
|
|
32
|
-
|
44
|
+
- Changed namespace from Passkeys::Rails to PasskeysRails
|
33
45
|
|
34
|
-
### 0.1.3
|
46
|
+
### 0.1.3 (2023/07/23)
|
35
47
|
|
36
|
-
|
48
|
+
- More restructuring and fixed issue where autoloading failed
|
37
49
|
during client app initialization.
|
38
50
|
|
39
|
-
### 0.1.2
|
51
|
+
### 0.1.2 (2023/07/23)
|
40
52
|
|
41
|
-
|
53
|
+
- Restructured lib directory.
|
42
54
|
|
43
|
-
|
55
|
+
- Fixed naming convention for gem/gemspec.
|
44
56
|
|
45
|
-
|
57
|
+
- Fixed exception handling.
|
46
58
|
|
47
|
-
### 0.1.1
|
59
|
+
### 0.1.1 (2023/07/23)
|
48
60
|
|
49
|
-
|
61
|
+
- Fixed dependency
|
50
62
|
|
51
|
-
### 0.1.0
|
63
|
+
### 0.1.0 (2023/07/23)
|
52
64
|
|
53
|
-
|
65
|
+
- Initial release - looking for feedback
|
data/README.md
CHANGED
@@ -1,42 +1,60 @@
|
|
1
|
+
# PasskeysRails - easy to integrate back end for implementing mobile passkeys
|
2
|
+
|
1
3
|
[](https://badge.fury.io/rb/passkeys-rails)
|
2
4
|
[](https://travis-ci.org/alliedcode/passkeys-rails)
|
3
5
|
[](https://codecov.io/gh/alliedcode/passkeys-rails)
|
4
6
|
|
5
|
-
|
7
|
+
<p align="center" >
|
8
|
+
Created by <b>Troy Anderson, Allied Code</b> - <a href="https://alliedcode.com">alliedcode.com</a>
|
9
|
+
</p>
|
6
10
|
|
7
|
-
|
11
|
+
PasskeysRails is a gem you can add to a Rails app to enable passskey registration and authorization from mobile front ends. PasskeysRails leverages webauthn for the cryptographic work, and presents a simple API interface for passkey registration, authentication, and testing.
|
8
12
|
|
9
13
|
The purpose of this gem is to make it easy to provide a rails back end API that supports PassKey authentication. It uses the [`webauthn`](https://github.com/w3c/webauthn) gem to do the cryptographic work and presents a simple API interface for passkey registration and authentication.
|
10
14
|
|
11
15
|
The target use case for this gem is a mobile application that uses a rails based API service to manage resources. The goal is to make it simple to register and authenticate users using passkeys from mobile applications in a rails API service.
|
12
16
|
|
17
|
+
What about [devise](https://github.com/heartcombo/devise)? Devise is awesome, but we don't need all that UI/UX for PassKeys, especially for an API back end.
|
18
|
+
|
19
|
+
## Documentation
|
20
|
+
* [Usage](#usage)
|
21
|
+
* [Installation](#installation)
|
22
|
+
* [Rails Integration - Standard](#rails-Integration-standard)
|
23
|
+
* [Rails Integration - Grape](#rails-Integration-grape)
|
24
|
+
* [Notifications](#notifications)
|
25
|
+
* [Failure Codes](#failure-codes)
|
26
|
+
* [Testing](#testing)
|
27
|
+
* [Mobile App Integration](#mobile-application-integration)
|
28
|
+
* [Reference/Example Mobile Applications](#referenceexample-mobile-applications)
|
13
29
|
|
14
30
|
## Usage
|
15
31
|
|
16
|
-
**PasskeysRails** maintains
|
32
|
+
**PasskeysRails** maintains a `PasskeysRails::Agent` model and related `PasskeysRails::Passkeys`. In rails apps that maintain their own "user" model, add `include PasskeysRails::Authenticatable` to that model and include the name of that class (e.g. `"User"`) in the `authenticatable_class` param when calling the register API or set the `PasskeysRails.default_class` to the name of that class.
|
33
|
+
|
34
|
+
In mobile apps, leverage the platform specific Passkeys APIs for ***registration*** and ***authentication***, and call the **PasskeysRails** API endpoints to complete the ceremony. **PasskeysRails** provides endpoints to support ***registration***, ***authentication***, ***token refresh***, and ***debugging***.
|
17
35
|
|
18
36
|
### Optionally providing a **"user"** model during registration
|
19
37
|
|
20
|
-
**PasskeysRails** does not require
|
38
|
+
**PasskeysRails** does not require any application specific models, but it's often useful to have one. For example, a User model can be created at registration. **PasskeysRails** provides two mechanisms to support this. Either provide the name of the model in the `authenticatable_class` param when calling the `finishRegistration` endpoint, or set a `default_class` in `config/initializers/passkeys_rails.rb`.
|
21
39
|
|
22
|
-
**PasskeysRails** supports multiple
|
40
|
+
**PasskeysRails** supports multiple different application specific models. Whatever model name supplied when calling the `finishRegistration` endpoint will be created during a successful the `finishRegiration` process. When created, it will be provided an opportunity to do any initialization at that time.
|
23
41
|
|
24
|
-
There are two **PasskeysRails** configuration options related to this: `default_class` and `class_whitelist
|
42
|
+
There are two **PasskeysRails** configuration options related to this: `default_class` and `class_whitelist`:
|
25
43
|
|
26
44
|
#### `default_class`
|
27
45
|
|
28
|
-
Configure `default_class` in `passkeys_rails.rb`. Its value will be used during registration if none is provided in the API call. The default value is `"User"`. Since the `default_class` is just a default, it can be overridden in the `finishRegiration` API call to use a different model. If no model is to be used by default, set it to nil.
|
46
|
+
Configure `default_class` in `config/initializers/passkeys_rails.rb`. Its value will be used during registration if none is provided in the API call. The default value is `"User"`. Since the `default_class` is just a default, it can be overridden in the `finishRegiration` API call to use a different model. If no model is to be used by default, set it to nil.
|
29
47
|
|
30
48
|
#### `class_whitelist`
|
31
49
|
|
32
|
-
Configure `class_whitelist` in `passkeys_rails.rb`. The default value is `nil`. When `nil`, no whitelist will be applied. If it is non-nil, it should be an array of class names that are allowed during registration. Supply an empty array to prevent **PasskeysRails** from attempting to create anything other than its own `PasskeysRails::Agent` during registration.
|
50
|
+
Configure `class_whitelist` in `config/initializers/passkeys_rails.rb`. The default value is `nil`. When `nil`, no whitelist will be applied. If it is non-nil, it should be an array of class names that are allowed during registration. Supply an empty array to prevent **PasskeysRails** from attempting to create anything other than its own `PasskeysRails::Agent` during registration.
|
33
51
|
|
34
52
|
## Installation
|
35
53
|
|
36
54
|
Add this line to your application's Gemfile:
|
37
55
|
|
38
56
|
```ruby
|
39
|
-
gem "
|
57
|
+
gem "passkeys-rails"
|
40
58
|
```
|
41
59
|
|
42
60
|
And then execute:
|
@@ -58,29 +76,32 @@ $ rails generate passkeys_rails:install
|
|
58
76
|
|
59
77
|
This will add the `config/initializers/passkeys_rails.rb` configuration file, passkeys routes, and a couple of database migrations to your project.
|
60
78
|
|
61
|
-
### Adding to an standard rails project
|
62
79
|
|
63
|
-
|
80
|
+
<a id="rails-Integration-standard"></a>
|
81
|
+
## Rails Integration <p><small>Adding to a standard rails project</small></p>
|
64
82
|
|
65
|
-
|
83
|
+
- ### Add `before_action :authenticate_passkey!`
|
66
84
|
|
67
|
-
|
85
|
+
To prevent access to controller actions, add `before_action :authenticate_passkey!`. If an action is attempted without an authenticated entity, an error will be rendered in JSON with an :unauthorized result code.
|
68
86
|
|
69
|
-
|
87
|
+
- ### Use `current_agent` and `current_agent.authenticatable`
|
70
88
|
|
71
|
-
|
89
|
+
To access the currently authenticated entity, use `current_agent`. If you associated the registration of the agent with one of your own models, use `current_agent.authenticatable`. For example, if you associated the `User` class with the registration, `current_agent.authenticatable` will be a User object.
|
90
|
+
|
91
|
+
- ### Add `include PasskeysRails::Authenticatable` to model class(es)
|
72
92
|
|
73
93
|
If you have one or more classes that you want to use with authentication - e.g. a User class and an AdminUser class - add `include PasskeysRails::Authenticatable` to each of those classes. That adds a `registered?` method that you can call on your model to determine if they are registerd with your service, and a `registering_with(params)` method that you can override to initialize attributes of your model when it is created during registration. `params` is a hash with params passed to the API when registering. When called, your object has been built, but not yet saved. Upon return, **PasskeysRails** will attempt to save your object before finishing registration. If it is not valid, the registration will fail as well, returning the error error details to the caller.
|
74
94
|
|
75
|
-
|
95
|
+
<a id="rails-Integration-grape"></a>
|
96
|
+
## Rails Integration - <p><small>Adding to a Grape API rails project</small></p>
|
76
97
|
|
77
|
-
|
98
|
+
- ### Call `PasskeysRails.authenticate(request)` to authenticate the request.
|
78
99
|
|
79
100
|
Call `PasskeysRails.authenticate(request)` to get an object back that responds to `.success?` and `.failure?` as well as `.agent`, `.code`, and `.message`.
|
80
101
|
|
81
102
|
Alternatively, call `PasskeysRails.authenticate!(request)` from a helper in your base class. It will raise a `PasskeysRails.Error` exception if the caller isn't authenticated. You can catch the exception and render an appropriate error. The exception contains the error code and message.
|
82
103
|
|
83
|
-
|
104
|
+
- ### Consider adding the following helpers to your base API class:
|
84
105
|
|
85
106
|
```ruby
|
86
107
|
helpers do
|
@@ -109,15 +130,17 @@ This will add the `config/initializers/passkeys_rails.rb` configuration file, pa
|
|
109
130
|
|
110
131
|
To prevent access to various endpoints, add `before_action :authenticate_passkey!` or call `authenticate_passkey!` from any method that requires authentication. If an action is attempted without an authenticated entity, an error will be rendered in JSON with an :unauthorized result code.
|
111
132
|
|
112
|
-
|
133
|
+
- ### Use `current_agent` and `current_agent.authenticatable`
|
113
134
|
|
114
135
|
To access the currently authenticated entity, use `current_agent`. If you associated the registration of the agent with one of your own models, use `current_agent.authenticatable`. For example, if you associated the `User` class with the registration, `current_agent.authenticatable` will be a User object.
|
115
136
|
|
116
|
-
|
137
|
+
## Notifications
|
138
|
+
|
139
|
+
Certain actions trigger notifications that can be subscribed. See `subscribe` in `config/initializers/passkeys_rails.rb`.
|
117
140
|
|
118
|
-
|
141
|
+
These are completely optional. **PasskeysRails** will manage all the credentials and keys without these being implemented. They are useful for taking application specific actions like logging based on the authentication related events.
|
119
142
|
|
120
|
-
|
143
|
+
### Events
|
121
144
|
|
122
145
|
- `:did_register ` - a new agent has registered
|
123
146
|
|
@@ -125,7 +148,7 @@ Certain actions trigger notifications that can be subscribed. See `subscribe` i
|
|
125
148
|
|
126
149
|
- `:did_refresh` - an agent's auth token has been refreshed
|
127
150
|
|
128
|
-
A convenient place to set these up in is in `passkeys_rails.rb`
|
151
|
+
A convenient place to set these up in is in `config/initializers/passkeys_rails.rb`
|
129
152
|
|
130
153
|
```ruby
|
131
154
|
PasskeysRails.config do |c|
|
@@ -147,10 +170,9 @@ PasskeysRails.subscribe(:did_register) do |event, agent, request|
|
|
147
170
|
end
|
148
171
|
```
|
149
172
|
|
173
|
+
## Failure Codes
|
150
174
|
|
151
|
-
|
152
|
-
|
153
|
-
1. In the event of authentication failure, PasskeysRails returns an error code and message.
|
175
|
+
1. In the event of authentication failure, **PasskeysRails** API endpoints render an error code and message.
|
154
176
|
|
155
177
|
1. In a standard rails controller, the error code and message are rendered in JSON if `before_action :authenticate_passkey!` fails.
|
156
178
|
|
@@ -166,14 +188,12 @@ end
|
|
166
188
|
- When `.code` is `:expired_token`, `.message` is **The token has expired**, which means that the token is valid, but expired, thuis it's not considered authentic.
|
167
189
|
- When `.code` is `:token_error`, `.message` is a description of the error. This is a catch-all in the event we are unable to decode the token.
|
168
190
|
|
169
|
-
In the future, the intention is to have the `.code` value stay consistent even if the `.message` changes. This also allows you to localize the messages as
|
191
|
+
In the future, the intention is to have the `.code` value stay consistent even if the `.message` changes. This also allows you to localize the messages as needed using the code.
|
170
192
|
|
171
|
-
|
193
|
+
## Testing
|
172
194
|
|
173
195
|
PasskeysRails includes some test helpers for integration tests. In order to use them, you need to include the module in your test cases/specs.
|
174
196
|
|
175
|
-
### Integration tests
|
176
|
-
|
177
197
|
Integration test helpers are available by including the `PasskeysRails::IntegrationHelpers` module.
|
178
198
|
|
179
199
|
```ruby
|
@@ -210,20 +230,38 @@ RSpec.describe 'Posts', type: :request do
|
|
210
230
|
end
|
211
231
|
```
|
212
232
|
|
213
|
-
|
233
|
+
## Mobile Application Integration
|
234
|
+
|
235
|
+
### Prerequisites
|
236
|
+
|
237
|
+
For iOS, you need to associate your app with your server. This amounts to setting up a special file on your server that defines the association. See [setup your apple-app-site-association](#Ensure-`.well-known/apple-app-site-association`-is-in-place)
|
214
238
|
|
215
|
-
|
239
|
+
|
240
|
+
### Mobile API Endpoints
|
241
|
+
|
242
|
+
There are 3 groups of API endpoints that your mobile application might consume.
|
216
243
|
|
217
244
|
1. Unauthenticated (public) endpoints
|
218
245
|
1. Authenticated (private) endpoints
|
219
|
-
1.
|
246
|
+
1. Passkey endpoints (for supporting authentication)
|
220
247
|
|
221
|
-
**Unauthenticated endpoints** can be consumed without
|
248
|
+
**Unauthenticated endpoints** can be consumed without any authentication.
|
222
249
|
|
223
250
|
**Authenticated endpoints** are protected by `authenticate_passkey!` or `PasskeysRails.authenticate!(request)`. Those methods check for and validate the `X-Auth` header, which must be set to the auth token returned in the `AuthResponse`, described below.
|
224
251
|
|
225
252
|
**Passkey endpoints** are supplied by this gem and allow you to register a user, authenticate (login) a user, and refresh the token. This section describes these endpoints.
|
226
253
|
|
254
|
+
This gem supports the Passkey endpoints.
|
255
|
+
|
256
|
+
### Passkey Endpoints
|
257
|
+
|
258
|
+
* [POST /passkeys/challenge](post-passkeys-challenge)
|
259
|
+
* [POST /passkeys/register](post-passkeys-register)
|
260
|
+
* [POST /passkeys/authenticate](post-passkeys-authenticate)
|
261
|
+
* [POST /passkeys/refresh](post-passkeys-refresh)
|
262
|
+
* [POST /passkeys/debug_register](post-passkeys-debug-register)
|
263
|
+
* [POST /passkeys/debug_login](post-passkeys-debug-login)
|
264
|
+
|
227
265
|
All Passkey endpoints accept and respond with JSON.
|
228
266
|
|
229
267
|
On **success**, they will respond with a 200 or 201 response code and relevant JSON.
|
@@ -249,18 +287,24 @@ Some endpoints return an `AuthResponse`, which has this JSON structure:
|
|
249
287
|
}
|
250
288
|
```
|
251
289
|
|
252
|
-
|
290
|
+
### POST /passkeys/challenge
|
253
291
|
|
254
292
|
Submit this to begin registration or authentication.
|
255
293
|
|
256
|
-
|
294
|
+
#### Registration (register)
|
295
|
+
|
296
|
+
To begin registration of a new credential, supply a `{ "username": "unique username" }`.
|
257
297
|
If all goes well, the JSON response will be the `options_for_create` from webauthn.
|
258
298
|
If the username is already in use, or anything else goes wrong, an error with code `validation_errors` will be returned.
|
259
299
|
|
260
|
-
|
261
|
-
|
300
|
+
After receiving a successful response, follow up with a POST to `/passkeys/register`, below.
|
301
|
+
|
302
|
+
#### Authentication (login)
|
303
|
+
To begin authenticating an existing credential, omit the `username`. The JSON response will be the `options_for_get` from webauthn.
|
262
304
|
|
263
|
-
|
305
|
+
After receiving a successful response, follow up with a POST to `/passkeys/authenticate`, below.
|
306
|
+
|
307
|
+
### POST /passkeys/register
|
264
308
|
|
265
309
|
After calling the `challenge` endpoint with a `username`, and handling its response, finish registering by calling this endpoint.
|
266
310
|
|
@@ -291,16 +335,16 @@ On **success**, the response is an `AuthResponse`.
|
|
291
335
|
|
292
336
|
Possible **failure codes** (using the `ErrorResponse` structure) are:
|
293
337
|
|
294
|
-
- webauthn_error - something is wrong with the credential
|
295
|
-
- error - something else went wrong during credentail validation - see the `message` in the `ErrorResponse`
|
296
|
-
- passkey_error - unable to
|
297
|
-
- invalid_authenticatable_class - the supplied authenticatable class can't be created/found (check spelling & capitalization)
|
298
|
-
- invalid_class_whitelist - the whitelist in the passkeys_rails.rb configuration is invalid - be sure it's nil or an array
|
299
|
-
- invalid_authenticatable_class - the supplied authenticatable class is not allowed - maybe it's not in the whitelist
|
300
|
-
- record_invalid - the object of the supplied authenticatable class cannot be saved due to validation errors
|
301
|
-
- agent_not_found - the agent referenced in the credential cannot be found in the database
|
338
|
+
- `webauthn_error` - something is wrong with the credential
|
339
|
+
- `error` - something else went wrong during credentail validation - see the `message` in the `ErrorResponse`
|
340
|
+
- `passkey_error` - unable to persist the passkey
|
341
|
+
- `invalid_authenticatable_class` - the supplied authenticatable class can't be created/found (check spelling & capitalization)
|
342
|
+
- `invalid_class_whitelist` - the whitelist in the passkeys_rails.rb configuration is invalid - be sure it's nil or an array
|
343
|
+
- `invalid_authenticatable_class` - the supplied authenticatable class is not allowed - maybe it's not in the whitelist
|
344
|
+
- `record_invalid` - the object of the supplied authenticatable class cannot be saved due to validation errors
|
345
|
+
- `agent_not_found` - the agent referenced in the credential cannot be found in the database
|
302
346
|
|
303
|
-
|
347
|
+
### POST /passkeys/authenticate
|
304
348
|
|
305
349
|
After calling the `challenge` endpoint without a `username`, and handling its response, finish authenticating by calling this endpoint.
|
306
350
|
|
@@ -325,12 +369,12 @@ On **success**, the response is an `AuthResponse`.
|
|
325
369
|
|
326
370
|
Possible **failure codes** (using the `ErrorResponse` structure) are:
|
327
371
|
|
328
|
-
- webauthn_error - something is wrong with the credential
|
329
|
-
- passkey_not_found - the passkey referenced in the credential cannot be found in the database
|
372
|
+
- `webauthn_error` - something is wrong with the credential
|
373
|
+
- `passkey_not_found` - the passkey referenced in the credential cannot be found in the database
|
330
374
|
|
331
|
-
|
375
|
+
### POST /passkeys/refresh
|
332
376
|
|
333
|
-
The token will expire after some time (configurable in passkeys_rails.rb). Before that happens, refresh it using this API. Once it
|
377
|
+
The token will expire after some time (configurable in `config/initializers/passkeys_rails.rb`). Before that happens, refresh it using this API. Once it expires, to get a new token, use the `/authentication` API.
|
334
378
|
|
335
379
|
Supply the following JSON structure:
|
336
380
|
|
@@ -345,27 +389,27 @@ On **success**, the response is an `AuthResponse` with a new, refreshed token.
|
|
345
389
|
|
346
390
|
Possible **failure codes** (using the `ErrorResponse` structure) are:
|
347
391
|
|
348
|
-
- invalid_token - the token data is invalid
|
349
|
-
- expired_token - the token is expired
|
350
|
-
- token_error - some other error ocurred when decoding the token
|
392
|
+
- `invalid_token` - the token data is invalid
|
393
|
+
- `expired_token` - the token is expired
|
394
|
+
- `token_error` - some other error ocurred when decoding the token
|
351
395
|
|
352
|
-
|
396
|
+
### POST /passkeys/debug_register
|
353
397
|
|
354
|
-
As it may not be possible to acess Passkey functionality in mobile simulators, this endpoint may be called to
|
398
|
+
As it may not be possible to acess Passkey functionality in mobile simulators, this endpoint may be called to register a username while bypassing the normal challenge/response sequence.
|
355
399
|
|
356
|
-
This endpoint only responds if DEBUG_LOGIN_REGEX is set in the server environment. It is very insecure to set this variable in a production environment as it bypasses all Passkey checks. It is only intended to be used during mobile application development.
|
400
|
+
This endpoint only responds if `DEBUG_LOGIN_REGEX` is set in the server environment. It is **very insecure to set this variable in a production environment** as it bypasses all Passkey checks. It is only intended to be used during mobile application development.
|
357
401
|
|
358
402
|
To use this endpoint:
|
359
403
|
|
360
|
-
1.
|
404
|
+
1. Set `DEBUG_LOGIN_REGEX` to a regex that matches any username you want to use during development - for example `^test(-\d+)?$` will match `test`, `test-1`, `test-123`, etc.
|
361
405
|
|
362
|
-
1.
|
406
|
+
1. In the mobile application, call this endpoint in stead of the `/passkeys/challenge` and `/passkeys/register`. The response is identicial to that of `/passkeys/register`.
|
363
407
|
|
364
|
-
1.
|
408
|
+
1. Use the response as if it was from `/passkeys/register`.
|
365
409
|
|
366
|
-
|
410
|
+
If you supply a username that doesn't match the `DEBUG_LOGIN_REGEX`, the endpoint will respond with an error.
|
367
411
|
|
368
|
-
|
412
|
+
Supply the following JSON structure:
|
369
413
|
|
370
414
|
```JSON
|
371
415
|
# POST body
|
@@ -377,71 +421,62 @@ On **success**, the response is an `AuthResponse`.
|
|
377
421
|
|
378
422
|
Possible **failure codes** (using the `ErrorResponse` structure) are:
|
379
423
|
|
380
|
-
- not_allowed - Invalid username (the username doesn't match the regex)
|
381
|
-
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
**TODO**: Point to the soon-to-be-created reference mobile applications for how to use **passkeys-rails** for passkey authentication.
|
386
|
-
|
387
|
-
## Contributing
|
424
|
+
- `not_allowed` - Invalid username (the username doesn't match the regex)
|
425
|
+
- `invalid_authenticatable_class` - the supplied authenticatable class can't be created/found (check spelling & capitalization)
|
426
|
+
- `invalid_class_whitelist` - the whitelist in the passkeys_rails.rb configuration is invalid - be sure it's nil or an array
|
427
|
+
- `invalid_authenticatable_class` - the supplied authenticatable class is not allowed - maybe it's not in the whitelist
|
428
|
+
- `record_invalid` - the object of the supplied authenticatable class cannot be saved due to validation errors
|
388
429
|
|
389
|
-
###
|
430
|
+
### POST /passkeys/debug_login
|
390
431
|
|
391
|
-
|
392
|
-
|
393
|
-
To ensure a smooth collaboration, please follow the guidelines below when submitting your contributions:
|
394
|
-
|
395
|
-
#### Code of Conduct
|
396
|
-
|
397
|
-
Please note that this project follows the [Code of Conduct](https://github.com/alliedcode/passkeys-rails/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. If you encounter any behavior that violates the code, please report it to the project maintainers.
|
398
|
-
|
399
|
-
#### How to Contribute
|
400
|
-
|
401
|
-
1. Fork the repository on GitHub.
|
402
|
-
|
403
|
-
2. Create a new branch for your contribution. Use a descriptive name that reflects the purpose of your changes.
|
404
|
-
|
405
|
-
3. Make your changes and commit them with clear and concise messages. Remember to follow the project's coding style and guidelines.
|
432
|
+
As it may not be possible to acess Passkey functionality in mobile simulators, this endpoint may be called to login (authenticate) a username while bypassing the normal challenge/response sequence.
|
406
433
|
|
407
|
-
|
434
|
+
This endpoint only responds if `DEBUG_LOGIN_REGEX` is set in the server environment. It is **very insecure to set this variable in a production environment** as it bypasses all Passkey checks. It is only intended to be used during mobile application development.
|
408
435
|
|
409
|
-
|
436
|
+
To use this endpoint:
|
410
437
|
|
411
|
-
|
438
|
+
1. Manually create one or more PasskeysRails::Agent records in the database. A unique username is required for each.
|
412
439
|
|
413
|
-
|
440
|
+
1. Set `DEBUG_LOGIN_REGEX` to a regex that matches any username you want to use during development - for example `^test(-\d+)?$` will match `test`, `test-1`, `test-123`, etc.
|
414
441
|
|
415
|
-
|
442
|
+
1. In the mobile application, call this endpoint in stead of the `/passkeys/challenge` and `/passkeys/authenticate`. The response is identicial to that of `/passkeys/authenticate`.
|
416
443
|
|
417
|
-
|
444
|
+
1. Use the response as if it was from `/passkeys/authenticate`.
|
418
445
|
|
419
|
-
|
446
|
+
If you supply a username that doesn't match the `DEBUG_LOGIN_REGEX`, the endpoint will respond with an error.
|
420
447
|
|
421
|
-
|
448
|
+
Supply the following JSON structure:
|
422
449
|
|
423
|
-
|
450
|
+
```JSON
|
451
|
+
# POST body
|
452
|
+
{
|
453
|
+
"username": String
|
454
|
+
}
|
455
|
+
```
|
456
|
+
On **success**, the response is an `AuthResponse`.
|
424
457
|
|
425
|
-
|
458
|
+
Possible **failure codes** (using the `ErrorResponse` structure) are:
|
426
459
|
|
427
|
-
-
|
460
|
+
- `not_allowed` - Invalid username (the username doesn't match the regex)
|
461
|
+
- `agent_not_found` - No agent found with that username
|
428
462
|
|
429
|
-
|
463
|
+
## Reference/Example Mobile Applications
|
430
464
|
|
431
|
-
|
465
|
+
There is a sample iOS app that integrates with **passkeys-rails** based server implementations. It's a great place to get a quick start on implementing passkyes in your iOS, iPadOS or MacOS apps.
|
432
466
|
|
433
|
-
|
467
|
+
Check out the [PasskeysRailsDemo](https://github.com/alliedcode/PasskeysRailsDemo) app.
|
434
468
|
|
435
|
-
|
469
|
+
## Contributing
|
436
470
|
|
437
|
-
|
471
|
+
### Contribution Guidelines
|
438
472
|
|
439
|
-
|
473
|
+
Thank you for considering contributing to PasskeysRails! We welcome your help to improve and enhance this project. Whether it's a bug fix, documentation update, or a new feature, your contributions are valuable to the community.
|
440
474
|
|
441
|
-
|
475
|
+
To ensure a smooth collaboration, please follow the [Contribution Guidelines](https://github.com/alliedcode/passkeys-rails/blob/main/CONTRIBUTION_GUIDELINES.md) when submitting your contributions.
|
442
476
|
|
443
|
-
|
477
|
+
### Code of Conduct
|
444
478
|
|
479
|
+
Please note that this project follows the [Code of Conduct](https://github.com/alliedcode/passkeys-rails/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. If you encounter any behavior that violates the code, please report it to the project maintainers.
|
445
480
|
|
446
481
|
## License
|
447
482
|
|
@@ -7,16 +7,23 @@ module PasskeysRails
|
|
7
7
|
result = PasskeysRails::BeginChallenge.call!(username: challenge_params[:username])
|
8
8
|
|
9
9
|
# Store the challenge so we can verify the future register or authentication request
|
10
|
-
|
10
|
+
cookies.signed[:passkeys_rails] = {
|
11
|
+
value: result.cookie_data.to_json,
|
12
|
+
expire: Time.now.utc + (result.response.timeout / 1000),
|
13
|
+
secure: true,
|
14
|
+
httponly: true,
|
15
|
+
same_site: :strict
|
16
|
+
}
|
11
17
|
|
12
18
|
render json: result.response.as_json
|
13
19
|
end
|
14
20
|
|
15
21
|
def register
|
22
|
+
cookie_data = JSON.parse(cookies.signed["passkeys_rails"] || "{}")
|
16
23
|
result = PasskeysRails::FinishRegistration.call!(credential: attestation_credential_params.to_h,
|
17
24
|
authenticatable_info: authenticatable_params&.to_h,
|
18
|
-
username:
|
19
|
-
challenge:
|
25
|
+
username: cookie_data["username"],
|
26
|
+
challenge: cookie_data["challenge"])
|
20
27
|
|
21
28
|
broadcast(:did_register, agent: result.agent)
|
22
29
|
|
@@ -24,8 +31,9 @@ module PasskeysRails
|
|
24
31
|
end
|
25
32
|
|
26
33
|
def authenticate
|
34
|
+
cookie_data = JSON.parse(cookies.signed["passkeys_rails"] || "{}")
|
27
35
|
result = PasskeysRails::FinishAuthentication.call!(credential: authentication_params.to_h,
|
28
|
-
challenge:
|
36
|
+
challenge: cookie_data["challenge"])
|
29
37
|
|
30
38
|
broadcast(:did_authenticate, agent: result.agent)
|
31
39
|
|
@@ -10,7 +10,7 @@ module PasskeysRails
|
|
10
10
|
options = result.options
|
11
11
|
|
12
12
|
context.response = options
|
13
|
-
context.
|
13
|
+
context.cookie_data = cookie_data(options)
|
14
14
|
rescue Interactor::Failure => e
|
15
15
|
context.fail! code: e.context.code, message: e.context.message
|
16
16
|
end
|
@@ -25,7 +25,7 @@ module PasskeysRails
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
28
|
+
def cookie_data(options)
|
29
29
|
{
|
30
30
|
username:,
|
31
31
|
challenge: WebAuthn.standard_encoder.encode(options.challenge)
|
@@ -13,6 +13,8 @@ module PasskeysRails
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def create_or_replace_unregistered_agent
|
16
|
+
context.fail! code: :origin_error, message: "config.wa_origin must be set" if WebAuthn.configuration.origin.blank?
|
17
|
+
|
16
18
|
Agent.unregistered.where(username:).destroy_all
|
17
19
|
|
18
20
|
agent = Agent.create(username:, webauthn_identifier: WebAuthn.generate_user_id)
|
@@ -28,7 +28,11 @@ module PasskeysRails
|
|
28
28
|
rescue WebAuthn::Error => e
|
29
29
|
context.fail!(code: :webauthn_error, message: e.message)
|
30
30
|
rescue StandardError => e
|
31
|
-
|
31
|
+
if e.message == "undefined method `end_with?' for nil:NilClass"
|
32
|
+
context.fail!(code: :webauthn_error, message: "origin is not set")
|
33
|
+
else
|
34
|
+
context.fail!(code: :error, message: e.message)
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def store_passkey_and_register_agent!
|
@@ -51,7 +55,7 @@ module PasskeysRails
|
|
51
55
|
def agent
|
52
56
|
@agent ||= begin
|
53
57
|
agent = Agent.find_by(username:)
|
54
|
-
context.fail!(code: :agent_not_found, message: "Agent not found for
|
58
|
+
context.fail!(code: :agent_not_found, message: "Agent not found for cookie value: \"#{username}\"") if agent.blank?
|
55
59
|
|
56
60
|
agent
|
57
61
|
end
|
@@ -60,4 +60,45 @@ PasskeysRails.config do |c|
|
|
60
60
|
# c.subscribe(:did_register) do |event, agent, request|
|
61
61
|
# puts("#{event} | #{agent.id} | #{request.headers}")
|
62
62
|
# end
|
63
|
+
|
64
|
+
# PasskeysRails uses webauthn to help with the protocol.
|
65
|
+
# The following settings are passed throught webauthn.
|
66
|
+
# wa_origin is the only one requried
|
67
|
+
|
68
|
+
# This value needs to match `window.location.origin` evaluated by
|
69
|
+
# the User Agent during registration and authentication ceremonies.
|
70
|
+
# c.wa_origin = ENV['DEFAULT_HOST'] || https://myapp.mydomain.com
|
71
|
+
|
72
|
+
# Relying Party name for display purposes
|
73
|
+
# c.wa_relying_party_name = "My App Name"
|
74
|
+
|
75
|
+
# Optionally configure a client timeout hint, in milliseconds.
|
76
|
+
# This hint specifies how long the browser should wait for any
|
77
|
+
# interaction with the user.
|
78
|
+
# This hint may be overridden by the browser.
|
79
|
+
# https://www.w3.org/TR/webauthn/#dom-publickeycredentialcreationoptions-timeout
|
80
|
+
# c.wa_credential_options_timeout = 120_000
|
81
|
+
|
82
|
+
# You can optionally specify a different Relying Party ID
|
83
|
+
# (https://www.w3.org/TR/webauthn/#relying-party-identifier)
|
84
|
+
# if it differs from the default one.
|
85
|
+
#
|
86
|
+
# In this case the default would be "auth.example.com", but you can set it to
|
87
|
+
# the suffix "example.com"
|
88
|
+
#
|
89
|
+
# c.wa_rp_id = "example.com"
|
90
|
+
|
91
|
+
# Configure preferred binary-to-text encoding scheme. This should match the encoding scheme
|
92
|
+
# used in your client-side (user agent) code before sending the credential to the server.
|
93
|
+
# Supported values: `:base64url` (default), `:base64` or `false` to disable all encoding.
|
94
|
+
#
|
95
|
+
# c.wa_encoding = :base64url
|
96
|
+
|
97
|
+
# Possible values: "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "RS256", "RS384", "RS512", "RS1"
|
98
|
+
# Default: ["ES256", "PS256", "RS256"]
|
99
|
+
#
|
100
|
+
# c.wa_algorithms = ["ES256", "PS256", "RS256"]
|
101
|
+
|
102
|
+
# Append an algorithm to the existing set
|
103
|
+
# c.wa_algorithm = "PS512"
|
63
104
|
end
|
data/lib/passkeys-rails.rb
CHANGED
@@ -1,49 +1,42 @@
|
|
1
1
|
# rubocop:disable Naming/FileName
|
2
2
|
require 'passkeys_rails/engine'
|
3
|
+
require 'passkeys_rails/configuration'
|
3
4
|
require 'passkeys_rails/version'
|
4
5
|
require_relative "generators/passkeys_rails/install_generator"
|
6
|
+
require 'forwardable'
|
5
7
|
|
6
8
|
module PasskeysRails
|
7
9
|
module Test
|
8
10
|
autoload :IntegrationHelpers, 'passkeys_rails/test/integration_helpers'
|
9
11
|
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
# Changing this value will invalidate all tokens that have been fetched
|
14
|
-
# through the API.
|
15
|
-
mattr_accessor(:auth_token_secret)
|
13
|
+
class << self
|
14
|
+
extend Forwardable
|
16
15
|
|
17
|
-
|
18
|
-
# Changing this value will invalidate all tokens that have been fetched
|
19
|
-
# through the API.
|
20
|
-
mattr_accessor :auth_token_algorithm, default: "HS256"
|
16
|
+
def_delegators :config, :auth_token_secret, :auth_token_algorithm, :auth_token_expires_in, :default_class, :class_whitelist
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
def config
|
19
|
+
@config ||= begin
|
20
|
+
config = Configuration.new
|
21
|
+
yield(config) if block_given?
|
22
|
+
apply_webauthn_configuration(config)
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
# calling the API, no resource is created other than
|
31
|
-
# a PaskeysRails::Agent that is used to track the passkey.
|
32
|
-
#
|
33
|
-
# This library doesn't assume that there will only be one
|
34
|
-
# model, but it is a common use case, so setting the
|
35
|
-
# default_class simplifies the use of the API in that case.
|
36
|
-
mattr_accessor :default_class, default: "User"
|
24
|
+
config
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
29
|
+
def self.apply_webauthn_configuration(config)
|
30
|
+
WebAuthn.configure do |c|
|
31
|
+
c.origin = config.wa_origin
|
32
|
+
c.rp_name = config.wa_relying_party_name if config.wa_relying_party_name
|
33
|
+
c.credential_options_timeout = config.wa_credential_options_timeout if config.wa_credential_options_timeout
|
34
|
+
c.rp_id = config.wa_rp_id if config.wa_rp_id
|
35
|
+
c.encoding = config.wa_encoding if config.wa_encoding
|
36
|
+
c.algorithms = config.wa_algorithms if config.wa_algorithms
|
37
|
+
c.algorithms << config.wa_algorithm if config.wa_algorithm
|
38
|
+
end
|
39
|
+
end
|
47
40
|
|
48
41
|
# Convenience method to subscribe to various events in PasskeysRails.
|
49
42
|
#
|
@@ -104,16 +97,6 @@ module PasskeysRails
|
|
104
97
|
message: auth.message)
|
105
98
|
end
|
106
99
|
|
107
|
-
class << self
|
108
|
-
def config
|
109
|
-
yield self
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
100
|
require 'passkeys_rails/railtie' if defined?(Rails)
|
114
101
|
end
|
115
|
-
|
116
|
-
ActiveSupport.on_load(:before_initialize) do
|
117
|
-
PasskeysRails.auth_token_secret ||= Rails.application.secret_key_base
|
118
|
-
end
|
119
102
|
# rubocop:enable Naming/FileName
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class Configuration
|
3
|
+
# Secret used to encode the auth token.
|
4
|
+
# Rails.application.secret_key_base is used if none is defined here.
|
5
|
+
# Changing this value will invalidate all tokens that have been fetched
|
6
|
+
# through the API.
|
7
|
+
attr_accessor :auth_token_secret
|
8
|
+
|
9
|
+
# Algorithm used to generate the auth token.
|
10
|
+
# Changing this value will invalidate all tokens that have been fetched
|
11
|
+
# through the API.
|
12
|
+
attr_accessor :auth_token_algorithm
|
13
|
+
|
14
|
+
# How long the auth token is valid before requiring a refresh or new login.
|
15
|
+
# Set it to 0 for no expiration (not recommended in production).
|
16
|
+
attr_accessor :auth_token_expires_in
|
17
|
+
|
18
|
+
# Model to use when creating or authenticating a passkey.
|
19
|
+
# This can be overridden when calling the API, but if no
|
20
|
+
# value is supplied when calling the API, this value is used.
|
21
|
+
# If nil, there is no default, and if none is supplied when
|
22
|
+
# calling the API, no resource is created other than
|
23
|
+
# a PaskeysRails::Agent that is used to track the passkey.
|
24
|
+
#
|
25
|
+
# This library doesn't assume that there will only be one
|
26
|
+
# model, but it is a common use case, so setting the
|
27
|
+
# default_class simplifies the use of the API in that case.
|
28
|
+
attr_accessor :default_class
|
29
|
+
|
30
|
+
# By providing a class_whitelist, the API will require that
|
31
|
+
# any supplied class is in the whitelist. If it is not, the
|
32
|
+
# auth API will return an error. This prevents a caller from
|
33
|
+
# attempting to create an unintended record on registration.
|
34
|
+
# If nil, any model will be allowed.
|
35
|
+
# If [], no model will be allowed.
|
36
|
+
# This should be an array of symbols or strings,
|
37
|
+
# for example: %w[User AdminUser]
|
38
|
+
attr_accessor :class_whitelist
|
39
|
+
|
40
|
+
# webauthn settings
|
41
|
+
attr_accessor :wa_origin,
|
42
|
+
:wa_relying_party_name,
|
43
|
+
:wa_credential_options_timeout,
|
44
|
+
:wa_rp_id,
|
45
|
+
:wa_encoding,
|
46
|
+
:wa_algorithms,
|
47
|
+
:wa_algorithm
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
# defaults
|
51
|
+
@auth_token_secret = Rails.application.secret_key_base
|
52
|
+
@auth_token_algorithm = "HS256"
|
53
|
+
@auth_token_expires_in = 30.days
|
54
|
+
@default_class = "User"
|
55
|
+
@wa_origin = "https://example.com"
|
56
|
+
end
|
57
|
+
|
58
|
+
def subscribe(event_name)
|
59
|
+
PasskeysRails.subscribe(event_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passkeys-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Troy Anderson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '7.
|
19
|
+
version: '7.2'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 7.
|
22
|
+
version: 7.2.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '7.
|
29
|
+
version: '7.2'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 7.
|
32
|
+
version: 7.2.1
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: interactor
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,266 +50,280 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 2.
|
53
|
+
version: 2.9.1
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 2.
|
60
|
+
version: 2.9.1
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: webauthn
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: 3.
|
67
|
+
version: 3.1.0
|
68
68
|
type: :runtime
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: 3.
|
74
|
+
version: 3.1.0
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: cbor
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.5.9.8
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.5.9.8
|
75
89
|
- !ruby/object:Gem::Dependency
|
76
90
|
name: dotenv
|
77
91
|
requirement: !ruby/object:Gem::Requirement
|
78
92
|
requirements:
|
79
93
|
- - "~>"
|
80
94
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
95
|
+
version: 3.1.4
|
82
96
|
type: :development
|
83
97
|
prerelease: false
|
84
98
|
version_requirements: !ruby/object:Gem::Requirement
|
85
99
|
requirements:
|
86
100
|
- - "~>"
|
87
101
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
102
|
+
version: 3.1.4
|
89
103
|
- !ruby/object:Gem::Dependency
|
90
104
|
name: puma
|
91
105
|
requirement: !ruby/object:Gem::Requirement
|
92
106
|
requirements:
|
93
107
|
- - "~>"
|
94
108
|
- !ruby/object:Gem::Version
|
95
|
-
version:
|
109
|
+
version: 6.4.3
|
96
110
|
type: :development
|
97
111
|
prerelease: false
|
98
112
|
version_requirements: !ruby/object:Gem::Requirement
|
99
113
|
requirements:
|
100
114
|
- - "~>"
|
101
115
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
116
|
+
version: 6.4.3
|
103
117
|
- !ruby/object:Gem::Dependency
|
104
118
|
name: rake
|
105
119
|
requirement: !ruby/object:Gem::Requirement
|
106
120
|
requirements:
|
107
121
|
- - "~>"
|
108
122
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
123
|
+
version: 13.2.1
|
110
124
|
type: :development
|
111
125
|
prerelease: false
|
112
126
|
version_requirements: !ruby/object:Gem::Requirement
|
113
127
|
requirements:
|
114
128
|
- - "~>"
|
115
129
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
130
|
+
version: 13.2.1
|
117
131
|
- !ruby/object:Gem::Dependency
|
118
132
|
name: sprockets-rails
|
119
133
|
requirement: !ruby/object:Gem::Requirement
|
120
134
|
requirements:
|
121
135
|
- - "~>"
|
122
136
|
- !ruby/object:Gem::Version
|
123
|
-
version: 3.
|
137
|
+
version: 3.5.2
|
124
138
|
type: :development
|
125
139
|
prerelease: false
|
126
140
|
version_requirements: !ruby/object:Gem::Requirement
|
127
141
|
requirements:
|
128
142
|
- - "~>"
|
129
143
|
- !ruby/object:Gem::Version
|
130
|
-
version: 3.
|
144
|
+
version: 3.5.2
|
131
145
|
- !ruby/object:Gem::Dependency
|
132
146
|
name: sqlite3
|
133
147
|
requirement: !ruby/object:Gem::Requirement
|
134
148
|
requirements:
|
135
149
|
- - "~>"
|
136
150
|
- !ruby/object:Gem::Version
|
137
|
-
version: 1.
|
151
|
+
version: 2.1.0
|
138
152
|
type: :development
|
139
153
|
prerelease: false
|
140
154
|
version_requirements: !ruby/object:Gem::Requirement
|
141
155
|
requirements:
|
142
156
|
- - "~>"
|
143
157
|
- !ruby/object:Gem::Version
|
144
|
-
version: 1.
|
158
|
+
version: 2.1.0
|
145
159
|
- !ruby/object:Gem::Dependency
|
146
160
|
name: codecov
|
147
161
|
requirement: !ruby/object:Gem::Requirement
|
148
162
|
requirements:
|
149
163
|
- - "~>"
|
150
164
|
- !ruby/object:Gem::Version
|
151
|
-
version: 0.
|
165
|
+
version: 0.6.0
|
152
166
|
type: :development
|
153
167
|
prerelease: false
|
154
168
|
version_requirements: !ruby/object:Gem::Requirement
|
155
169
|
requirements:
|
156
170
|
- - "~>"
|
157
171
|
- !ruby/object:Gem::Version
|
158
|
-
version: 0.
|
172
|
+
version: 0.6.0
|
159
173
|
- !ruby/object:Gem::Dependency
|
160
174
|
name: debug
|
161
175
|
requirement: !ruby/object:Gem::Requirement
|
162
176
|
requirements:
|
163
177
|
- - "~>"
|
164
178
|
- !ruby/object:Gem::Version
|
165
|
-
version: 1.
|
179
|
+
version: 1.9.2
|
166
180
|
type: :development
|
167
181
|
prerelease: false
|
168
182
|
version_requirements: !ruby/object:Gem::Requirement
|
169
183
|
requirements:
|
170
184
|
- - "~>"
|
171
185
|
- !ruby/object:Gem::Version
|
172
|
-
version: 1.
|
186
|
+
version: 1.9.2
|
173
187
|
- !ruby/object:Gem::Dependency
|
174
188
|
name: simplecov
|
175
189
|
requirement: !ruby/object:Gem::Requirement
|
176
190
|
requirements:
|
177
191
|
- - "~>"
|
178
192
|
- !ruby/object:Gem::Version
|
179
|
-
version: 0.
|
193
|
+
version: 0.21.2
|
180
194
|
type: :development
|
181
195
|
prerelease: false
|
182
196
|
version_requirements: !ruby/object:Gem::Requirement
|
183
197
|
requirements:
|
184
198
|
- - "~>"
|
185
199
|
- !ruby/object:Gem::Version
|
186
|
-
version: 0.
|
200
|
+
version: 0.21.2
|
187
201
|
- !ruby/object:Gem::Dependency
|
188
202
|
name: reek
|
189
203
|
requirement: !ruby/object:Gem::Requirement
|
190
204
|
requirements:
|
191
205
|
- - "~>"
|
192
206
|
- !ruby/object:Gem::Version
|
193
|
-
version: 6.
|
207
|
+
version: 6.3.0
|
194
208
|
type: :development
|
195
209
|
prerelease: false
|
196
210
|
version_requirements: !ruby/object:Gem::Requirement
|
197
211
|
requirements:
|
198
212
|
- - "~>"
|
199
213
|
- !ruby/object:Gem::Version
|
200
|
-
version: 6.
|
214
|
+
version: 6.3.0
|
201
215
|
- !ruby/object:Gem::Dependency
|
202
216
|
name: factory_bot_rails
|
203
217
|
requirement: !ruby/object:Gem::Requirement
|
204
218
|
requirements:
|
205
219
|
- - "~>"
|
206
220
|
- !ruby/object:Gem::Version
|
207
|
-
version: 6.
|
221
|
+
version: 6.4.3
|
208
222
|
type: :development
|
209
223
|
prerelease: false
|
210
224
|
version_requirements: !ruby/object:Gem::Requirement
|
211
225
|
requirements:
|
212
226
|
- - "~>"
|
213
227
|
- !ruby/object:Gem::Version
|
214
|
-
version: 6.
|
228
|
+
version: 6.4.3
|
215
229
|
- !ruby/object:Gem::Dependency
|
216
230
|
name: generator_spec
|
217
231
|
requirement: !ruby/object:Gem::Requirement
|
218
232
|
requirements:
|
219
233
|
- - "~>"
|
220
234
|
- !ruby/object:Gem::Version
|
221
|
-
version: 0.
|
235
|
+
version: 0.10.0
|
222
236
|
type: :development
|
223
237
|
prerelease: false
|
224
238
|
version_requirements: !ruby/object:Gem::Requirement
|
225
239
|
requirements:
|
226
240
|
- - "~>"
|
227
241
|
- !ruby/object:Gem::Version
|
228
|
-
version: 0.
|
242
|
+
version: 0.10.0
|
229
243
|
- !ruby/object:Gem::Dependency
|
230
244
|
name: rspec
|
231
245
|
requirement: !ruby/object:Gem::Requirement
|
232
246
|
requirements:
|
233
247
|
- - "~>"
|
234
248
|
- !ruby/object:Gem::Version
|
235
|
-
version:
|
249
|
+
version: 3.13.0
|
236
250
|
type: :development
|
237
251
|
prerelease: false
|
238
252
|
version_requirements: !ruby/object:Gem::Requirement
|
239
253
|
requirements:
|
240
254
|
- - "~>"
|
241
255
|
- !ruby/object:Gem::Version
|
242
|
-
version:
|
256
|
+
version: 3.13.0
|
243
257
|
- !ruby/object:Gem::Dependency
|
244
258
|
name: rspec-rails
|
245
259
|
requirement: !ruby/object:Gem::Requirement
|
246
260
|
requirements:
|
247
261
|
- - "~>"
|
248
262
|
- !ruby/object:Gem::Version
|
249
|
-
version:
|
263
|
+
version: 7.0.1
|
250
264
|
type: :development
|
251
265
|
prerelease: false
|
252
266
|
version_requirements: !ruby/object:Gem::Requirement
|
253
267
|
requirements:
|
254
268
|
- - "~>"
|
255
269
|
- !ruby/object:Gem::Version
|
256
|
-
version:
|
270
|
+
version: 7.0.1
|
257
271
|
- !ruby/object:Gem::Dependency
|
258
272
|
name: timecop
|
259
273
|
requirement: !ruby/object:Gem::Requirement
|
260
274
|
requirements:
|
261
275
|
- - "~>"
|
262
276
|
- !ruby/object:Gem::Version
|
263
|
-
version: 0.9.
|
277
|
+
version: 0.9.10
|
264
278
|
type: :development
|
265
279
|
prerelease: false
|
266
280
|
version_requirements: !ruby/object:Gem::Requirement
|
267
281
|
requirements:
|
268
282
|
- - "~>"
|
269
283
|
- !ruby/object:Gem::Version
|
270
|
-
version: 0.9.
|
284
|
+
version: 0.9.10
|
271
285
|
- !ruby/object:Gem::Dependency
|
272
286
|
name: rubocop
|
273
287
|
requirement: !ruby/object:Gem::Requirement
|
274
288
|
requirements:
|
275
289
|
- - "~>"
|
276
290
|
- !ruby/object:Gem::Version
|
277
|
-
version:
|
291
|
+
version: 1.66.1
|
278
292
|
type: :development
|
279
293
|
prerelease: false
|
280
294
|
version_requirements: !ruby/object:Gem::Requirement
|
281
295
|
requirements:
|
282
296
|
- - "~>"
|
283
297
|
- !ruby/object:Gem::Version
|
284
|
-
version:
|
298
|
+
version: 1.66.1
|
285
299
|
- !ruby/object:Gem::Dependency
|
286
300
|
name: rubocop-performance
|
287
301
|
requirement: !ruby/object:Gem::Requirement
|
288
302
|
requirements:
|
289
303
|
- - "~>"
|
290
304
|
- !ruby/object:Gem::Version
|
291
|
-
version: 1.
|
305
|
+
version: 1.22.1
|
292
306
|
type: :development
|
293
307
|
prerelease: false
|
294
308
|
version_requirements: !ruby/object:Gem::Requirement
|
295
309
|
requirements:
|
296
310
|
- - "~>"
|
297
311
|
- !ruby/object:Gem::Version
|
298
|
-
version: 1.
|
312
|
+
version: 1.22.1
|
299
313
|
- !ruby/object:Gem::Dependency
|
300
314
|
name: rubocop-rails
|
301
315
|
requirement: !ruby/object:Gem::Requirement
|
302
316
|
requirements:
|
303
317
|
- - "~>"
|
304
318
|
- !ruby/object:Gem::Version
|
305
|
-
version: 2.
|
319
|
+
version: 2.26.2
|
306
320
|
type: :development
|
307
321
|
prerelease: false
|
308
322
|
version_requirements: !ruby/object:Gem::Requirement
|
309
323
|
requirements:
|
310
324
|
- - "~>"
|
311
325
|
- !ruby/object:Gem::Version
|
312
|
-
version: 2.
|
326
|
+
version: 2.26.2
|
313
327
|
- !ruby/object:Gem::Dependency
|
314
328
|
name: rubocop-rake
|
315
329
|
requirement: !ruby/object:Gem::Requirement
|
@@ -325,19 +339,61 @@ dependencies:
|
|
325
339
|
- !ruby/object:Gem::Version
|
326
340
|
version: 0.6.0
|
327
341
|
- !ruby/object:Gem::Dependency
|
328
|
-
name: rubocop-
|
342
|
+
name: rubocop-rspec_rails
|
343
|
+
requirement: !ruby/object:Gem::Requirement
|
344
|
+
requirements:
|
345
|
+
- - "~>"
|
346
|
+
- !ruby/object:Gem::Version
|
347
|
+
version: 2.30.0
|
348
|
+
type: :development
|
349
|
+
prerelease: false
|
350
|
+
version_requirements: !ruby/object:Gem::Requirement
|
351
|
+
requirements:
|
352
|
+
- - "~>"
|
353
|
+
- !ruby/object:Gem::Version
|
354
|
+
version: 2.30.0
|
355
|
+
- !ruby/object:Gem::Dependency
|
356
|
+
name: rubocop-factory_bot
|
357
|
+
requirement: !ruby/object:Gem::Requirement
|
358
|
+
requirements:
|
359
|
+
- - "~>"
|
360
|
+
- !ruby/object:Gem::Version
|
361
|
+
version: 2.26.1
|
362
|
+
type: :development
|
363
|
+
prerelease: false
|
364
|
+
version_requirements: !ruby/object:Gem::Requirement
|
365
|
+
requirements:
|
366
|
+
- - "~>"
|
367
|
+
- !ruby/object:Gem::Version
|
368
|
+
version: 2.26.1
|
369
|
+
- !ruby/object:Gem::Dependency
|
370
|
+
name: danger-changelog
|
371
|
+
requirement: !ruby/object:Gem::Requirement
|
372
|
+
requirements:
|
373
|
+
- - "~>"
|
374
|
+
- !ruby/object:Gem::Version
|
375
|
+
version: 0.7.0
|
376
|
+
type: :development
|
377
|
+
prerelease: false
|
378
|
+
version_requirements: !ruby/object:Gem::Requirement
|
379
|
+
requirements:
|
380
|
+
- - "~>"
|
381
|
+
- !ruby/object:Gem::Version
|
382
|
+
version: 0.7.0
|
383
|
+
- !ruby/object:Gem::Dependency
|
384
|
+
name: danger-toc
|
329
385
|
requirement: !ruby/object:Gem::Requirement
|
330
386
|
requirements:
|
331
387
|
- - "~>"
|
332
388
|
- !ruby/object:Gem::Version
|
333
|
-
version: 2.
|
389
|
+
version: 0.2.0
|
334
390
|
type: :development
|
335
391
|
prerelease: false
|
336
392
|
version_requirements: !ruby/object:Gem::Requirement
|
337
393
|
requirements:
|
338
394
|
- - "~>"
|
339
395
|
- !ruby/object:Gem::Version
|
340
|
-
version: 2.
|
396
|
+
version: 0.2.0
|
341
397
|
description: Devise is awesome, but we don't need all that UI/UX for PassKeys. This
|
342
398
|
gem is to make it easy to provide a back end that authenticates a mobile front end
|
343
399
|
with PassKeys.
|
@@ -380,6 +436,7 @@ files:
|
|
380
436
|
- lib/generators/passkeys_rails/templates/README
|
381
437
|
- lib/generators/passkeys_rails/templates/passkeys_rails_config.rb
|
382
438
|
- lib/passkeys-rails.rb
|
439
|
+
- lib/passkeys_rails/configuration.rb
|
383
440
|
- lib/passkeys_rails/engine.rb
|
384
441
|
- lib/passkeys_rails/railtie.rb
|
385
442
|
- lib/passkeys_rails/test/integration_helpers.rb
|
@@ -408,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
408
465
|
- !ruby/object:Gem::Version
|
409
466
|
version: '0'
|
410
467
|
requirements: []
|
411
|
-
rubygems_version: 3.4.
|
468
|
+
rubygems_version: 3.4.21
|
412
469
|
signing_key:
|
413
470
|
specification_version: 4
|
414
471
|
summary: PassKey authentication back end with simple API
|