Skip to content

Commit e3a8a16

Browse files
committed
fix: clarify short token setup errors
1 parent d8ce6ba commit e3a8a16

2 files changed

Lines changed: 26 additions & 4 deletions

File tree

server/src/routes/setup.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,24 @@ test('setup complete rejects oversized and overlong public bodies without consum
110110
await app.close();
111111
});
112112

113+
test('setup complete returns a clear error for short tokens without consuming setup code', async () => {
114+
await auth.resetStoredAuthForLocalAdmin();
115+
const setupCode = auth.getOrCreateSetupCode();
116+
const app = Fastify();
117+
await app.register(authRoutes);
118+
119+
const response = await app.inject({
120+
method: 'POST',
121+
url: '/api/setup/complete',
122+
payload: { setupCode, token: 'short' },
123+
});
124+
125+
assert.equal(response.statusCode, 400);
126+
assert.equal(response.json().error, `Token must be at least ${auth.TOKEN_MIN_LENGTH} characters long`);
127+
assert.equal(await auth.completeSetup(setupCode, 'valid-route-secret'), true);
128+
await app.close();
129+
});
130+
113131
test('token rotation rejects oversized and overlong public bodies before verification', async () => {
114132
await auth.resetStoredAuthForLocalAdmin();
115133
await auth.setStoredToken('current-route-secret');

server/src/routes/setup.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const setupCompleteRouteOptions = {
5454
additionalProperties: false,
5555
properties: {
5656
setupCode: { type: 'string', minLength: 1, maxLength: 128 },
57-
token: { type: 'string', minLength: TOKEN_MIN_LENGTH, maxLength: TOKEN_MAX_LENGTH },
57+
token: { type: 'string', minLength: 1, maxLength: TOKEN_MAX_LENGTH },
5858
},
5959
},
6060
},
@@ -69,12 +69,16 @@ const rotateTokenRouteOptions = {
6969
additionalProperties: false,
7070
properties: {
7171
currentToken: { type: 'string', minLength: 1, maxLength: TOKEN_MAX_LENGTH },
72-
nextToken: { type: 'string', minLength: TOKEN_MIN_LENGTH, maxLength: TOKEN_MAX_LENGTH },
72+
nextToken: { type: 'string', minLength: 1, maxLength: TOKEN_MAX_LENGTH },
7373
},
7474
},
7575
},
7676
} as const;
7777

78+
function isTokenValidationError(err: unknown): boolean {
79+
return err instanceof Error && /^Token must be /.test(err.message);
80+
}
81+
7882
export async function authRoutes(app: FastifyInstance) {
7983
app.get('/api/setup/status', async (req) => {
8084
const token = bearerToken(req);
@@ -115,7 +119,7 @@ export async function authRoutes(app: FastifyInstance) {
115119
}
116120
return { success: true, data: { authenticated: true } };
117121
} catch (err) {
118-
reply.status(429);
122+
reply.status(isTokenValidationError(err) ? 400 : 429);
119123
return { success: false, error: err instanceof Error ? err.message : 'Setup failed' };
120124
}
121125
});
@@ -149,7 +153,7 @@ export async function authRoutes(app: FastifyInstance) {
149153
return { success: false, error: 'Current token is invalid' };
150154
}
151155
} catch (err) {
152-
reply.status(409);
156+
reply.status(isTokenValidationError(err) ? 400 : 409);
153157
return { success: false, error: err instanceof Error ? err.message : 'Token rotation failed' };
154158
}
155159
return { success: true, data: { rotated: true } };

0 commit comments

Comments
 (0)