|
1 | | -import { CombinedGraphQLErrors } from "@apollo/client"; |
2 | 1 | import { getIronSession } from "iron-session"; |
3 | 2 | import { cookies } from "next/headers"; |
4 | | -import { makeClient } from "./apollo"; |
| 3 | +import z from "zod"; |
5 | 4 | import buildUri from "./build-uri"; |
6 | | -import { BASIC_USER_INFO_QUERY, type BasicUserInfo, isAdmin } from "./user"; |
7 | 5 |
|
8 | 6 | // Constants for OAuth 2.0 PKCE flow |
9 | 7 | export const OAUTH_CONFIG = { |
@@ -225,45 +223,77 @@ export interface AuthStatus { |
225 | 223 | loggedIn: boolean; |
226 | 224 |
|
227 | 225 | role?: "admin" | "user"; |
228 | | - user?: BasicUserInfo; |
| 226 | + introspectResult?: z.infer<typeof introspectSchema>; |
229 | 227 | } |
230 | 228 |
|
| 229 | +export const introspectSchema = z.union([ |
| 230 | + z.object({ |
| 231 | + active: z.literal(true), |
| 232 | + scope: z.string().transform((scope) => scope.split(" ")).describe("the scopes of the token"), |
| 233 | + sub: z.string().describe("the subject of the token"), |
| 234 | + exp: z.number().describe("the time the token expires"), |
| 235 | + iat: z.number().describe("the time the token was issued"), |
| 236 | + azp: z.string().describe("the machine that is authorized to use this token"), |
| 237 | + }), |
| 238 | + z.object({ |
| 239 | + active: z.literal(false), |
| 240 | + }), |
| 241 | +]); |
| 242 | + |
231 | 243 | export async function getAuthStatus(): Promise<AuthStatus> { |
232 | 244 | const token = await getAuthToken(); |
233 | 245 | if (!token) { |
234 | 246 | return { |
235 | 247 | loggedIn: false, |
| 248 | + introspectResult: undefined, |
236 | 249 | }; |
237 | 250 | } |
238 | 251 |
|
239 | 252 | // get user info |
240 | | - const client = makeClient({ token }); |
241 | | - |
242 | | - try { |
243 | | - const { data } = await client.query({ |
244 | | - query: BASIC_USER_INFO_QUERY, |
245 | | - }); |
246 | | - if (!data) { |
247 | | - return { |
248 | | - loggedIn: true, |
249 | | - }; |
250 | | - } |
| 253 | + const response = await fetch(buildUri("/api/auth/v2/introspect"), { |
| 254 | + method: "POST", |
| 255 | + headers: { |
| 256 | + "Content-Type": "application/x-www-form-urlencoded", |
| 257 | + }, |
| 258 | + body: new URLSearchParams({ |
| 259 | + token, |
| 260 | + token_type_hint: "access_token", |
| 261 | + }), |
| 262 | + }); |
251 | 263 |
|
| 264 | + if (!response.ok) { |
| 265 | + console.error("Error validating auth:", response.status, response.statusText); |
252 | 266 | return { |
253 | | - role: isAdmin(data?.me) ? "admin" : "user", |
254 | | - user: data.me, |
255 | | - loggedIn: true, |
| 267 | + loggedIn: false, |
| 268 | + introspectResult: undefined, |
256 | 269 | }; |
257 | | - } catch (error) { |
258 | | - if (CombinedGraphQLErrors.is(error) && error.message === "require authentication") { |
259 | | - return { |
260 | | - loggedIn: false, |
261 | | - }; |
262 | | - } |
263 | | - |
264 | | - console.log("Error validating auth:", error); |
| 270 | + } |
| 271 | + |
| 272 | + const data = await response.json(); |
| 273 | + const parsedData = introspectSchema.safeParse(data); |
| 274 | + |
| 275 | + if (!parsedData.success) { |
| 276 | + console.error("Error validating auth:", parsedData.error); |
265 | 277 | return { |
266 | 278 | loggedIn: false, |
| 279 | + introspectResult: undefined, |
267 | 280 | }; |
268 | 281 | } |
| 282 | + |
| 283 | + if (!parsedData.data.active) { |
| 284 | + return { |
| 285 | + loggedIn: false, |
| 286 | + introspectResult: parsedData.data, |
| 287 | + }; |
| 288 | + } |
| 289 | + |
| 290 | + if (parsedData.data.scope.includes("*")) { |
| 291 | + return { |
| 292 | + loggedIn: true, |
| 293 | + role: "admin", |
| 294 | + introspectResult: parsedData.data, |
| 295 | + }; |
| 296 | + } |
| 297 | + |
| 298 | + return { loggedIn: true, role: "user", introspectResult: parsedData.data }; |
269 | 299 | } |
0 commit comments