Maison > interface Web > js tutoriel > Déplacer la gestion des utilisateurs de l'interne vers un produit : pourquoi nous l'avons fait et ce que nous avons appris

Déplacer la gestion des utilisateurs de l'interne vers un produit : pourquoi nous l'avons fait et ce que nous avons appris

PHPz
Libérer: 2024-08-13 06:40:09
original
460 Les gens l'ont consulté

Table des matières

  • TL;DR
  • Notre objectif principal
  • Recherche et évaluation
  • Mise en œuvre
  • Stratégie JWT vs stratégie de commis
  • Synchronisation entre Clerk et Novu
    • Synchronisation des utilisateurs et des organisations
    • Ce qui est stocké dans Clerk vs Novu
  • Injection de fournisseurs d’édition Entreprise
    • AuthService & AuthModule - Injection dynamique
    • Dépôts - Utilisateurs, organisations, membres
    • Contrôleurs
    • Problèmes liés à cette approche
  • Modification des points de terminaison
  • Points clés à considérer et à éviter
  • Pleins feux sur l'équipe
  • Résumé
  • Points bonus rétrospectifs

Ce message vous a été présenté par Novu

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

Plateforme de notification open source. Centre de notifications intégrable, intégrations de courrier électronique, Push et 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

L'infrastructure de notification open source pour les développeurs

Le service ultime pour gérer les notifications multicanaux avec une seule API


Explorez la documentation »

Signaler un bug · Fonctionnalité de demande · Rejoignez notre Discord · Feuille de route · X · Répertoire des notifications

⭐️Pourquoi Novu ?

Novu fournit une API unifiée qui facilite l'envoi de notifications via plusieurs canaux, notamment In-App, Push, Email, SMS et Chat. Avec Novu, vous pouvez créer des flux de travail personnalisés et définir des conditions pour chaque canal, garantissant ainsi que vos notifications sont envoyées de la manière la plus efficace possible.

✨ Caractéristiques

  • ? API unique pour tous les fournisseurs de messagerie (In-App, Email, SMS, Push, Chat)
  • ? GitOps Flow entièrement géré, déployé depuis votre CI
  • ? Définir le workflow et les validations d'étapes avec Zod ou JSON Schema
  • ? Intégrations React Email/Maizzle/MJML
  • ? Equipé d'un CMS pour les mises en page avancées et la gestion du design
  • ? Déboguer et analyser les messages multicanaux dans un seul tableau de bord
  • ? Centre de notifications intégrable…
Voir sur GitHub

TL;DR

Novu a implémenté Clerk en tant que solution de gestion des utilisateurs (infrastructure d'authentification) qui a jeté les bases de l'offre de la fonctionnalité SAML Single Sign-On (SSO), Google et GitHub en tant que fournisseurs OAuth, l'authentification multifacteur, le contrôle de compte basé sur les rôles (RBAC ), et bien plus encore.

Un développeur nommé Adam l'a implémenté, avec l'aide solide de l'ingénieur de plateforme Denis.


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

Comme la plupart des projets, celui-ci est parti du backlog. Pas avant que des dizaines de clients ne l'aient demandé et aient fortement voté pour la demande dans notre feuille de route.

En tant que solution d'infrastructure de notification, une partie de notre application et de notre architecture implique la gestion des utilisateurs à partir des inscriptions, des connexions et des journaux de session pour permettre aux utilisateurs d'inviter des membres de l'équipe dans l'organisation et de gérer l'accès de chaque rôle.

Tout est une question de priorités. L'objectif principal de Novu est de résoudre tout ce qui concerne les notifications et la gestion des messages afin que nos utilisateurs n'aient pas à le faire. Par conséquent, nous consacrons nos cycles à fournir la meilleure expérience pour créer et gérer des flux de travail de notification, en permettant aux développeurs de protéger leur temps et en facilitant la collaboration entre les équipes produit et marketing.
Une gestion efficace des utilisateurs ne relève pas de cette « valeur fondamentale » à laquelle nous nous engageons.

De la même manière que nous attendons de vous que vous déchargeiez le fardeau des notifications d'ingénierie sur notre expertise, nous avons transféré le fardeau de l'ingénierie d'une gestion efficace des utilisateurs à l'expertise du commis.

Inutile de dire que notre équipe a construit en interne dès le premier jour une excellente infrastructure d'authentification et d'autorisation, basée sur une architecture sur mesure et bien conçue.

Au fur et à mesure que nous progressons, nous nous concentrons encore plus sur le perfectionnement de l'expérience de développement de notifications.

Nous attendons des développeurs et des ingénieurs qu'ils évitent de réinventer la roue et laissent Novu gérer les notifications, tout comme nous choisissons de tirer parti de solutions éprouvées, testées et de pointe pour d'autres aspects de notre produit : MongoDB pour la base de données, Stripe pour les paiements, et maintenant Commis à la gestion des utilisateurs. Nous joignons le geste à la parole.


Notre objectif premier

Créer une expérience sécurisée et facile à utiliser pour nos utilisateurs.

Lors de la présentation de l'ébauche initiale de ce projet, elle peut paraître brève et simple, donnant peut-être même l'impression qu'elle pourrait être achevée en un week-end.

Le projet initial de liste de contrôle :

  • Fournisseur OAuth (GitHub, Google)
  • SAML SSO
  • Gestion sécurisée des sessions
  • RBAC
  • Authentification du lien magique

Notez que si la version initiale n’a pas changé, cela signifie que le projet n’a pas reçu suffisamment de commentaires et de contributions. Naturellement, la liste s'est allongée.

La liste de contrôle actuelle :

  • Inscrivez-vous avec les informations d'identification de l'utilisateur
  • Inscrivez-vous auprès des fournisseurs OAuth (Github, Google)
  • Connectez-vous avec les informations d'identification de l'utilisateur
  • Connectez-vous avec les fournisseurs OAuth (Github, Google)
  • Connectez-vous avec SSO (SAML)
  • Connectez-vous depuis Novu CLI
  • Connectez-vous/vous depuis Vercel Marketplace
  • Créer une organisation
  • Gestion de l'organisation
  • Gestion des utilisateurs (mettre à jour les informations utilisateur, les informations d'identification, etc…)
  • MFA/2FA (OTP via sms/email, TOTP, mot de passe, biométrique, etc.)
  • Invitations
  • RBAC : Deux rôles d'administrateur et d'éditeur
    • admin = L'administrateur peut accéder et interagir avec n'importe quelle page de la plateforme Web (y compris les membres de l'équipe et les paramètres)
    • éditeur = Le rôle d'éditeur reste le "gestionnaire de contenu principal" (alias chef de produit ou responsable marketing)

Recherche et évaluation

Après avoir identifié la portée du projet, l'étape suivante consiste à mener des recherches et à évaluer les ressources nécessaires pour atteindre le résultat souhaité.

Ce processus comprend :

  • Avoir une compréhension très claire de l'état actuel et de chaque couche du produit :

    • Dépendances
    • Points de terminaison
    • Architecture
    • Composants et représentation de la couche client (frontend)
    • Tests

    Et plus encore.

  • Décrivez les spécifications de migration (ce qui reste en interne et doit être obstrué)

  • Compatibilité descendante

  • Essayez de trouver des références à des projets similaires, d'anciens collègues peut-être, et apprenez de leur processus et de leurs recommandations

  • Essayez de trouver des solutions open source

  • Trouvez s'il existe des fournisseurs (solutions tierces) et comparez-les.

Et plus encore.

Dans un autre article de blog, nous explorerons comment nous évaluons et comparons les solutions (ou produits) tiers en tant que service/infrastructure en tant que société de services.

Une recherche insuffisante ou une évaluation inexacte entraîne généralement une dette technique et de futures pertes de ressources, telles que du temps d'ingénierie lors de l'ajout de fonctionnalités supplémentaires et de la maintenance, qui nécessitent une refactorisation de l'ensemble. Alors, recherchez les coûts cachés de chaque option.

Les chefs d'équipe expérimentés savent comment évaluer le retour sur investissement (ROI) de chaque option, ce qui les aide à prendre la meilleure décision pour l'entreprise.

C’est exactement comme ça que nous nous sommes retrouvés avec Clerk. Leur solution couvre la plupart de nos cas d'utilisation et d'un point de vue métier, le retour sur investissement de leur mise en œuvre pour gérer la couche utilisateurs et organisations est logique.


Mise en œuvre

Le service Novu contient de nombreux micro-services et aspects tels que :

  • Canaux de notification (SMS, e-mail, In-app, Push, Chat, etc.),
  • Orchestration des notifications (synchronisation multi-appareils, moteur de résumé, délai, prise en compte du fuseau horaire, etc.)
  • Observabilité des notifications (débogage, informations, etc.)
  • Gestion du contenu des notifications (éditeur, branding, mises en page, traductions, gestion des variables, etc.)
  • Gestion des utilisateurs finaux (Préférences utilisateur, abonnés, sujets, segments, gestion des abonnements, etc.)
  • Gestion des comptes (SSO, contrôle d'accès basé sur les rôles, multi-location, facturation, etc.)

Le diagramme ci-dessous montre une version simplifiée de la structure de l'API de Novu, se concentrant uniquement sur l'authentification et l'autorisation des utilisateurs et des organisations de Novu avant d'implémenter Clerk.

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

Nous utilisons MongoDB pour stocker toutes les données dont Novu a besoin, chaque utilisateur, organisation, locataire, abonné, sujet… bref, tout.

Étant donné que Clerk dispose de sa propre base de données pour gérer les utilisateurs, nous devions gérer la migration et la synchronisation entre les bases de données avec beaucoup de soin et de précision.


Stratégie JWT vs stratégie Clerk

L'une des principales choses dont nous devions nous assurer est que l'objet UserSessionData ne changera pas pour ne pas interrompre la session de l'utilisateur lors de l'utilisation de Novu. Il devrait rester compatible.

Ici vous pouvez voir l'exemple de fichier 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;
  }
}
Copier après la connexion

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

Pour maintenir la compatibilité avec le reste de l'application, nous devions transformer la charge utile JWT de Clerk au format JWT précédemment existant.

Voici comment nous avons procédé :

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;
}
Copier après la connexion

Ici vous pouvez voir l'exemple de fichier 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...
}

Copier après la connexion

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


Synchronisation entre Clerk et Novu

Bien que l'objectif soit d'utiliser idéalement uniquement Clerk pour créer et récupérer des utilisateurs, des organisations, etc., ce n'est malheureusement pas entièrement possible en raison de la nécessité de stocker et d'interroger certaines métadonnées sur les utilisateurs et les organisations de manière performante.

Voici un exemple de méthode dans le référentiel d'organisation de 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 }
    );
  }
Copier après la connexion

Cette méthode utilise diverses constructions spécifiques à MongoDB pour filtrer un document - ceci n'est pas possible à reproduire en utilisant Clerk de manière performante car ce n'est pas une base de données destinée à de telles requêtes.

Ce que nous pouvons faire est de stocker ces métadonnées sur l'organisation dans notre collection d'organisations MongoDB et de lier/synchroniser la collection avec la base de données Clerk en utilisant externalId.

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

Nous pouvons désormais combiner Clerk et MongoDB pour interroger les métadonnées si nécessaire.

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);
}
Copier après la connexion

En appelant getUsersMembersOrganizations, findPartnerConfigurationDetails obtient les données d'organisation nécessaires pour effectuer une recherche filtrée sur communityOrganizationRepository, garantissant que seules les configurations pertinentes sont renvoyées.

Nous devons synchroniser uniquement les utilisateurs et les organisations entre Clerk et Novu, les membres de l'organisation n'ont pas besoin d'être synchronisés.


Synchronisation des utilisateurs et des organisations

Il existe deux manières de synchroniser les identifiants de base de données :

  • 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;
};
Copier après la connexion

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);
    }
  }
}

Copier après la connexion

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 {
    ...
}

Copier après la connexion

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


Points bonus rétrospectifs

Lorsque nous avons pris la décision de mettre en œuvre Clerk pour la gestion des utilisateurs, nous avons également opté pour les avantages à long terme liés à l'extension des capacités et des fonctionnalités que Clerk prendra en charge et offrira à l'avenir.

Voici quelques exemples de ce que nous pourrions envisager de soutenir dans un avenir proche :

  • Contrôle d'accès précis (FGAC)
    • Basé sur les attributs : FGAC est souvent mis en œuvre à l'aide d'un contrôle d'accès basé sur les attributs (ABAC), où les décisions d'accès sont basées sur divers attributs des utilisateurs, des ressources et de l'environnement. Les attributs peuvent inclure le rôle de l'utilisateur, le service, le type de ressource, l'heure de la journée, etc.
    • Flexibilité : FGAC offre une plus grande flexibilité et granularité en permettant des contrôles d'accès détaillés et basés sur des conditions. Cela signifie que les autorisations peuvent être ajustées à des scénarios très spécifiques.
    • Dynamique : FGAC peut s'adapter de manière dynamique aux changements de l'environnement, tels que les accès urgents ou les restrictions basées sur la localisation.
    • Autorisations détaillées : les autorisations dans FGAC sont plus spécifiques et peuvent être adaptées à des actions individuelles, des utilisateurs ou des situations.

Fournir ce niveau de flexibilité détaillée à Novu aurait pu être hors de portée ou même supprimé dès le départ en raison de la complexité potentielle de la mise en œuvre.

  • Usurpation d'identité de l'utilisateur

    Nos équipes de réussite client ou d'assistance pourraient l'utiliser pour résoudre des problèmes, fournir une assistance ou tester des fonctionnalités du point de vue de l'utilisateur usurpé sans avoir besoin de connaître son mot de passe ou d'autres détails d'authentification.

    • Réduit le temps et la complexité liés au diagnostic et à la résolution des problèmes des utilisateurs.
    • Garantit que les mesures d'assistance ou d'administration prises sont exactes puisque le personnel d'assistance peut voir et interagir avec le système exactement comme le ferait l'utilisateur.
    • Améliore la satisfaction des utilisateurs en fournissant une assistance plus rapide et plus efficace.

En termes simples, nous pourrons facilement améliorer l'expérience des utilisateurs de Novu étant donné que désormais, l'infrastructure d'authentification est prévue pour cela.


Si vous souhaitez suggérer des fonctionnalités supplémentaires impliquant AuthN (ou tout autre), visitez notre feuille de route pour examiner et voter pour les demandes, ou soumettre votre idée.


Vous avez aimé ce que vous avez lu ? Cliquez sur suivre pour plus de mises à jour et déposez un commentaire ci-dessous. Je voudrais ❤️ entendre votre ?

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

Emil Pearce

J'écris du code et des mots dans les cafés.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal