Fix/percistent mcp connection (#392)

* feat(oauth): add support for refresh tokens in OAuth flow and update database schema

* feat(prompts): add MCP prompts and corresponding functionality for prompt retrieval

* feat(auth): enhance error handling for refresh token operations and validation
This commit is contained in:
Mattsson
2026-05-05 13:48:53 +02:00
committed by GitHub
parent fa7d4075cf
commit c03582b5c7
10 changed files with 574 additions and 22 deletions
+6
View File
@@ -173,6 +173,12 @@ describe('validateApiKey', () => {
expect(result).toEqual({ error: 'Invalid API key format', status: 401 })
})
it('rejects a refresh token presented as Bearer with a specific message', async () => {
const result = await validateApiKey('gnubok_rt_some_refresh_token')
expect('status' in result && result.status).toBe(401)
expect('error' in result && result.error).toContain('Refresh token')
})
it('rejects when RPC returns error', async () => {
setupMockRpc({ data: null, error: { message: 'db error' } })
+23
View File
@@ -2,6 +2,7 @@ import crypto from 'crypto'
import { createClient } from '@supabase/supabase-js'
const KEY_PREFIX = 'gnubok_sk_'
const REFRESH_TOKEN_PREFIX = 'gnubok_rt_'
// ── API Key Scopes ──────────────────────────────────────────
@@ -140,6 +141,21 @@ export function hashApiKey(key: string): string {
return crypto.createHash('sha256').update(key).digest('hex')
}
export function generateRefreshToken(): { token: string; hash: string } {
const random = crypto.randomBytes(32).toString('base64url')
const token = `${REFRESH_TOKEN_PREFIX}${random}`
const hash = crypto.createHash('sha256').update(token).digest('hex')
return { token, hash }
}
export function hashRefreshToken(token: string): string {
return crypto.createHash('sha256').update(token).digest('hex')
}
export function isRefreshToken(token: string): boolean {
return token.startsWith(REFRESH_TOKEN_PREFIX)
}
export function extractBearerToken(request: Request): string | null {
const authHeader = request.headers.get('authorization')
if (!authHeader?.startsWith('Bearer ')) return null
@@ -170,6 +186,13 @@ export async function validateApiKey(
}
| { error: string; status: number }
> {
if (isRefreshToken(key)) {
return {
error: 'Refresh token cannot be used as access token; exchange it at /api/mcp-oauth/token',
status: 401,
}
}
if (!key.startsWith(KEY_PREFIX)) {
return { error: 'Invalid API key format', status: 401 }
}