handleOAuthGetClient is the public read endpoint the consent UI uses to look up client metadata by client_id. Anonymous: RFC 7591 §4 says client metadata is generally not secret, and the consent screen needs the friendly name to give the user a meaningful "Allow X to access Y" prompt. 404 when oauth
(w http.ResponseWriter, r *http.Request)
| 474 | // CDNs can't absorb (Go's http.NotFound emits no Cache-Control, so |
| 475 | // 404 responses aren't edge-cacheable). |
| 476 | func (a *API) handleOAuthGetClient(w http.ResponseWriter, r *http.Request) { |
| 477 | if a.oauthStorage == nil { |
| 478 | http.NotFound(w, r) |
| 479 | return |
| 480 | } |
| 481 | if ok, retryAfter := a.dcrLimit.AllowWithRetryAfter(dcrSourceIP(r)); !ok { |
| 482 | secs := int(retryAfter.Round(time.Second).Seconds()) |
| 483 | if secs < 1 { |
| 484 | secs = 1 |
| 485 | } |
| 486 | w.Header().Set("Retry-After", strconv.Itoa(secs)) |
| 487 | writeOAuthError(w, http.StatusTooManyRequests, "rate_limited", |
| 488 | "too many client-metadata requests from this IP; try again later") |
| 489 | return |
| 490 | } |
| 491 | clientID := mux.Vars(r)["client_id"] |
| 492 | if clientID == "" { |
| 493 | http.NotFound(w, r) |
| 494 | return |
| 495 | } |
| 496 | var ( |
| 497 | name string |
| 498 | redirects []string |
| 499 | scopes []string |
| 500 | createdAt time.Time |
| 501 | ) |
| 502 | err := a.oauthStorage.Pool().QueryRow(r.Context(), ` |
| 503 | SELECT client_name, redirect_uris, scopes, created_at |
| 504 | FROM oauth_clients |
| 505 | WHERE client_id = $1 |
| 506 | `, clientID).Scan(&name, &redirects, &scopes, &createdAt) |
| 507 | if err != nil { |
| 508 | // Distinguish "not found" (which 404 is the right answer for) |
| 509 | // from a transient DB failure (which the UI should retry |
| 510 | // instead of telling the user "this client isn't registered"). |
| 511 | // pgx.ErrNoRows is the canonical sentinel for an empty row. |
| 512 | if errors.Is(err, pgx.ErrNoRows) { |
| 513 | http.NotFound(w, r) |
| 514 | return |
| 515 | } |
| 516 | log.Printf("[oauth] client metadata lookup failed: client=%q err=%v", clientID, err) |
| 517 | writeOAuthError(w, http.StatusInternalServerError, "server_error", |
| 518 | "client metadata lookup failed; try again") |
| 519 | return |
| 520 | } |
| 521 | w.Header().Set("Content-Type", "application/json") |
| 522 | w.Header().Set("Cache-Control", "public, max-age=60") |
| 523 | accountEligible := len(redirects) > 0 |
| 524 | for _, ru := range redirects { |
| 525 | if !isLoopbackRedirect(ru) { |
| 526 | accountEligible = false |
| 527 | break |
| 528 | } |
| 529 | } |
| 530 | _ = json.NewEncoder(w).Encode(OAuthClientPublicMetadata{ |
| 531 | ClientID: clientID, |
| 532 | ClientName: name, |
| 533 | RedirectURIs: redirects, |
nothing calls this directly
no test coverage detected