devise-api 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d86e2276841e4ca3ae7497278542d686da38368f5f5cdf76629abd463c7976f
4
- data.tar.gz: 3f33438f2a503b0f1bd510e6020c0d88f61a82715aff8b701a1872a4901ffb75
3
+ metadata.gz: b59405402be8f8b9db001a874ea536a0ecf10d0fbf7acf8205b921d9fcc9b6b9
4
+ data.tar.gz: 9bb3114fcbb15fe38af1e46bf009508991f2ac32fccdff7da6626f33c1d11e15
5
5
  SHA512:
6
- metadata.gz: acea2091463ddb32d14df2c6b6aaf88e55549796b79ba35855ffdd08f3f78e8a5e5d9f305f48a855a2f3d17aa2cee2c8e7fce7ce07efda222af9de785a9f57cf
7
- data.tar.gz: 8cecc648a297705d7d7f82420aa48fd6f4ed7d8899ba721cf0002cbae34f9f1e3fae5032877a4efa826f609c50592f894f9407adeeed6a12c7ca856b2dd9c5d2
6
+ metadata.gz: d797c3f5a67bb5e2f0e0293d828b617b3b55e151d0648dce25739f1b9e7a0594da1c24ba605c677b6806df8ed1dc6d3762548ca91dbd2afe3280462df4d19d8b
7
+ data.tar.gz: d1cff639ba814071d5b3e5e5e3f3680f363fb25514a264e98c55bcf077bfe1cc1c4ad3b85d7f8974dc13975f0d233c57d04533efcca5856763c73e93380cac6c
@@ -0,0 +1,38 @@
1
+ Create a git commit for the current staged/unstaged changes.
2
+
3
+ ## Steps
4
+
5
+ 1. Run `git status` and `git diff` to review all changes
6
+ 2. Run `git log --oneline -5` to match the project's recent commit style
7
+ 3. Stage only relevant files — never stage `.env`, `*.pem`, credentials, or build artifacts (`*.gem`, `pkg/`)
8
+ 4. Draft a commit message following the format below
9
+ 5. Create the commit
10
+
11
+ ## Commit Message Format
12
+
13
+ ```
14
+ <type>(<scope>): <description>
15
+ ```
16
+
17
+ **Types:** `feat` | `fix` | `refactor` | `chore` | `docs` | `test` | `perf`
18
+
19
+ **Scopes** (lowercase, optional — this repo's history mostly omits the scope; use one only when it adds clarity): `tokens` | `services` | `responses` | `config` | `model` | `routes` | `generators` | `locales` | `specs` | `docs` | `ci`
20
+
21
+ **Rules:**
22
+ - Description explains WHY, not WHAT
23
+ - Use present tense ("add", not "added")
24
+ - If changes span multiple areas, omit the scope
25
+ - Keep under 72 characters
26
+
27
+ ## Examples
28
+
29
+ ```
30
+ feat(tokens): add token rotation on refresh to block replay
31
+ fix(responses): correct translation key for unconfirmed signup message
32
+ refactor(services): extract error rendering shared by controller actions
33
+ chore(ci): add Ruby 3.3 to the test matrix
34
+ test(specs): cover non-default authorization locations
35
+ docs: document refresh chain semantics in data-model
36
+ ```
37
+
38
+ $ARGUMENTS
@@ -0,0 +1,57 @@
1
+ Create a new git branch following the project's naming conventions.
2
+
3
+ ## Branch Naming Format
4
+
5
+ ```
6
+ {type}/{description}
7
+ ```
8
+
9
+ ## Types
10
+
11
+ | Type | Use Case |
12
+ |------|----------|
13
+ | `feature` | New functionality, endpoints, entities |
14
+ | `fix` | Bug fixes, issue corrections |
15
+ | `chore` | Maintenance, config, non-feature changes |
16
+ | `refactor` | Code restructuring without behavior change |
17
+ | `ci` | CI/CD pipeline changes |
18
+ | `test` | Test additions or modifications |
19
+ | `docs` | Documentation only changes |
20
+ | `hotfix` | Urgent production fixes |
21
+
22
+ ## Description Rules
23
+
24
+ 1. Use **kebab-case** (words separated by hyphens)
25
+ 2. Start with a **verb**: `add-`, `fix-`, `update-`, `remove-`, `complete-`, `enable-`, `change-`
26
+ 3. **Lowercase only** — no uppercase letters
27
+ 4. Be descriptive but concise
28
+
29
+ ## Workflow
30
+
31
+ 1. Infer the branch type from context (default to `feature` for new functionality)
32
+ 2. Generate a descriptive branch name based on: $ARGUMENTS (or the work done in the session)
33
+ 3. **Immediately create and checkout the branch** — no confirmation needed:
34
+
35
+ ```bash
36
+ git checkout -b {type}/{description}
37
+ ```
38
+
39
+ 4. Report the branch name to the user after creation
40
+
41
+ ## Examples
42
+
43
+ | Description | Branch Name |
44
+ |-------------|-------------|
45
+ | Adding email notifications | `feature/add-email-notification-structure` |
46
+ | Fixing login timeout | `fix/login-timeout-issue` |
47
+ | Updating Redis config | `chore/update-redis-configuration` |
48
+ | Refactoring ticket service | `refactor/ticket-service` |
49
+ | Adding user repo tests | `test/add-user-repository-tests` |
50
+
51
+ ## Anti-patterns
52
+
53
+ - No uppercase: `Feature/Add-Users`
54
+ - No underscores: `feature_add_users`
55
+ - No camelCase: `feature/AddUserEndpoint`
56
+ - Missing type prefix: `add-users`
57
+ - Too vague: `feature/users` (no verb)
@@ -0,0 +1,41 @@
1
+ Analyze all commits on the current branch (compared to main) and create a comprehensive PR.
2
+
3
+ ## Steps
4
+
5
+ 1. Run `git log main..HEAD` and `git diff main...HEAD` to understand all changes
6
+ 2. Generate PR title in conventional format: `<type>(<scope>): <description>`
7
+ 3. Push the branch to remote if not already pushed
8
+ 4. Create PR using GitHub CLI:
9
+
10
+ ```bash
11
+ gh pr create --title "<title>" --body "<body>"
12
+ ```
13
+
14
+ ## PR Body Template
15
+
16
+ ```markdown
17
+ ## Summary
18
+ - [1-3 bullet points describing what changed and why]
19
+
20
+ ## Changes
21
+ - [List of significant code changes by file/area]
22
+
23
+ ## Architecture Impact
24
+ - [Any cross-service changes, new events, DB migrations]
25
+
26
+ ## Test Plan
27
+ - [ ] Unit tests added/updated
28
+ - [ ] Integration tests pass
29
+ - [ ] Manual testing completed
30
+ - [ ] Migration tested on clean DB
31
+
32
+ ## Related
33
+ - Closes #issue-number (if applicable)
34
+ ```
35
+
36
+ ## Rules
37
+
38
+ - Never include credentials or secrets in PR description
39
+ - Link related issues with `Closes #N` syntax
40
+ - If migration exists, mention it explicitly in Architecture Impact
41
+ - Scope: $ARGUMENTS (optional: specific context or issue number)
data/CHANGELOG.md CHANGED
@@ -1,4 +1,57 @@
1
- ## [Unreleased]
1
+ # Changelog
2
+
3
+ ## [0.3.0] - 2026-08-25
4
+
5
+ ### Added
6
+ - Refresh token rotation with reuse detection behind `refresh_token.rotation_enabled` (default `false`): each refresh revokes the presented refresh token, and presenting an already-rotated/revoked refresh token revokes the whole token family (SEC-2)
7
+ - Paranoid mode behind `paranoid` (default `false`): unknown accounts and wrong passwords both return the generic `invalid_authentication` error with no lockable/confirmable details, preventing account enumeration (SEC-5)
8
+ - `error_response.verbose_account_state` (default `true`): set to `false` to omit the `lockable`/`confirmable` metadata blocks from error responses (SEC-7)
9
+ - `Devise::Api::Token#revoke!` and `#revoke_family!` helpers
10
+ - New `invalid_login` error (HTTP 400) returned instead of `invalid_email` when the model's `authentication_keys` do not include `:email`
11
+ - Lockable error responses now include the correctly spelled `failed_attempts` key alongside the deprecated `failed_attemps` (the misspelling will be removed in the next major release)
12
+ - `access_token`, `refresh_token` and `previous_refresh_token` are added to the host app's `filter_parameters`, and the token model filters them from `#inspect` output (GH-51)
13
+
14
+ ### Changed
15
+ - **Breaking-ish:** `POST /<scope>/tokens/refresh` with an unknown refresh token now returns `invalid_refresh_token` (HTTP 400) instead of `invalid_token` (HTTP 401)
16
+ - The install generator's migration now creates **unique** indexes on `access_token` and `refresh_token`; token creation rescues `ActiveRecord::RecordNotUnique` and retries with a fresh token (SEC-4). Existing installs should add a migration:
17
+ ```ruby
18
+ remove_index :devise_api_tokens, :access_token
19
+ remove_index :devise_api_tokens, :refresh_token
20
+ add_index :devise_api_tokens, :access_token, unique: true
21
+ add_index :devise_api_tokens, :refresh_token, unique: true
22
+ ```
23
+ - `current_devise_api_refresh_token` is now memoized in the shared controller helpers (the duplicate controller-level override was removed)
24
+ - Internal time handling standardized on `Time.current`
25
+
26
+ ### Removed
27
+ - Vestigial RBS stub (`sig/devise/api.rbs`)
28
+
29
+ ## [0.2.0] - 2024-09-27
30
+
31
+ - Resource lookup uses the model's `authentication_keys` instead of hardcoding `email` (#46)
32
+ - Fixed nil memoization of `current_devise_api_token` / `current_devise_api_refresh_token` (#48)
33
+ - Fixed the translation key for the unconfirmed signup message (#49)
34
+
35
+ ## [0.1.3] - 2023-08-08
36
+
37
+ - Fixed `AbstractController::DoubleRenderError` on refresh (#29)
38
+ - Allowed defining extra fields for sign up via `sign_up.extra_fields` (#36, #38)
39
+ - Disabled parameter wrapping in `TokensController` (#42)
40
+
41
+ ## [0.1.2] - 2023-05-30
42
+
43
+ - Added `sign_up.enabled` option to disable the sign up endpoint (#15)
44
+ - Fixed refresh behavior (#14)
45
+ - Fixed undefined variable error in the controller helper (#25)
46
+ - Migration template respects the configured primary/foreign key types (#23)
47
+
48
+ ## [0.1.1] - 2023-01-14
49
+
50
+ - Fixed invalid strategy error (#2)
51
+
52
+ ## [0.1.0] - 2023-01-14
53
+
54
+ - First public release: `:api` Devise module with token sign up / sign in / refresh / revoke / info endpoints (#1)
2
55
 
3
56
  ## [0.0.0] - 2023-01-09
4
57
 
data/CLAUDE.md ADDED
@@ -0,0 +1,49 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ `devise-api` is a Rails engine gem that adds token-based API authentication (access tokens + refresh tokens) to Devise. It registers an `:api` Devise module that models opt into via `devise :api`. Supports Ruby >= 2.7, Rails >= 6.0.
8
+
9
+ ## Documentation
10
+
11
+ `docs/` holds the detailed internal documentation — start at `docs/README.md` (index + ground rules for working in this repo). Highlights:
12
+
13
+ - `docs/architecture.md` — components, boot sequence, request lifecycle (with diagrams)
14
+ - `docs/api-reference.md` — endpoints, payloads, full error catalog
15
+ - `docs/configuration.md`, `docs/data-model.md`, `docs/services.md`, `docs/extending.md`, `docs/testing.md`, `docs/development.md`
16
+ - `docs/analysis/security-review.md` and `docs/analysis/known-issues.md` — the vetted backlog of security findings (SEC-*) and code-quality issues (KI-*). **Consult these before changing surprising code** (some quirks, like the `failed_attemps` response-field typo, are shipped public API), and update them when you fix an item.
17
+
18
+ Docs are contractual: a PR that changes behavior described in `docs/` must update the matching document.
19
+
20
+ ## Commands
21
+
22
+ ```bash
23
+ bundle install # install dependencies
24
+ bundle exec rake # default task: rspec + rubocop (what CI runs)
25
+ bundle exec rake rspec # run the full test suite
26
+ bundle exec rspec spec/services/tokens_service/refresh_spec.rb # run one spec file
27
+ bundle exec rspec spec/services/tokens_service/refresh_spec.rb:12 # run one example by line
28
+ bundle exec rubocop # lint (config in .rubocop.yml, single quotes, 120-char lines)
29
+ ```
30
+
31
+ Tests run against the dummy Rails app in `spec/dummy` (sqlite3, schema in `spec/dummy/db/schema.rb`). `spec/spec_helper.rb` boots it via `require 'dummy/config/environment'`; DatabaseCleaner wraps each example. If token/schema fields change, the dummy app's migrations and schema must be updated alongside the generator template.
32
+
33
+ ## Architecture
34
+
35
+ Everything is configured through a single global: `Devise.api.config` (a `Dry::Configurable` instance defined in `lib/devise/api/configuration.rb`, installed as `Devise.api` in `lib/devise/api.rb`). Config includes token expiry/generators, sign_up enablement/extra_fields, authorization header/params location, before/after callbacks for each action, and — importantly — `base_token_model` and `base_controller`, which are **string class names resolved with `constantize` at use sites** so host apps can substitute their own subclasses. When touching model/controller references, use the config values, never hardcode `Devise::Api::Token`.
36
+
37
+ Flow of a request:
38
+
39
+ 1. **Routes** — `lib/devise/api/rails/routes.rb` monkey-patches `ActionDispatch::Routing::Mapper#devise_api`, which `devise_for` invokes because `lib/devise/api.rb` calls `Devise.add_module :api, route: {...}`. Draws POST `sign_up`/`sign_in`/`revoke`/`refresh` and GET `info` under `/<scope>/tokens`.
40
+ 2. **Controller** — `app/controllers/devise/api/tokens_controller.rb` (inherits from configurable `base_controller`, default `::DeviseController`). Each action runs the configured `before_*` callback, delegates to a service, then renders a `TokenResponse` or `ErrorResponse` and fires the `after_successful_*` callback.
41
+ 3. **Services** — `app/services/devise/api/**`, all inheriting `Devise::Api::BaseService` which wires up dry-initializer (`option :x, type: Types::...`) and dry-monads (`Success`/`Failure` + do-notation `yield`). Two namespaces: `ResourceOwnerService` (`Authenticate`, `SignIn`, `SignUp`) and `TokensService` (`Create`, `Refresh`, `Revoke`). Services compose: e.g. `SignIn` yields `Authenticate` then `TokensService::Create`. Failures are hashes like `{ error: :invalid_authentication, record: ... }` splatted into `ErrorResponse`.
42
+ 4. **Responses** — `lib/devise/api/responses/`. `ErrorResponse` maps a symbolic error type (see its `ERROR_TYPES` list) to an i18n message (`config/locales/en.yml`, keys under `devise.api.responses`) and HTTP status. `TokenResponse` shapes the JSON per action.
43
+
44
+ Supporting pieces:
45
+
46
+ - **Token model** — `lib/devise/api/token.rb` (`devise_api_tokens` table, polymorphic `resource_owner`). Knows `expired?`/`revoked?`/`active?` and generates unique tokens via the configured generator procs. Refresh chains are linked through `previous_refresh_token`.
47
+ - **Controller helpers** — `lib/devise/api/controllers/helpers.rb` is included into all of ActionController on load: `authenticate_devise_api_token!`, `current_devise_api_token`, `current_devise_api_user`. Token extraction honors `authorization.location` (`:header`, `:params`, `:both`).
48
+ - **Devise module** — `Devise::Models::Api` in `lib/devise/api.rb` adds the `access_tokens` association and `supported_devise_modules`; the codebase checks that inquiry to conditionally support optional Devise modules (trackable, lockable, confirmable).
49
+ - **Generator** — `rails generate devise_api:install` (`lib/devise/api/generators/install_generator.rb`) copies the migration template and locale file into the host app.
data/Gemfile CHANGED
@@ -47,3 +47,6 @@ gem 'rspec-core'
47
47
 
48
48
  # Common code needed by the other RSpec gems. Not intended for direct use [https://github.com/rspec/rspec-support]
49
49
  gem 'rspec-support'
50
+
51
+ # Code coverage analysis tool for Ruby [https://github.com/simplecov-ruby/simplecov]
52
+ gem 'simplecov', '~> 0.22', require: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- devise-api (0.1.3)
4
+ devise-api (0.3.0)
5
5
  devise (>= 4.7.2)
6
6
  dry-configurable (~> 1.0, >= 1.0.1)
7
7
  dry-initializer (>= 3.1.1)
@@ -98,6 +98,7 @@ GEM
98
98
  responders
99
99
  warden (~> 1.2.3)
100
100
  diff-lcs (1.5.0)
101
+ docile (1.4.1)
101
102
  dry-configurable (1.0.1)
102
103
  dry-core (~> 1.0, < 2)
103
104
  zeitwerk (~> 2.6)
@@ -236,6 +237,12 @@ GEM
236
237
  rubocop-ast (1.24.1)
237
238
  parser (>= 3.1.1.0)
238
239
  ruby-progressbar (1.11.0)
240
+ simplecov (0.22.0)
241
+ docile (~> 1.1)
242
+ simplecov-html (~> 0.11)
243
+ simplecov_json_formatter (~> 0.1)
244
+ simplecov-html (0.13.2)
245
+ simplecov_json_formatter (0.1.4)
239
246
  sprockets (4.2.0)
240
247
  concurrent-ruby (~> 1.0)
241
248
  rack (>= 2.2.4, < 4)
@@ -260,6 +267,7 @@ PLATFORMS
260
267
  arm64-darwin-21
261
268
  arm64-darwin-22
262
269
  arm64-darwin-23
270
+ arm64-darwin-25
263
271
 
264
272
  DEPENDENCIES
265
273
  awesome_print
@@ -275,6 +283,7 @@ DEPENDENCIES
275
283
  rspec-rails (~> 6.0, >= 6.0.1)
276
284
  rspec-support
277
285
  rubocop (~> 1.21)
286
+ simplecov (~> 0.22)
278
287
  sprockets-rails
279
288
  sqlite3 (~> 1.4)
280
289