| 141 | * revoked" body (some endpoints signal revocation this way instead of 401). |
| 142 | */ |
| 143 | export async function withOAuth401Retry<T>( |
| 144 | request: () => Promise<T>, |
| 145 | opts?: { also403Revoked?: boolean }, |
| 146 | ): Promise<T> { |
| 147 | const initialSession = getAuthRuntime().getCurrentSession() |
| 148 | if (initialSession.principalSource === 'managed_oauth') { |
| 149 | await getAuthRuntime().resolveSession({ allowRefresh: true }) |
| 150 | } |
| 151 | |
| 152 | try { |
| 153 | return await request() |
| 154 | } catch (err) { |
| 155 | if (!axios.isAxiosError(err)) throw err |
| 156 | const status = err.response?.status |
| 157 | const isAuthError = |
| 158 | status === 401 || |
| 159 | (opts?.also403Revoked && |
| 160 | status === 403 && |
| 161 | typeof err.response?.data === 'string' && |
| 162 | err.response.data.includes('OAuth token has been revoked')) |
| 163 | if (!isAuthError) throw err |
| 164 | const failedAccessToken = getAuthRuntime().getCurrentSession().accessToken |
| 165 | if (!failedAccessToken) throw err |
| 166 | const recovered = |
| 167 | await getAuthRuntime().recoverManagedOAuth401(failedAccessToken) |
| 168 | if (!recovered) throw err |
| 169 | return await request() |
| 170 | } |
| 171 | } |