Skip to content

ETQ admin, je dois fournir un filtrage réseau pour avoir un jeton API#13357

Merged
mfo merged 9 commits into
demarche-numerique:mainfrom
mfo:harden-api-token
Jun 30, 2026
Merged

ETQ admin, je dois fournir un filtrage réseau pour avoir un jeton API#13357
mfo merged 9 commits into
demarche-numerique:mainfrom
mfo:harden-api-token

Conversation

@mfo

@mfo mfo commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Problème

Les jetons API peuvent être créés sans aucun filtrage réseau, ce qui permet leur utilisation depuis n'importe quelle adresse IP. En cas de vol de jeton, il peut être exploité depuis n'importe quelle machine.

Ref: #13282

Solution

Rend le filtrage réseau obligatoire pour tout nouveau jeton API via une colonne requires_ip_filtering (2 migrations : default false pour le stock, puis default true pour les nouveaux).

Deux modes au moment de la création :

  • Détection automatique (défaut) : l'IP est verrouillée au 1er appel API (assign_first_ip!)
  • Saisie manuelle : l'admin entre les réseaux autorisés

Les anciens jetons ne sont pas impactés. Un badge "en attente de détection IP" s'affiche dans la liste des jetons, et un message info apparaît sur la page d'édition sous l'input réseau.

Screenshots

Page sécurité – mode "Détection automatique" (défaut)

02-securite-auto-assign.png

Page sécurité – mode "Saisie manuelle"

03-securite-manual-ip.png

Confirmation de création du jeton

04-token-created-auto.png

Profil – badge "en attente" sur le nouveau jeton

05-profil-pending-badge.png

🤖 Generated with Claude Code

@mfo mfo changed the title ETQ admin, je dois fournir un filtrage réseau pour avoir un jeton API WIP – ETQ admin, je dois fournir un filtrage réseau pour avoir un jeton API Jun 26, 2026
@mfo
mfo force-pushed the harden-api-token branch 6 times, most recently from ecadbd4 to 5a64d0b Compare June 26, 2026 14:20
@mfo
mfo marked this pull request as ready for review June 26, 2026 14:24
@mfo mfo self-assigned this Jun 26, 2026
@mfo mfo added this to The Kanban Jun 26, 2026
@mfo mfo removed this from The Kanban Jun 26, 2026
@mfo mfo changed the title WIP – ETQ admin, je dois fournir un filtrage réseau pour avoir un jeton API ETQ admin, je dois fournir un filtrage réseau pour avoir un jeton API Jun 26, 2026

@colinux colinux left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security review OK

Bien que ce soit un gain net en sécu, je réalise qu'on va augmenter la durée de tokens à vie illimitée, ce qui n'est pas idéal pour autant. Je crois qu'une prochaine étape c'est de limiter les tokens à 1 an comme un peu partout, sinon on pousse à l'inertie

Comment thread app/models/api_token.rb Outdated
end

def assign_first_ip!(ip)
return unless requires_ip_filtering? && authorized_networks.empty?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return unless requires_ip_filtering? && authorized_networks.empty?
return if !requires_ip_filtering? || authorized_networks.any?

(pas de unless sur conditions multiples)

mfo and others added 9 commits June 30, 2026 05:11
Existing tokens get `requires_ip_filtering: false` so their behavior
is unchanged. A follow-up migration will flip the default to true
for newly created tokens.

Then change default  requires_ip_filtering=true by default,
enforcing mandatory network filtering at creation time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
assign_first_ip! writes the caller's IP into authorized_networks
on the first API call when the token has requires_ip_filtering=true
but no networks configured yet (auto-assign mode).

pending_auto_ip? returns true when a token is waiting for its first
call to lock onto an IP.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a token with requires_ip_filtering=true and empty
authorized_networks makes its first API call, the caller's IP
is written into authorized_networks. Subsequent calls from
a different IP will be rejected (403).

Legacy tokens (requires_ip_filtering=false) are unaffected.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All new tokens are created with requires_ip_filtering=true.
The autoAssign network filtering mode creates a token with empty
authorized_networks that will lock onto the first caller's IP.

Infinite lifetime is now allowed no matter what since the
token will be IP-filtered after its first use.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the old radio buttons ("custom networks" / "any IP") with
two new options:
- "Détection automatique au 1er appel API" (default)
- "Saisie manuelle des réseaux autorisés"

The "no filtering" option is removed. The "Infini" lifetime is
always available since every new token will have IP filtering.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tokens awaiting their first API call show an info badge
"en attente de détection IP (1er appel)" instead of the
legacy "aucun filtrage réseau" warning.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add two new system specs covering the full token creation journey:
- auto-assign mode: token created with empty networks, pending IP
- manual mode: token created with explicit IP/CIDR

Update existing test to use the new autoAssign radio value.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
IP filtering is now mandatory for all new tokens, so the guard
on infinite lifetime is no longer needed. Remove the conditional
and always accept infinite lifetime.

Add an info message below the network input on the edit page when
a token is pending auto-IP assignment.

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover the same auto-assign scenarios as API v1:
- auto-assign on first call when requires_ip_filtering is true
- legacy token (requires_ip_filtering false) does not auto-assign
- forbidden after IP is locked and request comes from different IP

Ref demarche-numerique#13282

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mfo
mfo force-pushed the harden-api-token branch from 5a64d0b to 5e7caff Compare June 30, 2026 03:11
@mfo
mfo enabled auto-merge June 30, 2026 03:11
@mfo
mfo added this pull request to the merge queue Jun 30, 2026
Merged via the queue into demarche-numerique:main with commit 4ff6614 Jun 30, 2026
20 checks passed
@mfo
mfo deleted the harden-api-token branch June 30, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants