Skip to content

Commit 32d6e7c

Browse files
committed
Bugfix(modeling-commons): treat legacy sign up as password reset
1 parent 8500baa commit 32d6e7c

3 files changed

Lines changed: 96 additions & 1 deletion

File tree

apps/modeling-commons-backend/src/server/plugins/auth.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,28 @@ async function authPlugin(fastify: FastifyInstance) {
112112
...(request.body ? { body: JSON.stringify(request.body) } : {}),
113113
});
114114

115-
const response = await auth.handler(req);
115+
const signUpEmailPath = `${auth.options.basePath}/sign-up/email`;
116+
117+
let response: Response;
118+
if (url.pathname === signUpEmailPath && request.method === 'POST') {
119+
const { email } = request.body as { email?: string };
120+
const isLegacyUserWithNoAccount = email
121+
? await prisma.user.findFirst({
122+
where: {
123+
email,
124+
legacyId: { not: null },
125+
accounts: { none: {} },
126+
},
127+
})
128+
: null;
129+
130+
response =
131+
isLegacyUserWithNoAccount && email
132+
? await auth.api.requestPasswordReset({ asResponse: true, body: { email } })
133+
: await auth.handler(req);
134+
} else {
135+
response = await auth.handler(req);
136+
}
116137

117138
reply.status(response.status);
118139
response.headers.forEach((value, key) => reply.header(key, value));

apps/modeling-commons-backend/tests/api/auth.feature

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,25 @@ Feature: Authentication
2222
Given a verified user with email "carol@test.local" and password "StrongPass1!"
2323
When I sign in with email "carol@test.local" and password "WrongPass1!"
2424
Then the signin should be rejected
25+
26+
@legacy-signup
27+
Scenario: Legacy user without an account gets a password reset instead of a sign-up
28+
Given a legacy user with email "legacy@test.local" and no linked account
29+
When I sign up with name "Legacy" email "legacy@test.local" and password "StrongPass1!"
30+
Then the response status should be 200
31+
And the response body should not have property "user"
32+
And the user "legacy@test.local" should still have no linked account
33+
34+
@legacy-signup
35+
Scenario: Existing user with an account is handled normally on sign-up
36+
Given a verified user with email "existing@test.local" and password "StrongPass1!"
37+
When I sign up with name "Existing" email "existing@test.local" and password "StrongPass1!"
38+
Then the response status should be 200
39+
And the response body should have property "user"
40+
41+
@legacy-signup
42+
Scenario: New user with no account is signed up normally
43+
When I sign up with name "Newbie" email "newbie@test.local" and password "StrongPass1!"
44+
Then the response status should be 200
45+
And the response body should have property "user"
46+
And the user "newbie@test.local" should have a linked account

apps/modeling-commons-backend/tests/api/auth.steps.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,42 @@ import { Given, When, Then } from '@cucumber/cucumber';
33
import type { ICustomWorld } from '../support/custom-world.ts';
44
import { signUp } from '../support/auth-helper.ts';
55

6+
interface PrismaCradle {
7+
prisma: {
8+
user: {
9+
create: (args: { data: Record<string, unknown> }) => Promise<{ id: string }>;
10+
findFirst: (args: {
11+
where: Record<string, unknown>;
12+
include?: Record<string, unknown>;
13+
}) => Promise<{ id: string; accounts: unknown[] } | null>;
14+
};
15+
};
16+
}
17+
18+
let legacyIdCounter = 0;
19+
620
Given(
721
'a registered user with email {string}',
822
async function (this: ICustomWorld, email: string) {
923
await signUp(this.server, { email });
1024
},
1125
);
1226

27+
Given(
28+
'a legacy user with email {string} and no linked account',
29+
async function (this: ICustomWorld, email: string) {
30+
const { prisma } = this.server.diContainer.cradle as unknown as PrismaCradle;
31+
await prisma.user.create({
32+
data: {
33+
name: 'Legacy User',
34+
email,
35+
emailVerified: true,
36+
legacyId: ++legacyIdCounter,
37+
},
38+
});
39+
},
40+
);
41+
1342
Given(
1443
'a verified user with email {string} and password {string}',
1544
async function (this: ICustomWorld, email: string, password: string) {
@@ -64,3 +93,26 @@ Then(
6493
assert.ok(code >= 400, `Expected error status code, got ${code}`);
6594
},
6695
);
96+
97+
async function findAccountCount(this: ICustomWorld, email: string): Promise<number> {
98+
const { prisma } = this.server.diContainer.cradle as unknown as PrismaCradle;
99+
const user = await prisma.user.findFirst({ where: { email }, include: { accounts: true } });
100+
assert.ok(user, `Expected a user with email "${email}"`);
101+
return user.accounts.length;
102+
}
103+
104+
Then(
105+
'the user {string} should still have no linked account',
106+
async function (this: ICustomWorld, email: string) {
107+
const count = await findAccountCount.call(this, email);
108+
assert.strictEqual(count, 0, `Expected no linked account, found ${count}`);
109+
},
110+
);
111+
112+
Then(
113+
'the user {string} should have a linked account',
114+
async function (this: ICustomWorld, email: string) {
115+
const count = await findAccountCount.call(this, email);
116+
assert.ok(count > 0, 'Expected a linked account, found none');
117+
},
118+
);

0 commit comments

Comments
 (0)