Files
opencode/packages/server/src/auth.ts
2026-07-03 13:58:26 -04:00

65 lines
1.8 KiB
TypeScript

export * as ServerAuth from "./auth"
import { Context, Effect, Layer, Option, Redacted } from "effect"
import { all, option, string, withDefault } from "effect/Config"
export type Credentials = {
password?: string
username?: string
}
export type DecodedCredentials = {
readonly username: string
readonly password: Redacted.Redacted
}
export type Info = {
readonly password: Option.Option<string>
readonly username: string
}
export class Config extends Context.Service<Config, Info>()("@opencode/ServerAuthConfig") {
static configLayer(input: Info) {
return Layer.succeed(this, this.of(input))
}
static get layer() {
return Layer.effect(
this,
Effect.gen(function* () {
return Config.of(
yield* all({
password: string("OPENCODE_SERVER_PASSWORD").pipe(option),
username: string("OPENCODE_SERVER_USERNAME").pipe(withDefault("opencode")),
}),
)
}),
)
}
}
export function required(config: Info) {
return Option.isSome(config.password) && config.password.value !== ""
}
export function authorized(credentials: DecodedCredentials, config: Info) {
return (
Option.isSome(config.password) &&
credentials.username === config.username &&
Redacted.value(credentials.password) === config.password.value
)
}
export function header(credentials?: Credentials) {
const password = credentials?.password ?? process.env.OPENCODE_SERVER_PASSWORD
if (!password) return undefined
return `Basic ${Buffer.from(`${credentials?.username ?? process.env.OPENCODE_SERVER_USERNAME ?? "opencode"}:${password}`).toString("base64")}`
}
export function headers(credentials?: Credentials) {
const authorization = header(credentials)
if (!authorization) return undefined
return { Authorization: authorization }
}