Rumah  >  Artikel  >  hujung hadapan web  >  Memindahkan Pengurusan Pengguna daripada Dalaman kepada Produk: Mengapa Kami Melakukannya dan Perkara yang Kami Pelajari

Memindahkan Pengurusan Pengguna daripada Dalaman kepada Produk: Mengapa Kami Melakukannya dan Perkara yang Kami Pelajari

PHPz
PHPzasal
2024-08-13 06:40:09375semak imbas

Jadual Kandungan

  • TL;DR
  • Objektif Utama Kami
  • Penyelidikan dan Penilaian
  • Pelaksanaan
  • Strategi JWT lwn. Strategi Kerani
  • Segerakkan Antara Kerani dan Novu
    • Menyegerakkan Pengguna dan Organisasi
    • Apa yang Disimpan dalam Kerani vs Novu
  • Suntikan Penyedia Edisi Perusahaan
    • AuthService & AuthModule - Suntikan Dinamik
    • Repositori - Pengguna, Organisasi, Ahli
    • Pengawal
    • Isu dengan Pendekatan Ini
  • Pengubahsuaian Titik Akhir
  • Perkara Penting untuk Dipertimbangkan dan Dielakkan
  • Sorotan Pasukan
  • Ringkasan
  • Mata Bonus Hindsight

Siaran ini dibawakan kepada anda oleh Novu

Moving User Management from In-House to a Product: Why We Did It and What We Learned novuhq / novu

Platform Pemberitahuan Sumber Terbuka. Integrasi Pusat Pemberitahuan Boleh Benam, E-mel, Push dan Slack.

Moving User Management from In-House to a Product: Why We Did It and What We Learned

Moving User Management from In-House to a Product: Why We Did It and What We Learned Moving User Management from In-House to a Product: Why We Did It and What We Learned Moving User Management from In-House to a Product: Why We Did It and What We Learned

Infrastruktur pemberitahuan sumber terbuka untuk pembangun

Perkhidmatan muktamad untuk mengurus pemberitahuan berbilang saluran dengan API tunggal


Terokai dokumen »

Laporkan Pepijat · Ciri Permintaan · Sertai Discord Kami · Pelan hala tuju · X · Direktori Pemberitahuan

⭐️ Kenapa Novu?

Novu menyediakan API bersatu yang memudahkan anda menghantar pemberitahuan melalui berbilang saluran, termasuk Dalam Apl, Tekan, E-mel, SMS dan Sembang. Dengan Novu, anda boleh membuat aliran kerja tersuai dan menentukan syarat untuk setiap saluran, memastikan pemberitahuan anda dihantar dengan cara yang paling berkesan.

✨ Ciri-ciri

  • ? API tunggal untuk semua penyedia pemesejan (Dalam Apl, E-mel, SMS, Tekan, Sembang)
  • ? Aliran GitOps terurus sepenuhnya, digunakan daripada CI
  • anda
  • ? Tentukan aliran kerja dan pengesahan langkah dengan Zod atau JSON Schema
  • ? React Email/Maizzle/MJML integrasi
  • ? Dilengkapi dengan CMS untuk susun atur lanjutan dan pengurusan reka bentuk
  • ? Nyahpepijat dan menganalisis mesej berbilang saluran dalam satu papan pemuka
  • ? Pusat pemberitahuan boleh terbenam…
Lihat di GitHub

TL;DR

Novu melaksanakan Clerk sebagai penyelesaian pengurusan pengguna (Infrastruktur pengesahan) yang meletakkan asas untuk menawarkan fungsi SAML Single Sign-On (SSO), Google dan GitHub sebagai penyedia OAuth, pengesahan berbilang faktor, Kawalan Akaun Berasaskan Peranan (RBAC). ), dan banyak lagi.

Seorang pembangun bernama Adam melaksanakannya, dengan bantuan padu daripada jurutera platform Denis.


Moving User Management from In-House to a Product: Why We Did It and What We Learned

Seperti kebanyakan projek, projek ini bermula daripada tertunggak. Tidak sebelum berpuluh-puluh pelanggan memintanya, dan mengundi tinggi permintaan itu dalam peta jalan kami.

Sebagai penyelesaian infrastruktur pemberitahuan, sebahagian daripada apl dan seni bina kami melibatkan pengurusan pengguna daripada log pendaftaran, log masuk dan sesi untuk membolehkan pengguna menjemput ahli pasukan ke organisasi dan mengurus akses setiap peranan.

Ini semua tentang keutamaan. Fokus teras Novu adalah untuk menyelesaikan semua perkara yang berkaitan dengan pemberitahuan dan pengurusan mesej supaya pengguna kami tidak perlu melakukannya. Oleh itu, kami menghabiskan kitaran kami untuk menyediakan pengalaman terbaik untuk membina dan mengurus aliran kerja pemberitahuan, memperkasakan pembangun untuk melindungi masa mereka dan melancarkan kerjasama antara produk dan pasukan pemasaran.
Pengurusan pengguna yang berkesan tidak termasuk di bawah "nilai teras" yang kami dedikasikan.

Dengan cara yang sama kami menjangkakan anda untuk menyalurkan beban pemberitahuan kejuruteraan kepada kepakaran kami, kami memunggah beban pengurusan pengguna yang berkesan kejuruteraan kepada kepakaran Kerani.

Tidak perlu dikatakan, pasukan kami membina infrastruktur pengesahan dan kebenaran yang hebat secara dalaman dari hari pertama, berdasarkan seni bina yang disesuaikan dan direka dengan baik.

Semasa kami menaik tahap, kami lebih menumpukan pada penyempurnaan pengalaman pembangunan pemberitahuan.

Kami mengharapkan pembangun dan jurutera mengelak mencipta semula roda dan membiarkan Novu mengendalikan pemberitahuan, sama seperti kami memilih untuk memanfaatkan penyelesaian yang terbukti, diuji dan terkemuka untuk aspek lain produk kami: MongoDB untuk pangkalan data, Stripe untuk pembayaran dan sekarang Kerani untuk pengurusan pengguna. Kita ikut cakap.


Objektif utama kami

Cipta pengalaman yang selamat dan mudah digunakan untuk pengguna kami.

Apabila menggariskan draf awal untuk projek ini, ia mungkin kelihatan ringkas dan mudah, malah mungkin memberi gambaran bahawa ia boleh disiapkan pada hujung minggu.

Senarai semak draf awal:

  • Pembekal OAuth (GitHub, Google)
  • SAML SSO
  • Pengurusan sesi selamat
  • RBAC
  • Pengesahan pautan ajaib

Perhatikan bahawa jika draf awal tidak berubah, maka projek itu belum menerima maklum balas dan input yang mencukupi. Sememangnya, senarai itu menjadi lebih panjang.

Senarai semak sebenar:

  • Daftar dengan bukti kelayakan pengguna
  • Daftar dengan penyedia OAuth (Github, Google)
  • Log masuk dengan bukti kelayakan pengguna
  • Log masuk dengan penyedia OAuth (Github, Google)
  • Log masuk dengan SSO (SAML)
  • Log masuk dari Novu CLI
  • Log masuk/daftar dari Vercel Marketplace
  • Buat organisasi
  • Pengurusan organisasi
  • Pengurusan pengguna (kemas kini maklumat pengguna, kelayakan, dll...)
  • MFA/2FA (OTP melalui sms/e-mel, TOTP, kunci laluan, biometrik, dll.)
  • Jemputan
  • RBAC: Dua peranan pentadbir & editor
    • admin = Pentadbir boleh mengakses dan berinteraksi dengan mana-mana halaman pada platform web (jadi, termasuk ahli pasukan dan tetapan)
    • editor = Peranan editor kekal sebagai "pengurus kandungan utama" (aka pengurus produk atau pengurus pemasaran)

Penyelidikan dan penilaian

Selepas mengenal pasti skop projek, langkah seterusnya ialah menjalankan penyelidikan dan menilai sumber yang diperlukan untuk mencapai hasil yang diinginkan.

Proses ini termasuk:

  • Mempunyai pemahaman yang sangat jelas tentang keadaan semasa dan setiap lapisan produk:

    • Pergantungan
    • Titik tamat
    • Seni Bina
    • Komponen dan perwakilan lapisan pelanggan (bahagian hadapan)
    • Ujian

    Dan banyak lagi.

  • Gariskan spesifikasi penghijrahan (Apa yang kekal di dalam rumah dan harus dihalang)

  • Keserasian ke belakang

  • Cuba cari rujukan kepada projek yang serupa, daripada bekas rakan sekerja mungkin, dan belajar daripada proses serta cadangan mereka

  • Cuba dan cari penyelesaian sumber terbuka

  • Cari jika terdapat mana-mana vendor (penyelesaian pihak ke-3) dan bandingkannya.

Dan banyak lagi.

Dalam catatan blog lain, kami akan meneroka cara kami menilai dan membandingkan penyelesaian (atau produk) pihak ketiga sebagai perkhidmatan/infrastruktur sebagai syarikat perkhidmatan.

Penyelidikan yang tidak mencukupi atau penilaian yang tidak tepat biasanya membawa kepada hutang teknikal dan kehilangan sumber pada masa hadapan, seperti masa kejuruteraan apabila menambah ciri dan penyelenggaraan tambahan, yang memerlukan pemfaktoran semula keseluruhannya. Jadi, cari kos tersembunyi bagi setiap pilihan.

Pemimpin pasukan yang berpengalaman mengetahui cara menilai pulangan pelaburan (ROI) setiap pilihan, yang membantu mereka membuat keputusan terbaik untuk perniagaan.

Begitulah cara kami berakhir dengan Kerani. Penyelesaian mereka meliputi kebanyakan kes penggunaan kami dan dari sudut perniagaan, ROI untuk melaksanakannya untuk mengurus pengguna dan lapisan organisasi masuk akal.


Perlaksanaan

Perkhidmatan Novu mengandungi banyak perkhidmatan mikro dan aspek seperti:

  • Saluran Pemberitahuan (SMS, E-mel, Dalam Apl, Tekan, Sembang dll..),
  • Orkestrasi Pemberitahuan (Penyegerakan Merentas Peranti, Enjin Digest, Kelewatan, Kesedaran Zon Waktu dll..)
  • Kebolehcerap Pemberitahuan (Penyahpepijatan, Cerapan dll.)
  • Pengurusan Kandungan Pemberitahuan (Editor, Penjenamaan, Reka Letak, Terjemahan, Pengurusan Pembolehubah dll..)
  • Pengurusan pengguna akhir (Keutamaan Pengguna, Pelanggan, Topik, Segmen, Pengurusan Langganan dll..)
  • Pengurusan Akaun (SSO, Kawalan Akses Berasaskan Peranan, Berbilang Sewa, Pengebilan dll..)

Rajah di bawah menunjukkan versi ringkas struktur API Novu, hanya memfokuskan pada pengesahan dan kebenaran pengguna dan organisasi Novu sebelum melaksanakan Kerani.

Moving User Management from In-House to a Product: Why We Did It and What We Learned

Kami menggunakan MongoDB untuk menyimpan semua data yang Novu perlukan, setiap pengguna, organisasi, penyewa, pelanggan, topik... ringkasnya, semuanya.

Oleh kerana Kerani mempunyai pangkalan data sendiri untuk mengurus pengguna, kami perlu mengendalikan pemindahan dan penyegerakan antara pangkalan data dengan sangat berhati-hati dan tepat.


Strategi JWT vs Strategi Kerani

Salah satu perkara utama yang perlu kami pastikan ialah objek UserSessionData tidak akan berubah untuk tidak memecahkan sesi pengguna apabila menggunakan Novu. Ia harus kekal serasi.

Di sini anda boleh melihat contoh fail jwt.stratgy.ts:

//jwt.stratgy.ts
import type http from 'http';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ApiAuthSchemeEnum, HttpRequestHeaderKeysEnum, UserSessionData } from '@novu/shared';
import { AuthService, Instrument } from '@novu/application-generic';
import { EnvironmentRepository } from '@novu/dal';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService, private environmentRepository: EnvironmentRepository) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
      passReqToCallback: true,
    });
  }
  @Instrument()
  async validate(req: http.IncomingMessage, session: UserSessionData) {
    // Set the scheme to Bearer, meaning the user is authenticated via a JWT coming from Dashboard
    session.scheme = ApiAuthSchemeEnum.BEARER;

    const user = await this.authService.validateUser(session);
    if (!user) {
      throw new UnauthorizedException();
    }

    await this.resolveEnvironmentId(req, session);

    return session;
  }

  @Instrument()
  async resolveEnvironmentId(req: http.IncomingMessage, session: UserSessionData) {
    // Fetch the environmentId from the request header
    const environmentIdFromHeader =
      (req.headers[HttpRequestHeaderKeysEnum.NOVU_ENVIRONMENT_ID.toLowerCase()] as string) || '';

    /*
     * Ensure backwards compatibility with existing JWTs that contain environmentId
     * or cached SPA versions of Dashboard as there is no guarantee all current users
     * will have environmentId in localStorage instantly after the deployment.
     */
    const environmentIdFromLegacyAuthToken = session.environmentId;

    let currentEnvironmentId = '';

    if (environmentIdFromLegacyAuthToken) {
      currentEnvironmentId = environmentIdFromLegacyAuthToken;
    } else {
      const environments = await this.environmentRepository.findOrganizationEnvironments(session.organizationId);
      const environmentIds = environments.map((env) => env._id);
      const developmentEnvironmentId = environments.find((env) => env.name === 'Development')?._id || '';

      currentEnvironmentId = developmentEnvironmentId;

      if (environmentIds.includes(environmentIdFromHeader)) {
        currentEnvironmentId = environmentIdFromHeader;
      }
    }

    session.environmentId = currentEnvironmentId;
  }
}

Moving User Management from In-House to a Product: Why We Did It and What We Learned

Untuk mengekalkan keserasian dengan apl yang lain, kami perlu menukar muatan JWT daripada Kerani kepada format JWT sedia ada sebelum ini.

Begini cara kami melakukannya:

async validate(payload: ClerkJwtPayload): Promise<IJwtClaims> {
  const jwtClaims: IJwtClaims = {
    // first time its clerk_id, after sync its novu internal id
    _id: payload.externalId || payload._id,
    firstName: payload.firstName,
    lastName: payload.lastName,
    email: payload.email,
    profilePicture: payload.profilePicture,
    // first time its clerk id, after sync its novu internal id
    organizationId: payload.externalOrgId || payload.org_id,
    environmentId: payload.environmentId,
    roles: payload.org_role ? [payload.org_role.replace('org:', '')] : [],
    exp: payload.exp,
  };

  return jwtClaims;
}

Di sini anda boleh melihat contoh fail clerk.strategy.ts:

import type http from 'http';
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { passportJwtSecret } from 'jwks-rsa';
import {
  ApiAuthSchemeEnum,
  ClerkJwtPayload,
  HttpRequestHeaderKeysEnum,
  PassportStrategyEnum,
  UserSessionData,
} from '@novu/shared';
import { EnvironmentRepository, EnvironmentEntity } from '@novu/dal';
import { LinkEntitiesService } from '../services/link-entities.service';

@Injectable()
export class ClerkStrategy extends PassportStrategy(Strategy, PassportStrategyEnum.JWT_CLERK) {
  constructor(private environmentRepository: EnvironmentRepository, private linkEntitiesService: LinkEntitiesService) {
    super({
      // ...configuration details
    });
  }

  async validate(req: http.IncomingMessage, payload: ClerkJwtPayload) {
    const { internalUserId, internalOrgId } = await this.linkEntitiesService.linkInternalExternalEntities(req, payload);

    const session: UserSessionData = {
      _id: internalUserId,
      firstName: payload.firstName,
      lastName: payload.lastName,
      email: payload.email,
      profilePicture: payload.profilePicture,
      organizationId: internalOrgId,
      roles: payload.org_role ? [payload.org_role.replace('org:', '')] : [],
      exp: payload.exp,
      iss: payload.iss,
      scheme: ApiAuthSchemeEnum.BEARER,
      environmentId: undefined,
    };

    await this.resolveEnvironmentId(req, session);

    return session;
  }

  // Other functions...
}

Moving User Management from In-House to a Product: Why We Did It and What We Learned


Segerakkan antara Kerani dan Novu

Walaupun matlamatnya adalah untuk menggunakan sebaik-baiknya Kerani untuk mencipta dan mendapatkan semula pengguna, organisasi dll, malangnya ia tidak dapat dilakukan sepenuhnya kerana keperluan untuk menyimpan dan menanyakan beberapa metadata tentang pengguna dan organisasi dengan cara yang berprestasi.

Berikut ialah contoh kaedah dalam repositori organisasi Novu:

  async findPartnerConfigurationDetails(organizationId: string, userId: string, configurationId: string) {
    const organizationIds = await this.getUsersMembersOrganizationIds(userId);

    return await this.find(
      {
        _id: { $in: organizationIds },
        'partnerConfigurations.configurationId': configurationId,
      },
      { 'partnerConfigurations.$': 1 }
    );
  }

Kaedah ini menggunakan pelbagai binaan khusus MongoDB untuk menapis dokumen - ini tidak mungkin untuk menghasilkan semula menggunakan Clerk secara berprestasi kerana itu bukan pangkalan data yang dimaksudkan untuk pertanyaan sedemikian.

Apa yang boleh kami lakukan ialah menyimpan metadata tentang organisasi ini dalam koleksi organisasi MongoDB kami dan memaut/segerakkan koleksi dengan pangkalan data Kerani menggunakan externalId.

Moving User Management from In-House to a Product: Why We Did It and What We Learned

Kini kami boleh menggabungkan kedua-dua Kerani dan MongoDB untuk menanyakan metadata jika perlu.

async findPartnerConfigurationDetails(
  organizationId: string,
  userId: string,
  configurationId: string
): Promise<OrganizationEntity[]> {
  const clerkOrganizations = await this.getUsersMembersOrganizations(userId);

  return await this.communityOrganizationRepository.find(
    {
      _id: { $in: clerkOrganizations.map((org) => org.id) },
      'partnerConfigurations.configurationId': configurationId,
    },
    { 'partnerConfigurations.$': 1 }
  );
}

private async getUsersMembersOrganizations(userId: string): Promise<Organization[]> {
  const userOrgMemberships = await this.clerkClient.users.getOrganizationMembershipList({
    userId,
  });

  return userOrgMemberships.data.map((membership) => membership.organization);
}

Dengan memanggil getUsersMembersOrganizations, findPartnerConfigurationDetails mendapat data organisasi yang diperlukan untuk melakukan carian yang ditapis pada communityOrganizationRepository, memastikan hanya konfigurasi yang berkaitan dikembalikan.

Kami hanya perlu menyegerakkan Pengguna dan Organisasi antara Kerani dan Novu, ahli organisasi tidak perlu disegerakkan.


Menyegerakkan pengguna dan organisasi

Terdapat dua cara bagaimana id pangkalan data disegerakkan:

  • middleware - any endpoint in API will sync the IDs if it detects that JWT doesn’t yet contain an internal ID.
  • webhook - as soon as the user/org is registered in Clerk, Clerk calls Novu’s API webhook, and we sync it.

Moving User Management from In-House to a Product: Why We Did It and What We Learned

Here is the flow we had in mind:

  1. A user creates a new account via frontend using the Clerk component
  2. Gets a new JWT containing Clerk user-id
  3. Any request that hits the API triggers the syncing process (given it hasn’t yet happened)
  4. A new user is created in Novu’s MongoDB containing the Clerk’s externalId
  5. Clerk user object gets updated with Novu internal object id (saved as externalId in Clerk)
  6. The new token returned from Clerk now contains an externalId that is equal to Novu's internal user ID.
  7. In the Clerk strategy in validate() function on API - we set _id to equal to externalId so it is compatible with the rest of the app.

Note
In the application, we always expect Novu’s internal id on input and we always return internal id on output - its important for the application to work as is without major changes to the rest of the code.
API expects internal _id everywhere and it needs to be MongoDB ObjectID type, because it parses this user id back to ObjectID e.g. when creating new environment or any other entity which needs reference to user.

The same logic applies to organizations; just the endpoint is different.


What is stored in Clerk vs Novu

Users

For the users, we store everything in Clerk. All the properties are mostly just simple key/value pairs and we don’t need any advanced filtering on them, therefore they can be retrieved and updated directly in Clerk.

In internal MongoDB, we store just the user internal and external ids.

The original Novu user properties are stored in Clerk’s publicMetadata :

export type UserPublicMetadata = {
  profilePicture?: string | null;
  showOnBoardingTour?: number;
};

There are also many other attributes coming from Clerk which can be set on the user.

Organizations

For the organizations, we store everything in Clerk except for apiServiceLevel, partnerConfigurations, and branding since they are “native” to Clerk and we update those attributes directly there via frontend components and so we don’t need to sync with our internal DB after we change organization name or logo via Clerk component.

Moving User Management from In-House to a Product: Why We Did It and What We Learned


Injection of Enterprise Edition providers

The goal here was to replace the community (open source) implementation with Clerk while being minimally invasive to the application and to keep the Clerk implementation in a separate package.

This means we need to keep the changed providers (OrganizationRepository, AuthService…) on the same place with the same name so we don’t break the imports all over the place, but we need to change their body to be different based on feature flag.

The other option would be to change all of these providers in the 100+ of files and then import the EE(enterprise edition) package everywhere, which is probably not a good idea.

This turned out to be quite challenging due to the fact that users, organization and members are relatively deeply integrated to the application itself, referenced in a lot of places and they’re also tied to MongoDB specifics such as ObjectID or queries (create, update, findOne …).

The idea is to provide different implementation using NestJS dynamic custom providers where we are able to inject different class/service on compile time based on the enterprise feature flag.

This is the most promising solution we found while keeping the rest of the app mostly untouched, there are some drawbacks explained later.


AuthService & AuthModule - dynamic injection

Moving User Management from In-House to a Product: Why We Did It and What We Learned

We have two implementations of AuthService - community and enterprise one (in private package), we inject one of those as AUTH_SERVICE provider.

We need to however have a common interface for both IAuthService

Since we also need to change the AuthModule, we initialize two different modules based on the feature flag like this:

function getModuleConfig(): ModuleMetadata {
  if (process.env.NOVU_ENTERPRISE === 'true') {
    return getEEModuleConfig();
  } else {
    return getCommunityAuthModuleConfig();
  }
}

@Global()
@Module(getModuleConfig())
export class AuthModule {
  public configure(consumer: MiddlewareConsumer) {
    if (process.env.NOVU_ENTERPRISE !== 'true') {
      configure(consumer);
    }
  }
}

The reason why the EEModule can be a standalone module in the @novu/ee-auth package which we would just import instead of the original AuthModule and instead we are initializing one module conditionally inside API, is that we are reusing some original providers in the EE one - e.g. ApiKeyStrategy , RolesGuard, EnvironmentGuard etc which resides directly in API.

We would need to import them in the @novu/ee-auth package which would require to export these things somewhere (probably in some shared package) and it introduces other issues like circular deps etc - it can be however refactored later.

Repositories - users, organizations, members

Same logic applies for the repositories. No module is being initialized here, they’re just directly injected to the original repository classes.

Moving User Management from In-House to a Product: Why We Did It and What We Learned


Controllers

The controllers are being conditionally imported from inside @novu/api . The reason for that is the same as in the auth module, there are too many imports that the controllers uses, that we would either need to move to @novu/ee-auth or move them to a separate shared package - which would then trigger a much bigger change to the other unrelated parts of the app, which would increase the scope of this change.

function getControllers() {
  if (process.env.NOVU_ENTERPRISE === 'true') {
    return [EEOrganizationController];
  }

  return [OrganizationController];
}

@Module({
  controllers: [...getControllers()],
})
export class OrganizationModule implements NestModule {
    ...
}


Issues with this approach

The main issue here is the need for common interface for both of the classes - community and enterprise. You want to remain compatible in both community and enterprise versions, so when there is a this.organizationService.getOrganizations() method being called in 50 places in the app - you need an enterprise equivalent with the same name otherwise you need to change 50 places to call something else.

This results in not-so-strict typing and methods without implementation

Moving User Management from In-House to a Product: Why We Did It and What We Learned

We need to have a common interface for both, however the community one relies on MongoDB methods and needs different method arguments as the enterprise one which causes a use of any to forcefully fit both classes etc.
In some cases we don’t need the method at all, so we need to throw Not Implemented .


Endpoints modification

We modified the endpoints as follows:

  • AuthController: Removed and replaced by frontend calls to Clerk.
  • UserController: Removed, added a sync endpoint for Clerk users with MongoDB.
  • OrganizationController: Removed several endpoints, which can be migrated later.
  • InvitesController: Completely removed.
  • StorageModule: Completely removed.

Key points to consider and avoid

  1. Avoid Storing Frequently Changing Properties in JWT
    • Example: environmentID
    • It can be cumbersome to update these properties.
  2. Simplify Stored Data Structures
    • Avoid storing complex structures in user, organization, or member records.
    • Clerk performs optimally with simple key:value pairs, not arrays of objects.
  3. Implement a User/Organization Replication Mechanism
    • This helps bridge the gap during the migration period before Clerk is fully enabled.
    • Use MongoDB triggers to replicate newly created users and organizations to both Clerk and your internal database.
  4. Store Original Emails
    • Do not sanitize emails as Clerk uses the original email as a unique identifier.

Team Spotlight

Lead Engineer: Adam Chmara

Platform Team Lead: Denis Kralj


Summary

Our implementation approach comes to the fact that we offloaded the Users, Organizations and Members management to Clerk.

The data property injection to Novu’s Controllers (endpoints) layer, Business layer and data layer happens based on “Enterprise” feature flag validation.

We are leveraging pre-built Clerk components on the frontend and reducing the need to build and maintain our own custom implementation on the backend.

You can also observe below the diagram of the current state after implementing Clerk.

Moving User Management from In-House to a Product: Why We Did It and What We Learned


Mata Bonus Hindsight

Apabila kami membuat keputusan untuk melaksanakan Kerani bagi pengurusan pengguna, kami turut mengikut serta untuk manfaat jangka panjang dengan menggunakan keupayaan dan ciri yang akan disokong dan ditawarkan oleh Kerani pada masa hadapan.

Berikut ialah beberapa contoh perkara yang mungkin kami pertimbangkan untuk menyokong dalam masa terdekat:

  • Kawalan akses berbutir halus (FGAC)
    • Berasaskan atribut: FGAC sering dilaksanakan menggunakan Kawalan Akses Berasaskan Atribut (ABAC), di mana keputusan akses adalah berdasarkan pelbagai atribut pengguna, sumber dan persekitaran. Atribut boleh termasuk peranan pengguna, jabatan, jenis sumber, masa dalam hari dan banyak lagi.
    • Fleksibiliti: FGAC menawarkan fleksibiliti dan butiran yang lebih besar dengan membenarkan kawalan akses berasaskan keadaan yang terperinci. Ini bermakna kebenaran boleh diperhalusi kepada senario yang sangat khusus.
    • Dinamik: FGAC boleh menyesuaikan diri secara dinamik kepada perubahan dalam persekitaran, seperti akses sensitif masa atau sekatan berasaskan lokasi.
    • Kebenaran terperinci: Kebenaran dalam FGAC adalah lebih khusus dan boleh disesuaikan dengan tindakan individu, pengguna atau situasi.

Berikan tahap fleksibiliti terperinci ini kepada Novu, mungkin telah di luar skop atau bahkan dikikis di pintu kerana potensi kerumitan pelaksanaan.

  • Penyamaran pengguna

    Kejayaan pelanggan atau pasukan sokongan kami boleh menggunakan ini untuk menyelesaikan masalah, memberikan sokongan atau menguji kefungsian daripada perspektif pengguna yang menyamar tanpa perlu mengetahui kata laluan mereka atau butiran pengesahan lain.

    • Mengurangkan masa dan kerumitan yang terlibat dalam mendiagnosis dan menyelesaikan isu pengguna.
    • Memastikan bahawa sokongan atau tindakan pentadbiran yang diambil adalah tepat kerana kakitangan sokongan boleh melihat dan berinteraksi dengan sistem sama seperti yang pengguna lakukan.
    • Meningkatkan kepuasan pengguna dengan memberikan sokongan yang lebih pantas dan berkesan.

Ringkasnya, kami akan dapat meningkatkan pengalaman untuk pengguna Novu dengan mudah memandangkan hakikat bahawa sekarang, infrastruktur pengesahan dibohongi untuknya.


Jika anda ingin mencadangkan ciri tambahan yang melibatkan AuthN (atau mana-mana yang lain), lawati Pelan Hala Tuju kami untuk menyemak dan mengundi permintaan, atau menyerahkan idea anda.


Suka apa yang anda baca? Tekan ikuti untuk lebih banyak kemas kini dan lepaskan komen di bawah. Saya ingin ❤️ mendengar ?

Moving User Management from In-House to a Product: Why We Did It and What We Learned

Emil Pearce

Saya menulis kod & perkataan di kedai kopi.

Atas ialah kandungan terperinci Memindahkan Pengurusan Pengguna daripada Dalaman kepada Produk: Mengapa Kami Melakukannya dan Perkara yang Kami Pelajari. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn