fix(endpoint-auth): accept code exchange without grant_type at the authorization endpoint - #891
fix(endpoint-auth): accept code exchange without grant_type at the authorization endpoint#891rmdes wants to merge 1 commit into
grant_type at the authorization endpoint#891Conversation
|
The issue this fixes now carries direct source evidence rather than the documentation inference it originally cited: indieauth.com's verification request is built at |
…ndpoint
`codeValidator` guards both the authorization endpoint, where a code is
exchanged for a profile URL, and the token endpoint, where it is exchanged for
an access token. It required `grant_type` on both.
indieauth.com performs the profile exchange without it. Its verification
request is built at controllers/auth-web.rb:562 and sends `code`, `client_id`
and `redirect_uri` only; the string `grant_type` does not appear anywhere in
that codebase. So signing in through it failed after the user had already
authenticated and been redirected back:
400 {"error":"bad_request",
"error_description":"Missing parameter: `grant_type`"}
Accept the omission on the authorization endpoint. The token endpoint keeps
requiring it: no client old enough to omit `grant_type` requests an access
token, so relaxing the credential-issuing route buys nothing. A `grant_type`
that is present but not `authorization_code` is still rejected on both.
Identical to the change proposed upstream in getindiekit/indiekit#891.
Release: 1.0.0-beta.35
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGHR7MuyvBaDbAFfAGUxeT
|
Confirmed end to end against a live Indiekit deployment: signing in through To be precise about what that does and does not evidence. The deployment runs a Both fixes were needed together. With One thing this surfaced that is out of scope here, but is a real bug in |
`codeValidator` guards two routes: the authorization endpoint, where an
authorization code is exchanged for a profile URL, and the token endpoint,
where it is exchanged for an access token. It requires `grant_type` on both.
Clients predating the specification that introduced the parameter perform the
profile exchange without it. indieauth.com is one, and still documents the
older shape — `code`, `client_id` and `redirect_uri` only — so signing in to
an Indiekit site through it fails:
400 {"error":"bad_request",
"error_description":"Missing parameter: `grant_type`"}
The user completes authentication, is redirected back to the client, and only
then does the exchange fail.
Accept the omission on the authorization endpoint, which is the route those
clients use. The token endpoint keeps requiring the parameter: relaxing
validation on the route that issues access tokens buys no compatibility, since
no client old enough to omit it requests a token. A `grant_type` that is
present but not `authorization_code` is still rejected on both routes.
Adds a test for the profile exchange without `grant_type`, which fails without
this change, and one asserting the token endpoint still rejects its absence —
that case had no coverage, so nothing would have caught the allowance being
widened to both routes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGHR7MuyvBaDbAFfAGUxeT
5540c8c to
6b5717e
Compare
|
Same thoughts as on #884, ultimately this feels like better UX and a more robust approach broadening compatibility with services using older IndieAuth services. I just want to check, is this behaviour compatible with the IndieAuth spec do you think? |
|
Same trade-off as #884, and the spec answer is firmer: no. Same asymmetry too. indielogin.com sends it ( What makes me more comfortable with this one than #884: it's scoped to the authorization endpoint, and Still your call whether that's worth carrying for a deprecated client. |
Fixes #890.
codeValidatorrequiresgrant_typeon both routes it guards — theauthorization endpoint, where a code is exchanged for a profile URL, and the
token endpoint, where it is exchanged for an access token.
Clients predating the specification that introduced the parameter perform the
profile exchange without it. indieauth.com is one. I originally cited its
developer documentation for that; its source is more direct. The verification
request is built at
controllers/auth-web.rb:562:Three parameters;
grant_typeappears nowhere in that repository. So signing into an Indiekit site through it fails after the user has authenticated and been
redirected back:
{"error":"bad_request","error_description":"Missing parameter: `grant_type`"}The change
and the value check becomes
(grant_type ?? "authorization_code") !== "authorization_code", so a parameter that is present but wrong is stillrejected on both routes.
Why scope it rather than drop the requirement
Only the authorization endpoint needs the allowance. For indieauth.com this is
checkable rather than assumed:
token_endpointappears nowhere in its source,and its only outbound request to a user's server is the profile exchange quoted
above, so it never reaches
/tokenat all.Generalising from that one client is a judgement, not a measurement — I have not
surveyed every client that omits
grant_type. But the asymmetry favoursscoping: the authorization endpoint returns a profile URL,
/tokenissuescredentials, and widening validation there buys compatibility only for a client
that would have to be simultaneously old enough to omit the parameter and new
enough to want a token.
/tokentherefore keeps requiring it.Tests
200-authorization-profile-no-grant-type.js— the profile exchange withoutgrant_type. Fails onmainwith 400, passes with this change.400-token-grant-no-grant-type.js— the token endpoint still rejects itsabsence. This passes on
maintoo; there was no coverage for that case, sonothing would have caught the allowance being widened to both routes.
node --testinpackages/endpoint-auth: 68 tests, 68 passing with thechange. Reverting only
code.jsand keeping both tests leaves exactly onefailure, the new profile-exchange test.
mainis 66/66 before this change.Relationship to #884
That PR fixes the same class of problem one step earlier, where
response_typeis required of clients that predate it. They are independent — this branch is
cut from
main, not from that one — but a client using indieauth.com needsboth to sign in: without #884 the authorization request is rejected, and
without this the code exchange is. Both are now confirmed together against a
live deployment; see the comment below.
Relationship to #893
#893 also changes
packages/endpoint-auth/lib/middleware/code.js, and I want toflag an interaction I only found after opening both.
The two merge cleanly — this PR adds a
grant_typeallowance to therequired-parameter list, #893 replaces the checks that follow it, and git
resolves that without conflict. But the merged result fails a test, and it is
this PR's:
200-authorization-profile-no-grant-type.jssigns a code carryingneither
client_idnorredirect_uri, and #893 begins rejecting codes missingthose claims. It passes on either branch alone and fails on both together.
Adding the two claims to that fixture resolves it, after which the combined
branches give 70 tests, 70 passing.
So if this merges second, CI goes red until that one-line fixture change is
made, and the cause is in this branch rather than in #893. Nothing structural,
but it will not go green on the merge alone.
Update: the client this affects is deprecated
indieauth.com now carries a deprecation notice — the service is being retired in
favour of indielogin.com.
The replacement does not need this fix. When it delegates to a user's own
IndieAuth server it sends the parameters:
response_type=codeis set byindieauth-client-php(
src/IndieAuth/Client.php:435),and
grant_type=authorization_codetogether withcode_verifieratapp/Provider/IndieAuth.php:73.So this is compatibility with a service on its way out, and worth weighing on
that basis. Against that: indieauth.com is still serving, and people still sign
in through it — confirmed against a live Indiekit deployment today — so the
breakage is real until the shutdown happens.
I also wrote above that indieauth.com is what the IndieWeb wiki authenticates
with. I went to verify that and could not: I found no reference to either
service on indieweb.org's login or front page. I should not have asserted it, so
treat that claim as withdrawn — it does not affect the rest, which is about what
indieauth.com sends.