Skip to content

##Bug: POST /v2/invites/{id}/resend rejects OAT-issued JWT with scope-invite-edit — inconsistent with POST /v2/invites/bulk which works #2527

Description

@shuvenduchatterjee

Summary

POST /v2/invites/bulk works correctly with an OAT-issued JWT. However, POST /v2/invites/{id}/resend rejects the same token with a 403 error, even though the OAT has scope-invite-edit configured — the scope that exists specifically for invite management.


Token Policy Matrix (observed)

Endpoint OAT JWT
POST /v2/invites/bulk ✅ 200 OK
POST /v2/invites/{id}/resend ❌ 403 Forbidden

No token-type restriction is documented in the API reference for either endpoint.


Environment

  • Docker Hub organization account (Business/Team plan)
  • Caller role: Organization Owner
  • OAT scopes: scope-member-edit, scope-invite-edit, scope-group-edit, scope-activity-read

Steps to Reproduce

Step 1 — Auth with OAT

export TOKEN=$(curl -s -X POST https://hub.docker.com/v2/users/login \
  -H "Content-Type: application/json" \
  -d '{"username": "<org-name>", "password": "<OAT>"}' \
  | jq -r '.token')

Step 2 — Bulk invite with OAT JWT — works ✅

curl -s -X POST https://hub.docker.com/v2/invites/bulk \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"org": "<org-name>", "role": "member", "invitees": [{"email": "test@example.com"}]}'

Step 3 — Get the pending invite ID

curl -s "https://hub.docker.com/v2/orgs/<org-name>/invites" \
  -H "Authorization: Bearer $TOKEN" | jq '.results[0].id'

Step 4 — Resend with same OAT JWT — fails ❌

curl -s -X POST "https://hub.docker.com/v2/invites/<invite-id>/resend" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

Actual Response

{
  "message": "token issued from organization access token is not allowed",
  "errinfo": {}
}

Expected Response

{
  "id": "<invite-id>",
  "status": "pending"
}

Issues

  1. Inconsistent policy between sibling endpoints/v2/invites/bulk and /v2/invites/{id}/resend are under the same namespace but apply different token policies with no documentation explaining the difference.
  2. scope-invite-edit does not cover resend — This scope exists for invite management but does not work on the resend endpoint, making it misleading and incomplete.
  3. No documented restriction — The API reference only states bearerAuth is required, with no mention of OAT restrictions.

Impact

Automated invite workflows using OATs — the token type Docker recommends for org-level automation — can send invites but cannot resend them. This makes scope-invite-edit only partially functional and forces a suboptimal workaround.


Workaround

Delete the pending invite via DELETE /v2/invites/{id} and re-issue via POST /v2/invites/bulk. This triggers a new invite email rather than a true resend — functionally similar but semantically different and potentially confusing to the invitee.


References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs triageThis issue needs to triage by our team (applied by default to new issue)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions