mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 17:46:06 +00:00
feat(admin): let a token read the growth scoreboard without a session
The weekly digest reads /api/admin/growth from a script, which has no browser and therefore no NextAuth session. The alternative was copying a session cookie out of a browser by hand: those are JWTs with a 30-day lifetime, so a scheduled job built on one stops working a month later and reports nothing rather than reporting a failure. The token path is off unless OPENFRAME_ADMIN_API_TOKEN is set, so an instance that never sets it keeps session-only admin access. A value under 32 characters is treated as no token at all: behind this header sit every paying account's name, email and usage, and a short token is a guessable path to all of it. Comparison runs over SHA-256 digests so it stays constant time without leaking the token's length.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import { auth } from '@/lib/auth';
|
||||
import { isAdminApiTokenRequest } from '@/lib/admin-api-token';
|
||||
import { apiErrors, successResponse, withCacheControl } from '@/lib/api-response';
|
||||
import { conversionRates, getScoreboard } from '@/lib/analytics/scoreboard';
|
||||
import { isProductAnalyticsEnabled } from '@/lib/feature-flags';
|
||||
@@ -9,12 +10,17 @@ import { logError } from '@/lib/logger';
|
||||
// the scoreboard instead of somebody retyping it into a table.
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) {
|
||||
return apiErrors.unauthorized();
|
||||
}
|
||||
if (!session.user.isAdmin) {
|
||||
return apiErrors.forbidden('Admin access required');
|
||||
// A bearer token stands in for the admin session so the weekly digest can
|
||||
// read this without a browser. Unset by default, in which case the only way
|
||||
// in is still an admin session.
|
||||
if (!isAdminApiTokenRequest(request)) {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) {
|
||||
return apiErrors.unauthorized();
|
||||
}
|
||||
if (!session.user.isAdmin) {
|
||||
return apiErrors.forbidden('Admin access required');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isProductAnalyticsEnabled()) {
|
||||
|
||||
Reference in New Issue
Block a user