|
|
@@ -80,12 +80,33 @@ function requestAuthorize(input: {
|
|
|
providerID: string
|
|
|
method: number
|
|
|
headers: HeadersInit
|
|
|
+ inputs?: Record<string, string>
|
|
|
}) {
|
|
|
return Effect.promise(async () => {
|
|
|
const response = await input.app.request(`/provider/${input.providerID}/oauth/authorize`, {
|
|
|
method: "POST",
|
|
|
headers: input.headers,
|
|
|
- body: JSON.stringify({ method: input.method }),
|
|
|
+ body: JSON.stringify({ method: input.method, ...(input.inputs ? { inputs: input.inputs } : {}) }),
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ status: response.status,
|
|
|
+ body: await response.text(),
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function requestCallback(input: {
|
|
|
+ app: ReturnType<typeof app>
|
|
|
+ providerID: string
|
|
|
+ method: number
|
|
|
+ headers: HeadersInit
|
|
|
+ code?: string
|
|
|
+}) {
|
|
|
+ return Effect.promise(async () => {
|
|
|
+ const response = await input.app.request(`/provider/${input.providerID}/oauth/callback`, {
|
|
|
+ method: "POST",
|
|
|
+ headers: input.headers,
|
|
|
+ body: JSON.stringify({ method: input.method, ...(input.code ? { code: input.code } : {}) }),
|
|
|
})
|
|
|
return {
|
|
|
status: response.status,
|
|
|
@@ -128,6 +149,47 @@ function writeProviderAuthPlugin(dir: string) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+function writeProviderAuthValidationPlugin(dir: string) {
|
|
|
+ return Effect.gen(function* () {
|
|
|
+ const fs = yield* AppFileSystem.Service
|
|
|
+
|
|
|
+ yield* fs.writeWithDirs(
|
|
|
+ path.join(dir, ".opencode", "plugin", "provider-oauth-validation.ts"),
|
|
|
+ [
|
|
|
+ "export default {",
|
|
|
+ ' id: "test.provider-oauth-validation",',
|
|
|
+ " server: async () => ({",
|
|
|
+ " auth: {",
|
|
|
+ ' provider: "test-oauth-validation",',
|
|
|
+ " methods: [",
|
|
|
+ " {",
|
|
|
+ ' type: "oauth",',
|
|
|
+ ' label: "OAuth",',
|
|
|
+ " prompts: [",
|
|
|
+ " {",
|
|
|
+ ' type: "text",',
|
|
|
+ ' key: "token",',
|
|
|
+ ' message: "Token",',
|
|
|
+ " validate: (value) => value === 'ok' ? undefined : 'Token must be ok',",
|
|
|
+ " },",
|
|
|
+ " ],",
|
|
|
+ " authorize: async () => ({",
|
|
|
+ ` url: "${oauthURL}",`,
|
|
|
+ ' method: "code",',
|
|
|
+ ` instructions: "${oauthInstructions}",`,
|
|
|
+ " callback: async () => ({ type: 'success', key: 'token' }),",
|
|
|
+ " }),",
|
|
|
+ " },",
|
|
|
+ " ],",
|
|
|
+ " },",
|
|
|
+ " }),",
|
|
|
+ "}",
|
|
|
+ "",
|
|
|
+ ].join("\n"),
|
|
|
+ )
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
function writeFunctionOptionsPlugin(dir: string) {
|
|
|
return Effect.gen(function* () {
|
|
|
const fs = yield* AppFileSystem.Service
|
|
|
@@ -240,6 +302,51 @@ describe("provider HttpApi", () => {
|
|
|
})
|
|
|
}),
|
|
|
projectOptions,
|
|
|
+ 30000,
|
|
|
+ )
|
|
|
+
|
|
|
+ it.instance(
|
|
|
+ "returns declared provider auth validation errors",
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const instance = yield* TestInstance
|
|
|
+ yield* writeProviderAuthValidationPlugin(instance.directory)
|
|
|
+ const response = yield* requestAuthorize({
|
|
|
+ app: app(),
|
|
|
+ providerID: "test-oauth-validation",
|
|
|
+ method: 0,
|
|
|
+ inputs: { token: "nope" },
|
|
|
+ headers: { "x-opencode-directory": instance.directory, "content-type": "application/json" },
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(response.status).toBe(400)
|
|
|
+ expect(JSON.parse(response.body)).toEqual({
|
|
|
+ name: "ProviderAuthValidationFailed",
|
|
|
+ data: { field: "token", message: "Token must be ok" },
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ projectOptions,
|
|
|
+ 30000,
|
|
|
+ )
|
|
|
+
|
|
|
+ it.instance(
|
|
|
+ "returns declared provider auth callback errors",
|
|
|
+ Effect.gen(function* () {
|
|
|
+ const instance = yield* TestInstance
|
|
|
+ const response = yield* requestCallback({
|
|
|
+ app: app(),
|
|
|
+ providerID,
|
|
|
+ method: 0,
|
|
|
+ headers: { "x-opencode-directory": instance.directory, "content-type": "application/json" },
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(response.status).toBe(400)
|
|
|
+ expect(JSON.parse(response.body)).toEqual({
|
|
|
+ name: "ProviderAuthOauthMissing",
|
|
|
+ data: { providerID },
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ projectOptions,
|
|
|
+ 30000,
|
|
|
)
|
|
|
|
|
|
it.instance(
|