aboutsummaryrefslogtreecommitdiff
path: root/apps/mcp/src/auth.ts
blob: 7bd3b9464d103468924954296f308da054d57cf2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * Authentication via API introspection
 *
 * This validates OAuth tokens and API keys by calling the main Supermemory API,
 */

export interface AuthUser {
	userId: string
	apiKey: string
	email?: string
	name?: string
}

/**
 * Check if a token is an API key (starts with "sm_")
 */
export function isApiKey(token: string): boolean {
	return token.startsWith("sm_")
}

/**
 * Validate API key by calling the main API's session endpoint.
 * Returns user info if the API key is valid.
 */
export async function validateApiKey(
	apiKey: string,
	apiUrl: string,
): Promise<AuthUser | null> {
	try {
		const sessionResponse = await fetch(`${apiUrl}/v3/session`, {
			method: "GET",
			headers: {
				Authorization: `Bearer ${apiKey}`,
			},
		})

		if (!sessionResponse.ok) {
			const responseText = await sessionResponse.text()
			const status = sessionResponse.status

			if (status === 401) {
				console.error("API key validation failed: Invalid or expired API key")
			} else if (status === 403) {
				console.error(
					"API key validation failed: User is blocked or access forbidden",
					responseText,
				)
			} else if (status === 429) {
				console.error("API key validation failed: Rate limit exceeded")
			} else if (status >= 500) {
				console.error(
					"API key validation failed: Server error",
					status,
					responseText,
				)
			} else {
				console.error("API key validation failed:", status, responseText)
			}
			return null
		}

		const sessionData = (await sessionResponse.json()) as {
			user?: {
				id?: string
				email?: string
				name?: string
			}
			session?: unknown
			org?: unknown
			error?: string
		} | null

		if (!sessionData?.user?.id) {
			console.error("Missing user.id in session response:", sessionData)
			return null
		}

		console.log("API key validated for user:", sessionData.user.id)

		return {
			userId: sessionData.user.id,
			apiKey: apiKey,
			email: sessionData.user.email,
			name: sessionData.user.name,
		}
	} catch (error) {
		console.error("API key validation error:", error)
		return null
	}
}

/**
 * Validate OAuth token by calling the main API's MCP session endpoint.
 * The main API validates the token via better-auth and returns user info + API key.
 */
export async function validateOAuthToken(
	token: string,
	apiUrl: string,
): Promise<AuthUser | null> {
	try {
		const sessionResponse = await fetch(`${apiUrl}/v3/mcp/session-with-key`, {
			method: "GET",
			headers: {
				Authorization: `Bearer ${token}`,
			},
		})

		if (!sessionResponse.ok) {
			const responseText = await sessionResponse.text()
			const status = sessionResponse.status

			if (status === 401) {
				console.error("Token validation failed: Invalid or expired token")
			} else if (status === 403) {
				console.error(
					"Token validation failed: User is blocked or access forbidden",
					responseText,
				)
			} else if (status === 429) {
				console.error("Token validation failed: Rate limit exceeded")
			} else if (status >= 500) {
				console.error(
					"Token validation failed: Server error",
					status,
					responseText,
				)
			} else {
				console.error("Token validation failed:", status, responseText)
			}
			return null
		}

		const sessionData = (await sessionResponse.json()) as {
			userId?: string
			apiKey?: string
			email?: string
			name?: string
			error?: string
		} | null

		if (!sessionData?.userId || !sessionData?.apiKey) {
			console.error(
				"Missing userId or apiKey in session response:",
				sessionData,
			)
			return null
		}

		console.log("OAuth validated, got API key for user:", sessionData.userId)

		return {
			userId: sessionData.userId,
			apiKey: sessionData.apiKey,
			email: sessionData.email,
			name: sessionData.name,
		}
	} catch (error) {
		console.error("Token validation error:", error)
		return null
	}
}