kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/715/head
rodzic
6d6557c720
commit
9dbd7ddd2e
|
@ -2,20 +2,19 @@ import { assert, pick } from '@agentic/platform-core'
|
||||||
import { issuer } from '@openauthjs/openauth'
|
import { issuer } from '@openauthjs/openauth'
|
||||||
import { GithubProvider } from '@openauthjs/openauth/provider/github'
|
import { GithubProvider } from '@openauthjs/openauth/provider/github'
|
||||||
import { PasswordProvider } from '@openauthjs/openauth/provider/password'
|
import { PasswordProvider } from '@openauthjs/openauth/provider/password'
|
||||||
import { MemoryStorage } from '@openauthjs/openauth/storage/memory'
|
|
||||||
import { PasswordUI } from '@openauthjs/openauth/ui/password'
|
import { PasswordUI } from '@openauthjs/openauth/ui/password'
|
||||||
|
|
||||||
import { type RawUser } from '@/db'
|
import { type RawUser } from '@/db'
|
||||||
import { subjects } from '@/lib/auth/subjects'
|
import { subjects } from '@/lib/auth/subjects'
|
||||||
import { upsertOrLinkUserAccount } from '@/lib/auth/upsert-or-link-user-account'
|
import { upsertOrLinkUserAccount } from '@/lib/auth/upsert-or-link-user-account'
|
||||||
|
import { DrizzleAuthStorage } from '@/lib/drizzle-auth-storage'
|
||||||
import { env } from '@/lib/env'
|
import { env } from '@/lib/env'
|
||||||
import { getGitHubClient } from '@/lib/external/github'
|
import { getGitHubClient } from '@/lib/external/github'
|
||||||
|
|
||||||
|
// Initialize OpenAuth issuer which is a Hono app for all auth routes.
|
||||||
export const authRouter = issuer({
|
export const authRouter = issuer({
|
||||||
subjects,
|
subjects,
|
||||||
storage: MemoryStorage({
|
storage: DrizzleAuthStorage(),
|
||||||
persist: './auth-db-temp.json'
|
|
||||||
}),
|
|
||||||
providers: {
|
providers: {
|
||||||
github: GithubProvider({
|
github: GithubProvider({
|
||||||
clientID: env.GITHUB_CLIENT_ID,
|
clientID: env.GITHUB_CLIENT_ID,
|
||||||
|
@ -25,6 +24,7 @@ export const authRouter = issuer({
|
||||||
password: PasswordProvider(
|
password: PasswordProvider(
|
||||||
PasswordUI({
|
PasswordUI({
|
||||||
sendCode: async (email, code) => {
|
sendCode: async (email, code) => {
|
||||||
|
// TODO: Send email code to user
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log({ email, code })
|
console.log({ email, code })
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,8 @@ export const authRouter = issuer({
|
||||||
accountId: value.email
|
accountId: value.email
|
||||||
},
|
},
|
||||||
partialUser: {
|
partialUser: {
|
||||||
email: value.email
|
email: value.email,
|
||||||
|
emailVerified: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { jsonb, pgTable, text, timestamp } from '@fisch0920/drizzle-orm/pg-core'
|
||||||
|
|
||||||
|
import { timestamps } from './common'
|
||||||
|
|
||||||
|
// Simple key-value store of JSON data for OpenAuth-related state.
|
||||||
|
export const authData = pgTable('auth_data', {
|
||||||
|
// Example ID keys:
|
||||||
|
// "oauth:refresh\u001fuser:f99d3004946f9abb\u001f2cae301e-3fdc-40c4-8cda-83b25a616d06"
|
||||||
|
// "signing:key\u001ff001a516-838d-4c88-aa9e-719d8fc9d5a3"
|
||||||
|
// "email\u001ft@t.com\u001fpassword"
|
||||||
|
// "encryption:key\u001f14d3c324-f9c7-4867-81a9-b0b77b0db0be"
|
||||||
|
id: text().primaryKey(),
|
||||||
|
...timestamps,
|
||||||
|
|
||||||
|
value: jsonb().$type<Record<string, any>>().notNull(),
|
||||||
|
expiry: timestamp()
|
||||||
|
})
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './account'
|
export * from './account'
|
||||||
|
export * from './auth-data'
|
||||||
export * from './common'
|
export * from './common'
|
||||||
export * from './consumer'
|
export * from './consumer'
|
||||||
export * from './deployment'
|
export * from './deployment'
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import {
|
||||||
|
joinKey,
|
||||||
|
splitKey,
|
||||||
|
type StorageAdapter
|
||||||
|
} from '@openauthjs/openauth/storage/storage'
|
||||||
|
|
||||||
|
import { and, db, eq, isNull, like, lt, or, schema } from '@/db'
|
||||||
|
|
||||||
|
export function DrizzleAuthStorage(): StorageAdapter {
|
||||||
|
return {
|
||||||
|
async get(key: string[]) {
|
||||||
|
const id = joinKey(key)
|
||||||
|
const entry = await db.query.authData.findFirst({
|
||||||
|
where: eq(schema.authData.id, id)
|
||||||
|
})
|
||||||
|
if (!entry) return undefined
|
||||||
|
|
||||||
|
if (entry.expiry && Date.now() >= entry.expiry.getTime()) {
|
||||||
|
await db.delete(schema.authData).where(eq(schema.authData.id, id))
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.value
|
||||||
|
},
|
||||||
|
|
||||||
|
async set(key: string[], value: Record<string, any>, expiry?: Date) {
|
||||||
|
const id = joinKey(key)
|
||||||
|
|
||||||
|
await db
|
||||||
|
.insert(schema.authData)
|
||||||
|
.values({
|
||||||
|
id,
|
||||||
|
value,
|
||||||
|
expiry
|
||||||
|
})
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: schema.authData.id,
|
||||||
|
set: {
|
||||||
|
value,
|
||||||
|
expiry: expiry ?? null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async remove(key: string[]) {
|
||||||
|
const id = joinKey(key)
|
||||||
|
await db.delete(schema.authData).where(eq(schema.authData.id, id))
|
||||||
|
},
|
||||||
|
|
||||||
|
async *scan(prefix: string[]) {
|
||||||
|
const now = new Date()
|
||||||
|
const idPrefix = joinKey(prefix)
|
||||||
|
|
||||||
|
const entries = await db.query.authData.findMany({
|
||||||
|
where: and(
|
||||||
|
like(schema.authData.id, `${idPrefix}%`),
|
||||||
|
or(isNull(schema.authData.expiry), lt(schema.authData.expiry, now))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
yield [splitKey(entry.id), entry.value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,8 +32,10 @@ app.use(middleware.accessLogger)
|
||||||
app.use(middleware.responseTime)
|
app.use(middleware.responseTime)
|
||||||
app.use(middleware.errorHandler)
|
app.use(middleware.errorHandler)
|
||||||
|
|
||||||
|
// Mount all auth routes which are handled by OpenAuth
|
||||||
app.route('', authRouter)
|
app.route('', authRouter)
|
||||||
|
|
||||||
|
// Mount all v1 API routes
|
||||||
app.route('/v1', apiV1)
|
app.route('/v1', apiV1)
|
||||||
|
|
||||||
app.doc31('/docs', {
|
app.doc31('/docs', {
|
||||||
|
|
|
@ -31,11 +31,8 @@ async function main() {
|
||||||
if (authSession) {
|
if (authSession) {
|
||||||
try {
|
try {
|
||||||
await client.setRefreshAuthToken(authSession.refreshToken)
|
await client.setRefreshAuthToken(authSession.refreshToken)
|
||||||
} catch (err: any) {
|
} catch {
|
||||||
console.warn(
|
console.warn('Existing auth session is invalid; logging out.\n')
|
||||||
'Existing auth session is invalid; logging out...',
|
|
||||||
err.message
|
|
||||||
)
|
|
||||||
AuthStore.clearAuth()
|
AuthStore.clearAuth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
readme.md
18
readme.md
|
@ -13,24 +13,10 @@
|
||||||
- like https://github.com/tierrun/tier and Saasify
|
- like https://github.com/tierrun/tier and Saasify
|
||||||
- https://github.com/tierrun/tier/blob/main/pricing/schema.json
|
- https://github.com/tierrun/tier/blob/main/pricing/schema.json
|
||||||
- https://blog.tier.run/tier-hello-world-demo
|
- https://blog.tier.run/tier-hello-world-demo
|
||||||
- auth
|
|
||||||
- decide on approach for auth
|
|
||||||
- built-in, first-party, tight coupling
|
|
||||||
- https://www.better-auth.com
|
|
||||||
- issues
|
|
||||||
- doesn't allow dynamic social provider config
|
|
||||||
- awkward schema cli generation and constraints
|
|
||||||
- awkward cookie-only session support (need JWTs for CLI and SDK)
|
|
||||||
- client uses dynamic proxy for methods which makes DX awkward
|
|
||||||
- should be able to use custom `ky`-based client
|
|
||||||
- drizzle pg adapter requires `Date` timestamps instead of default strings
|
|
||||||
- https://github.com/toolbeam/openauth
|
|
||||||
- https://github.com/aipotheosis-labs/aci/tree/main/backend/apps
|
|
||||||
- https://github.com/NangoHQ/nango
|
|
||||||
- https://github.com/transitive-bullshit?submit=Search&q=oauth&tab=stars&type=&sort=&direction=&submit=Search
|
|
||||||
- clerk / workos / auth0
|
|
||||||
- consider switching to [consola](https://github.com/unjs/consola) for logging?
|
- consider switching to [consola](https://github.com/unjs/consola) for logging?
|
||||||
- consider switching to `bun` (for `--hot` reloading!!)
|
- consider switching to `bun` (for `--hot` reloading!!)
|
||||||
|
- transactional emails
|
||||||
|
- openauth password emails and `sendCode`
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue