@@ -19,7 +19,7 @@ import {
1919} from "@dotkomonline/types"
2020import { createS3PresignedPost , slugify , getNextSemesterStart , getCurrentSemesterStart } from "@dotkomonline/utils"
2121import { trace } from "@opentelemetry/api"
22- import type { ManagementClient } from "auth0"
22+ import type { ManagementClient , PostIdentitiesRequestProviderEnum } from "auth0"
2323import * as crypto from "node:crypto"
2424import { isDevelopmentEnvironment } from "../../configuration"
2525import { isSameDay } from "date-fns"
@@ -66,6 +66,11 @@ export interface UserService {
6666 getByProfileSlug ( handle : DBHandle , profileSlug : UserProfileSlug ) : Promise < User >
6767 findByWorkspaceUserIds ( handle : DBHandle , workspaceUserIds : string [ ] ) : Promise < User [ ] >
6868 findUsers ( handle : DBHandle , query : UserFilterQuery , page ?: Pageable ) : Promise < User [ ] >
69+ /**
70+ * This is for manually linking two logins to the same user. Sometimes the automatic script seems to fail, so we this
71+ * is a manual way of linking two users. Should not be confused with the (Google) Workspace router's `linkUser`.
72+ */
73+ linkUsers ( handle : DBHandle , primaryUserId : UserId , secondaryUserId : UserId ) : Promise < User >
6974
7075 /**
7176 * Attempt to discover an automatically granted membership from FEIDE.
@@ -493,6 +498,36 @@ export function getUserService(
493498 return identity ?. access_token ?? null
494499 } ,
495500
501+ async linkUsers ( handle , primaryUserId , secondaryUserId ) {
502+ logger . info (
503+ "Linking authentication identities for survivor User(ID=%s) and consumed User(ID=%s)" ,
504+ primaryUserId ,
505+ secondaryUserId
506+ )
507+
508+ const primaryUser = await managementClient . users . get ( { id : primaryUserId } )
509+ const secondaryUser = await managementClient . users . get ( { id : secondaryUserId } )
510+
511+ const secondaryIdentity =
512+ secondaryUser . data . identities . find ( ( identity ) => secondaryUserId . endsWith ( identity . user_id ) ) ||
513+ secondaryUser . data . identities [ 0 ]
514+
515+ const secondaryIdentityProvider = secondaryIdentity . provider as PostIdentitiesRequestProviderEnum
516+
517+ await managementClient . users . link (
518+ {
519+ id : primaryUser . data . user_id ,
520+ } ,
521+ {
522+ provider : secondaryIdentityProvider , // usually "oauth2"
523+ user_id : secondaryIdentity . user_id ,
524+ connection_id : secondaryIdentity . connection ,
525+ }
526+ )
527+
528+ logger . info ( "Successfully linked identities for survivor User(ID=%s) and consumed User(ID=%s)" )
529+ } ,
530+
496531 async createFileUpload ( handle , filename , contentType , userId , createdByUserId ) {
497532 const user = await this . getById ( handle , userId )
498533
0 commit comments