#18477 · @kamilio · opened Mar 21, 2026 at 1:50 AM UTC · last updated Mar 21, 2026 at 1:57 AM UTC

feat: add Poe OAuth auth plugin

appfeat
52
+77913 files

Score breakdown

Impact

8.0

Clarity

9.0

Urgency

4.0

Ease Of Review

6.0

Guidelines

9.0

Readiness

7.0

Size

1.0

Trust

5.0

Traction

2.0

Summary

This PR introduces a new Poe OAuth authentication plugin, implementing both browser-based OAuth and manual API key entry. The feature includes comprehensive test coverage, detailed verification steps, and clear screenshots of the user flow. However, the substantial number of added lines presents a significant review burden.

Open in GitHub

Description

Issue for this PR

Closes #18478

Type of change

  • [ ] Bug fix
  • [x] New feature
  • [ ] Refactor / code improvement
  • [ ] Documentation

What does this PR do?

Adds Poe as a built-in auth plugin. The plugin supports two auth methods: browser-based OAuth using PKCE (opens poe.com/oauth/authorize, exchanges the authorization code for an API key via api.poe.com/token) and manual API key entry. The implementation follows the same patterns as the existing Codex auth plugin (local callback server on an ephemeral port, CSRF state validation, PKCE challenge, timeout handling).

The plugin is registered in the INTERNAL_PLUGINS list in src/plugin/index.ts alongside the existing Codex, Copilot, and GitLab plugins.

How did you verify your code works?

  • 12 tests pass covering: OAuth URL construction, browser open flow, error/missing-code/invalid-state callback handling, successful token exchange, expiry calculation, expired token re-login error, plugin provider visibility, ProviderAuth registration, and end-to-end model listing + chat stream with a mock server.
  • bun typecheck passes.
  • Full monorepo bun turbo typecheck passes (13/13 packages).
  • Manual tests performing the OAUth Flow

Screenshots / recordings

poe test ts — opencode 2026-03-20 at 20 43 11

poe test ts — opencode 2026-03-20 at 20 43 25

OpenCode - Poe Authorization Successful 2026-03-20 at 20 43 32

Checklist

  • [x] I have tested my changes locally
  • [x] I have not included unrelated changes in this PR

Linked Issues

#18478 [FEATURE]: Add Poe as a built-in auth provider

View issue

Comments

No comments.

Changed Files

packages/opencode/src/plugin/index.ts

+21
@@ -12,12 +12,13 @@ import { Session } from "../session"
import { NamedError } from "@opencode-ai/util/error"
import { CopilotAuthPlugin } from "./copilot"
import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth"
import { PoeAuthPlugin } from "./poe"
export namespace Plugin {
const log = Log.create({ service: "plugin" })
// Built-in plugins that are directly imported (not installed from npm)
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin]
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin, PoeAuthPlugin]
const state = Instance.state(async () => {
const client = createOpencodeClient({

packages/opencode/src/plugin/poe.ts

+4220
@@ -0,0 +1,422 @@
import type { AuthOuathResult, Hooks, PluginInput } from "@opencode-ai/plugin"
import { Log } from "../util/log"
const log = Log.create({ service: "plugin.poe" })
const CLIENT_ID = "client_728290227fc048cc9262091a1ea197ea"
const AUTHORIZE_URL = "https://poe.com/oauth/authorize"
const TOKEN_URL = "https://api.poe.com/token"
const SCOPE = "apikey:create"
export interface PkceCodes {
verifier: string
challenge: string
}
export interface PoeTokenResponse {
api_key: string
api_key_expires_in?: number | null | undefined
}
interface PendingOAuth {
pkce: PkceCodes
redirectUri: string
state: string
resolve: PromiseWithResolvers<PoeTokenResponse>["resolve"]
reject: PromiseWithResolvers<PoeTokenResponse>["reject"]
}
let oauthServer: Bun.Server<undefined> | undefined
let pendingOAuth: PendingOAuth | undefined
export function resetPoeOAuthForTest() {
pendingOAuth = undefined
stopOAuthServer()
}
export function getPoeExpiry(value: PoeTokenResponse): number {
if (value.api_key_expires_in == null) return Number.MAX_SAFE_INTEGER
return Date.now() + value.api_key_expires_in * 1000
}
export function buildAu

packages/opencode/test/plugin/poe.test.ts

+3550
@@ -0,0 +1,355 @@
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test"
import { EventEmitter } from "events"
import {
PoeAuthPlugin,
buildAuthorizeUrl,
escapeHtml,
getPoeExpiry,
resetPoeOAuthForTest,
HTML_ERROR,
type PkceCodes,
} from "../../src/plugin/poe"
import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { ProviderAuth } from "../../src/provider/auth"
import { ProviderID } from "../../src/provider/schema"
import { Auth } from "../../src/auth"
import { resolvePluginProviders } from "../../src/cli/cmd/providers"
import type { PluginInput } from "@opencode-ai/plugin"
import type { Agent } from "../../src/agent/agent"
import type { MessageV2 } from "../../src/session/message-v2"
const fetch0 = globalThis.fetch
let openCalledWith: string | undefined
mock.module("open", () => ({
default: async (url: string) => {
openCalledWith = url
return new EventEmitter()
},
}))
function input(): PluginInput {
return {
client: undefined as never,
project: undefined as never,
worktree: undefined as never,
directory: process.cwd()