Skip to content

Commit

Permalink
fix(server): user can not signup through oauth if ever invited (#6101)
Browse files Browse the repository at this point in the history
  • Loading branch information
forehalo committed Mar 13, 2024
1 parent fd9084e commit 573528b
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "registered" BOOLEAN NOT NULL DEFAULT true;
3 changes: 3 additions & 0 deletions packages/backend/server/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ model User {
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
/// Not available if user signed up through OAuth providers
password String? @db.VarChar
/// Indicate whether the user finished the signup progress.
/// for example, the value will be false if user never registered and invited into a workspace by others.
registered Boolean @default(true)
features UserFeatures[]
customer UserStripeCustomer?
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/server/src/core/auth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ export class AuthController {
throw new BadRequestException('Invalid Sign-in mail Token');
}

const user = await this.user.findOrCreateUser(email, {
const user = await this.user.fulfillUser(email, {
emailVerifiedAt: new Date(),
registered: true,
});

await this.auth.setCookie(req, res, user);
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/server/src/core/auth/current-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const CurrentUser = createParamDecorator(
);

export interface CurrentUser
extends Omit<User, 'password' | 'createdAt' | 'emailVerifiedAt'> {
extends Pick<User, 'id' | 'email' | 'avatarUrl' | 'name'> {
hasPassword: boolean | null;
emailVerified: boolean;
}
16 changes: 11 additions & 5 deletions packages/backend/server/src/core/auth/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ export function parseAuthUserSeqNum(value: any) {
}

export function sessionUser(
user: Omit<User, 'password'> & { password?: string | null }
user: Pick<
User,
'id' | 'email' | 'avatarUrl' | 'name' | 'emailVerifiedAt'
> & { password?: string | null }
): CurrentUser {
return assign(omit(user, 'password', 'emailVerifiedAt', 'createdAt'), {
hasPassword: user.password !== null,
emailVerified: user.emailVerifiedAt !== null,
});
return assign(
omit(user, 'password', 'registered', 'emailVerifiedAt', 'createdAt'),
{
hasPassword: user.password !== null,
emailVerified: user.emailVerifiedAt !== null,
}
);
}

@Injectable()
Expand Down
4 changes: 3 additions & 1 deletion packages/backend/server/src/core/user/management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ export class UserManagementResolver {
if (user) {
return this.feature.addEarlyAccess(user.id);
} else {
const user = await this.users.createAnonymousUser(email);
const user = await this.users.createAnonymousUser(email, {
registered: false,
});
return this.feature.addEarlyAccess(user.id);
}
}
Expand Down
23 changes: 22 additions & 1 deletion packages/backend/server/src/core/user/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ export class UserService {
email: true,
emailVerifiedAt: true,
avatarUrl: true,
registered: true,
} satisfies Prisma.UserSelect;

constructor(private readonly prisma: PrismaClient) {}

get userCreatingData(): Partial<Prisma.UserCreateInput> {
get userCreatingData() {
return {
name: 'Unnamed',
features: {
Expand Down Expand Up @@ -106,6 +107,26 @@ export class UserService {
return this.createAnonymousUser(email, data);
}

async fulfillUser(
email: string,
data: Partial<
Pick<Prisma.UserCreateInput, 'emailVerifiedAt' | 'registered'>
>
) {
return this.prisma.user.upsert({
select: this.defaultUserSelect,
where: {
email,
},
update: data,
create: {
email,
...this.userCreatingData,
...data,
},
});
}

async deleteUser(id: string) {
return this.prisma.user.delete({ where: { id } });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,9 @@ export class WorkspaceResolver {
// only invite if the user is not already in the workspace
if (originRecord) return originRecord.id;
} else {
target = await this.users.createAnonymousUser(email);
target = await this.users.createAnonymousUser(email, {
registered: false,
});
}

const inviteId = await this.permissions.grant(
Expand Down
14 changes: 11 additions & 3 deletions packages/backend/server/src/plugins/oauth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,17 @@ export class OAuthController {
if (user) {
// we can't directly connect the external account with given email in sign in scenario for safety concern.
// let user manually connect in account sessions instead.
throw new BadRequestException(
'The account with provided email is not register in the same way.'
);
if (user.registered) {
throw new BadRequestException(
'The account with provided email is not register in the same way.'
);
}

await this.user.fulfillUser(externalAccount.email, {
registered: true,
});

return user;
} else {
user = await this.createUserWithConnectedAccount(
provider,
Expand Down

0 comments on commit 573528b

Please sign in to comment.