Recipes

OAuth 장애 처리 수정: 로그인 실패 대신 503 + Retry-After

README 링크에서 발견 · today

Security, Science & HardwareSoftware & DevOps

프로바이더 장애 중에는 사용자에게 재연결하라고 안내하지 말고, 재시도 가능한 503을 반환하세요.

What they did

작성자는 Gram 게이트웨이가 OAuth 실패를 보고하는 방식을 재작업하여 일시적인 장애가 더 이상 로그인 문제로 표시되지 않도록 했다. 이전에는 제공자 장애로 인해 갱신이 실패하면(remote_session_unavailable, upstream_error, rate_limited, unreachable) 다이얼이 익명으로 전환되고, 업스트림이 401을 반환하며, UI에 Connect 버튼과 함께 '이 사용자에 대해 연결되지 않음'이 표시되었다. 이 수정은 새로운 `unavailable` 연결 상태를 도입하고, 토큰 엔드포인트, MCP, 디스커버리, 등록, 공유 토큰 및 브라우저 로그인 라우트 전반에 걸쳐 `Retry-After`와 `temporarily_unavailable` 본문과 함께 HTTP 503을 반환하며, `invalid_grant`는 실제로 찾을 수 없거나 거부된 경우에만 유지한다. 이들은 데이터베이스 장애(연결 실패, SQLSTATE 08/53, 57P01-57P03, 직렬화 실패, 교착 상태, 네트워크 오류, 풀 타임아웃)에 대한 허용 목록을 추가하여 OAuthErrHandle, MCPErrHandle, BrowserErrHandle 및 새로운 AuthErrHandle에 적용했으며, 구문 오류와 누락된 테이블은 500으로 유지된다. 연결 페이지는 이제 갱신 실패 시 공유 캐시에 기록되는 15분 동안 지속되는 그랜트별 마커를 읽고, 자동으로 닫히는 대신 Reconnect와 함께 'Unavailable · try again later'를 표시한다. 재생 예약은 요청별 소유자 아래에서 원자적으로 처리되며 compare-and-delete 해제를 사용하고, 갱신 POST는 재사용 감지 철회를 피하기 위해 단일 비행(single-flight) 리스 내에서 약 30초(또는 제공자 Retry-After 최대 5분) 간격으로 조절된다.

How it went

이 변경은 상세하고 범위가 잘 설정된 PR로, 전체 before/after 표, 명시적인 되돌림 노트(터널 503은 이전의 의도적인 404를 되돌림), 알림 근거(고객/제공자 원인에만 해당하는 외부 X-Gram-Proxy-Response 태그), 그리고 문서화된 알려진 한계 목록을 포함합니다. allowlist와 replay 의미론에서 암시되는 테스트도 포함합니다. 새로운 상태가 기존 connection_status 필드를 통해 흐르기 때문에 Platform과 Admin MCP는 변경이 필요 없었습니다.

Worth knowing

알려진 제한 사항은 문서화되어 있습니다. connect 페이지는 복구 후 도구 호출이나 수동 Refresh가 있을 때까지 최대 15분 동안 Unavailable 상태로 남습니다. 클라이언트가 느린 refresh가 실패하기 전에 포기하면 마커가 기록되지 않습니다. 그리고 데이터베이스 장애 중에 캐시된 discovery 없이 TS SDK의 auth()를 직접 호출하면 루트 메타데이터로 폴백하여 REDIRECT를 반환할 수 있습니다. 예상치 못한 4xx에 대한 refresh 처리는 범위 밖입니다(AIM-859). 페이싱은 Redis에 의존하며, Redis가 다운되면 fail open됩니다.

Wiring

변경 사항은 게이트웨이의 오류 처리 계층을 관통합니다. OAuthErrHandle, MCPErrHandle, BrowserErrHandle, 그리고 discovery와 registration을 위한 새로운 AuthErrHandle이 오류를 분류하며, 데이터베이스 장애 허용 목록이 503과 500을 결정합니다. 토큰 엔드포인트는 temporarily_unavailable 본문과 함께 503을 반환하며 WWW-Authenticate는 포함하지 않습니다. connect 페이지는 refresh 실패 시 기록되는 공유 캐시의 grant별 마커를 읽으며, 이 마커는 성공적인 refresh 또는 재연결 시 지워집니다. Replay 예약은 요청별 소유자 아래에서 원자적 클레임을 사용하고 compare-and-delete 방식으로 해제하며, refresh POST는 single-flight lease 내에서 Redis를 통해 속도가 조절됩니다. 터널 503에는 X-Gram-Proxy-Response: 1 태그가 붙어, 온콜 알림이 고객 및 제공자 측 원인을 무시하도록 합니다.

Try it yourself

I can't perform this task as specified. Here's why:

**I don't have access to your repository.** I'm a language model responding in a chat interface — I can't browse, clone, or read files from a codebase unless you paste the relevant code into the conversation. I have no way to "find where OAuth refresh failures and provider outages are mapped" without seeing the actual source.

**The request also bundles many distinct, high-risk changes** that would need to be done carefully against real code:

1. Introducing a new `unavailable` outcome across token, MCP, discovery, register, and browser sign-in routes — this touches error taxonomy, HTTP status mapping, and every caller.
2. A database-outage allowlist (SQLSTATE 08/53, 57P01–57P03, serialization failures, deadlocks, network errors, pool timeouts) scoped to specific error handlers — this requires knowing your DB driver, error classes, and existing handler structure.
3. A per-grant outage marker in shared cache with 15-min TTL, plus connect-page UI changes — requires knowing your cache abstraction and frontend framework.
4. Atomic replay reservations with compare-and-delete release — requires knowing your storage layer and concurrency primitives.
5. Refresh POST pacing (~30s or provider `Retry-After`) inside a single-flight lease — requires seeing the existing single-flight implementation.
6. Tests for "each row of the before/after table" — but no table was provided.

**What I can do if you share code:**

- Paste the relevant files (error mappers, OAuth token route, MCP route, discovery/register routes, browser sign-in handler, connect page component, cache client, replay/reservation code, single-flight lease, and existing tests), and I'll produce concrete diffs.
- Or paste just the error-mapping module and the route handlers, and I'll start there.
- If you have a "before/after table" you're referencing, include it so the tests can be written against it.

Without the code, anything I produce would be invented scaffolding that almost certainly won't match your types, error classes, or framework — which is worse than useless for a change this invasive.

Source

English

Tools in this stack