Skip to content

Commit b19d121

Browse files
committed
implement account linking for new users
1 parent 52228c5 commit b19d121

File tree

5 files changed

+57
-14
lines changed

5 files changed

+57
-14
lines changed

packages/services/api/src/modules/auth/lib/authz.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function parseResourceIdentifier(resource: string) {
5151
export type UserActor = {
5252
type: 'user';
5353
user: User;
54+
oidcIntegrationId: string | null;
5455
};
5556

5657
export type OrganizationAccessTokenActor = {

packages/services/api/src/modules/auth/lib/supertokens-strategy.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,24 @@ import { AuthNStrategy, AuthorizationPolicyStatement, Session, UserActor } from
1111

1212
export class SuperTokensCookieBasedSession extends Session {
1313
public superTokensUserId: string;
14+
public userId: string | undefined;
15+
public oidcIntegrationId: string | null | undefined;
1416
private organizationMembers: OrganizationMembers;
1517
private storage: Storage;
1618

1719
constructor(
18-
args: { superTokensUserId: string; email: string },
20+
args: {
21+
superTokensUserId: string;
22+
userId: string | undefined;
23+
oidcIntegrationId: string | null | undefined;
24+
email: string;
25+
},
1926
deps: { organizationMembers: OrganizationMembers; storage: Storage; logger: Logger },
2027
) {
2128
super({ logger: deps.logger });
2229
this.superTokensUserId = args.superTokensUserId;
30+
this.userId = args.userId;
31+
this.oidcIntegrationId = args.oidcIntegrationId;
2332
this.organizationMembers = deps.organizationMembers;
2433
this.storage = deps.storage;
2534
}
@@ -109,9 +118,9 @@ export class SuperTokensCookieBasedSession extends Session {
109118
}
110119

111120
public async getActor(): Promise<UserActor> {
112-
const user = await this.storage.getUserBySuperTokenId({
113-
superTokensUserId: this.superTokensUserId,
114-
});
121+
const user = this.userId
122+
? await this.storage.getUserById({ id: this.userId })
123+
: await this.storage.getUserBySuperTokenId({ superTokensUserId: this.superTokensUserId });
115124

116125
if (!user) {
117126
throw new AccessError('User not found');
@@ -120,6 +129,7 @@ export class SuperTokensCookieBasedSession extends Session {
120129
return {
121130
type: 'user',
122131
user,
132+
oidcIntegrationId: this.oidcIntegrationId ?? null,
123133
};
124134
}
125135

@@ -227,6 +237,8 @@ export class SuperTokensUserAuthNStrategy extends AuthNStrategy<SuperTokensCooki
227237
return new SuperTokensCookieBasedSession(
228238
{
229239
superTokensUserId: session.superTokensUserId,
240+
userId: session.userId,
241+
oidcIntegrationId: session.oidcIntegrationId,
230242
email: session.email,
231243
},
232244
{
@@ -241,5 +253,7 @@ export class SuperTokensUserAuthNStrategy extends AuthNStrategy<SuperTokensCooki
241253
const SuperTokenAccessTokenModel = zod.object({
242254
version: zod.literal('1'),
243255
superTokensUserId: zod.string(),
256+
userId: zod.string().optional(),
257+
oidcIntegrationId: zod.string().nullable().optional(),
244258
email: zod.string(),
245259
});

packages/services/api/src/modules/shared/providers/storage.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ export interface Storage {
7979
};
8080
firstName: string | null;
8181
lastName: string | null;
82-
}): Promise<'created' | 'no_action'>;
82+
}): Promise<{
83+
user: User;
84+
action: 'created' | 'no_action';
85+
}>;
8386

8487
getUserBySuperTokenId(_: { superTokensUserId: string }): Promise<User | null>;
8588
getUserById(_: { id: string }): Promise<User | null>;

packages/services/server/src/supertokens.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,17 +189,27 @@ export const backendConfig = (requirements: {
189189
);
190190
}
191191

192-
input.accessTokenPayload = {
192+
let payload = {
193193
version: '1',
194194
superTokensUserId: input.userId,
195195
email: user.emails[0],
196+
userId: undefined as string | undefined,
197+
oidcIntegrationId: undefined as string | null | undefined,
196198
};
199+
try {
200+
const internalUser = await internalApi.ensureUser({
201+
superTokensUserId: user.id,
202+
email: user.emails[0],
203+
oidcIntegrationId: input.userContext['oidcId'] ?? null,
204+
firstName: null,
205+
lastName: null,
206+
});
207+
payload.userId = internalUser.user.id;
208+
payload.oidcIntegrationId = input.userContext['oidcId'] ?? null;
209+
} catch {}
197210

198-
input.sessionDataInDatabase = {
199-
version: '1',
200-
superTokensUserId: input.userId,
201-
email: user.emails[0],
202-
};
211+
input.accessTokenPayload = structuredClone(payload);
212+
input.sessionDataInDatabase = structuredClone(payload);
203213

204214
return originalImplementation.createNewSession(input);
205215
},

packages/services/storage/src/index.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,20 @@ export async function createStorage(
609609
}) {
610610
return tracedTransaction('ensureUserExists', pool, async t => {
611611
let action: 'created' | 'no_action' = 'no_action';
612-
let internalUser = await shared.getUserBySuperTokenId({ superTokensUserId }, t);
613-
612+
const users = await pool.any<unknown>(sql`
613+
SELECT
614+
${userFields(sql`"users".`, sql`"stu".`)},
615+
FROM
616+
"users"
617+
LEFT JOIN "supertokens_thirdparty_users" AS "stu"
618+
ON ("stu"."user_id" = "users"."supertoken_user_id")
619+
WHERE
620+
"users"."email" = ${email};
621+
`);
622+
let internalUser = users.length > 0 ? UserModel.parse(users[0]) : null;
623+
if (!internalUser) {
624+
internalUser = await shared.getUserBySuperTokenId({ superTokensUserId });
625+
}
614626
if (!internalUser) {
615627
internalUser = await shared.createUser(
616628
buildUserData({
@@ -636,7 +648,10 @@ export async function createStorage(
636648
);
637649
}
638650

639-
return action;
651+
return {
652+
user: internalUser,
653+
action,
654+
};
640655
});
641656
},
642657
async getUserBySuperTokenId({ superTokensUserId }) {

0 commit comments

Comments
 (0)