Redeem refuses a target the completion did not record #10
2 changed files with 12 additions and 2 deletions
|
|
@ -2220,7 +2220,7 @@ export async function buildApp(options: BuildAppOptions = {}): Promise<FastifyIn
|
|||
// the route stays open to it. A link completion was minted for an
|
||||
// initiator the authenticated app named at start; only that app's
|
||||
// backend redeems it. Redeem is also where the connection target is
|
||||
// held to what the signed state named. Both refusals read the row
|
||||
// held to what the signed state named, both ways: a target the state never named is refused too. Both refusals read the row
|
||||
// without consuming it, so a wrong caller or target cannot burn the code.
|
||||
const pending = await store.findAuthCompletion(request.params.appSlug, request.body.completionCode);
|
||||
if (pending && !pending.consumedAt && pending.expiresAt > nowIso) {
|
||||
|
|
@ -2228,7 +2228,7 @@ export async function buildApp(options: BuildAppOptions = {}): Promise<FastifyIn
|
|||
reply.code(401);
|
||||
return { error: "app_auth_required" };
|
||||
}
|
||||
if (pending.targetId !== undefined && pending.targetId !== request.body.targetId) {
|
||||
if (pending.targetId !== request.body.targetId) {
|
||||
await store.createAuditEvent({
|
||||
accountId: pending.accountId,
|
||||
sessionId: pending.sessionId,
|
||||
|
|
|
|||
|
|
@ -218,6 +218,16 @@ describe("redeem binds caller, target and replay", () => {
|
|||
expect((await redeem(r, { completionCode, targetId: "target-a" }, appAuth)).statusCode).toBe(201);
|
||||
});
|
||||
|
||||
it("refuses a target named against a completion that recorded none, without consuming the code", async () => {
|
||||
const r = await rig("d-r2b");
|
||||
const completionCode = await linked(r, undefined);
|
||||
const refused = await redeem(r, { completionCode, targetId: "target-a" }, appAuth);
|
||||
expect([refused.statusCode, refused.json().error]).toEqual([403, "completion_target_mismatch"]);
|
||||
expect(refused.json().accessToken).toBeUndefined();
|
||||
expect((await r.store.findAuthCompletion("repixelizer", completionCode))?.consumedAt).toBeUndefined();
|
||||
expect((await redeem(r, { completionCode }, appAuth)).statusCode).toBe(201);
|
||||
});
|
||||
|
||||
it("refuses a replayed completion", async () => {
|
||||
const r = await rig("d-r3");
|
||||
const completionCode = await linked(r, "target-a");
|
||||
|
|
|
|||
Loading…
Reference in a new issue