MCPcopy Create free account
hub / github.com/Mnexa-AI/e2a / TestHTTP_Token_CodeReplay

Function TestHTTP_Token_CodeReplay

internal/agent/oauth_token_test.go:304–375  ·  view source on GitHub ↗

TestHTTP_Token_CodeReplay drives the code-reuse path: exchange once (success), then re-present the same code (rejection + the originally issued tokens get revoked per RFC 6749 §10.5).

(t *testing.T)

Source from the content-addressed store, hash-verified

302// once (success), then re-present the same code (rejection + the
303// originally issued tokens get revoked per RFC 6749 §10.5).
304func TestHTTP_Token_CodeReplay(t *testing.T) {
305 server, provider, pool, clientID, userID := setupOAuthAPI(t)
306 redirectURI := "http://localhost:8765/callback"
307 verifier, challenge := newPKCE(t)
308 code := mintAuthCode(t, provider, clientID, userID, redirectURI, challenge)
309
310 form := url.Values{}
311 form.Set("grant_type", "authorization_code")
312 form.Set("code", code)
313 form.Set("client_id", clientID)
314 form.Set("redirect_uri", redirectURI)
315 form.Set("code_verifier", verifier)
316
317 // First exchange wins.
318 resp, err := http.Post(server.URL+"/oauth2/token",
319 "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
320 if err != nil {
321 t.Fatal(err)
322 }
323 if resp.StatusCode != http.StatusOK {
324 t.Fatalf("first exchange: status = %d, want 200", resp.StatusCode)
325 }
326 var first struct {
327 AccessToken string `json:"access_token"`
328 }
329 json.NewDecoder(resp.Body).Decode(&first)
330 resp.Body.Close()
331
332 // Replay: same code, same verifier. Must fail.
333 resp2, err := http.Post(server.URL+"/oauth2/token",
334 "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
335 if err != nil {
336 t.Fatal(err)
337 }
338 defer resp2.Body.Close()
339 if resp2.StatusCode != http.StatusBadRequest {
340 t.Fatalf("replay: status = %d, want 400 invalid_grant", resp2.StatusCode)
341 }
342
343 // And the tokens from the first exchange must be revoked (the
344 // §10.5 reuse defense fosite drives via RevokeAccessToken +
345 // RevokeRefreshToken). Both halves must fire — checking only the
346 // access side would miss a regression that broke the refresh
347 // cascade.
348 var accessRevoked bool
349 if err := pool.QueryRow(context.Background(), `
350 SELECT revoked_at IS NOT NULL FROM oauth_access_tokens
351 WHERE request_id IN (
352 SELECT request_id FROM oauth_auth_codes WHERE active = FALSE
353 )
354 LIMIT 1
355 `).Scan(&accessRevoked); err != nil {
356 t.Fatalf("query access revoked status: %v", err)
357 }
358 if !accessRevoked {
359 t.Error("expected the originally-issued access token to be revoked after code replay")
360 }
361

Callers

nothing calls this directly

Calls 7

setupOAuthAPIFunction · 0.85
newPKCEFunction · 0.85
mintAuthCodeFunction · 0.85
ScanMethod · 0.80
QueryRowMethod · 0.80
CloseMethod · 0.45
ErrorMethod · 0.45

Tested by

no test coverage detected