fix(provider): type auth errors (#27301)

This commit is contained in:
Shoubhit Dash
2026-05-13 15:47:13 +05:30
committed by GitHub
parent 733bd3c74e
commit 809af5c590
6 changed files with 200 additions and 32 deletions

View File

@@ -10,6 +10,26 @@ import { described } from "./metadata"
const root = "/provider"
const ProviderAuthErrorName = Schema.Union([
Schema.Literal("BadRequest"),
Schema.Literal("ProviderAuthOauthMissing"),
Schema.Literal("ProviderAuthOauthCodeMissing"),
Schema.Literal("ProviderAuthOauthCallbackFailed"),
Schema.Literal("ProviderAuthValidationFailed"),
])
export class ProviderAuthApiError extends Schema.ErrorClass<ProviderAuthApiError>("ProviderAuthError")(
{
name: ProviderAuthErrorName,
data: Schema.Struct({
providerID: Schema.optional(ProviderID),
field: Schema.optional(Schema.String),
message: Schema.optional(Schema.String),
kind: Schema.optional(Schema.String),
}),
},
{ httpApiStatus: 400 },
) {}
export const ProviderApi = HttpApi.make("provider")
.add(
HttpApiGroup.make("provider")
@@ -39,7 +59,7 @@ export const ProviderApi = HttpApi.make("provider")
query: WorkspaceRoutingQuery,
payload: ProviderAuth.AuthorizeInput,
success: described(Schema.UndefinedOr(ProviderAuth.Authorization), "Authorization URL and method"),
error: HttpApiError.BadRequest,
error: ProviderAuthApiError,
}).annotateMerge(
OpenApi.annotations({
identifier: "provider.oauth.authorize",
@@ -52,7 +72,7 @@ export const ProviderApi = HttpApi.make("provider")
query: WorkspaceRoutingQuery,
payload: ProviderAuth.CallbackInput,
success: described(Schema.Boolean, "OAuth callback processed successfully"),
error: HttpApiError.BadRequest,
error: ProviderAuthApiError,
}).annotateMerge(
OpenApi.annotations({
identifier: "provider.oauth.callback",

View File

@@ -6,8 +6,29 @@ import { ProviderID } from "@/provider/schema"
import { mapValues } from "remeda"
import { Effect, Schema } from "effect"
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../api"
import { ProviderAuthApiError } from "../groups/provider"
function mapProviderAuthError<A, R>(self: Effect.Effect<A, ProviderAuth.Error, R>) {
return self.pipe(
Effect.mapError((error) => {
if (error instanceof ProviderAuth.OauthMissing) {
return new ProviderAuthApiError({ name: error._tag, data: { providerID: error.providerID } })
}
if (error instanceof ProviderAuth.OauthCodeMissing) {
return new ProviderAuthApiError({ name: error._tag, data: { providerID: error.providerID } })
}
if (error instanceof ProviderAuth.OauthCallbackFailed) {
return new ProviderAuthApiError({ name: error._tag, data: {} })
}
if (error instanceof ProviderAuth.ValidationFailed) {
return new ProviderAuthApiError({ name: error._tag, data: { field: error.field, message: error.message } })
}
return new ProviderAuthApiError({ name: "BadRequest", data: {} })
}),
)
}
export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider", (handlers) =>
Effect.gen(function* () {
@@ -44,13 +65,13 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
params: { providerID: ProviderID }
payload: ProviderAuth.AuthorizeInput
}) {
return yield* svc
.authorize({
return yield* mapProviderAuthError(
svc.authorize({
providerID: ctx.params.providerID,
method: ctx.payload.method,
inputs: ctx.payload.inputs,
})
.pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
}),
)
})
const authorizeRaw = Effect.fn("ProviderHttpApi.authorizeRaw")(function* (ctx: {
@@ -59,7 +80,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
}) {
const body = yield* Effect.orDie(ctx.request.text)
const payload = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ProviderAuth.AuthorizeInput))(body).pipe(
Effect.mapError(() => new HttpApiError.BadRequest({})),
Effect.mapError(() => new ProviderAuthApiError({ name: "BadRequest", data: {} })),
)
// Match legacy route behavior: when authorize() resolves without a
// result (e.g. no further redirect), serialize as JSON `null` instead
@@ -72,13 +93,13 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
params: { providerID: ProviderID }
payload: ProviderAuth.CallbackInput
}) {
yield* svc
.callback({
yield* mapProviderAuthError(
svc.callback({
providerID: ctx.params.providerID,
method: ctx.payload.method,
code: ctx.payload.code,
})
.pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
}),
)
return true
})

View File

@@ -28,7 +28,6 @@ export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect)
HttpServerResponse.jsonUnsafe(error.toObject(), {
status: iife(() => {
if (error instanceof Provider.ModelNotFoundError) return 400
if (error.name === "ProviderAuthValidationFailed") return 400
return 500
}),
}),