Apps multi-tenant

Una app tuya para muchas empresas: inicio de sesión con DinaupAuthClient, un MyAppClient por usuario vinculado a su empresa y el almacén clave-valor MyAppKVClient.

Una sola app tuya puede dar servicio a muchas empresas: Dinaup pone el inicio de sesión, el aislamiento entre empresas y la base de datos, y tú la interfaz y la lógica.

Antes de empezar

  • Paquete Dinaup y tu biblioteca MyDinaup.
  • El AppId (un GUID) y el AppToken de tu aplicación, registrados en Dinaup. Guárdalos en el Vault con las claves DINAUP_APPID y DINAUP_APPTOKEN.

Las tres piezas

PiezaQué esCiclo de vida
DinaupExternalAppSettingsLa identidad de tu aplicación: AppId + AppToken.Singleton
DinaupAuthClientAutentica usuarios por correo y contraseña y devuelve su empresa.Singleton
MyAppClientEl cliente de datos de un usuario, vinculado a su empresa.Uno por sesión (Scoped)

MyAppClient hereda de DinaupClientC: todo lo del Cliente Dinaup funciona igual, pero cada instancia solo llega a la empresa del usuario autenticado.

Montar la app

Carga la identidad de la app

var vault = new Dinaup.Vault.VaultData("VAULT_URL", "VAULT_PASSWORD");
vault.Initialize();

var appConfig = new Dinaup.Models.DinaupExternalAppSettings(vault);   // lee DINAUP_APPID y DINAUP_APPTOKEN

Sin Vault: new DinaupExternalAppSettings(appId, appToken), con appId como Guid.

Registra los servicios

builder.Services.AddSingleton<Dinaup.Models.DinaupExternalAppSettings>(appConfig);
builder.Services.AddSingleton<Dinaup.Auth.DinaupAuthClient>(new Dinaup.Auth.DinaupAuthClient());
builder.Services.AddSingleton<Dinaup.MyAppKVClient>();     // se construye con el DinaupExternalAppSettings registrado
builder.Services.AddScoped<SessionUserContext>();          // tu contexto de sesión, uno por usuario

builder.Services.AddHealthChecks().AddCheck<Dinaup.MyAppKVClient>("MyAppKVClient");

El MyAppClient y cualquier servicio que lo use van en Scoped. Un singleton compartiría la sesión de una empresa con los usuarios de otra.

Autentica al usuario

LoginAsync(email, password) valida las credenciales contra auth.dinaup.com y devuelve un AuthResponse, o null si fallan. Con él construyes su MyAppClient.

var authResponse = await _authClient.LoginAsync(email, password);
if (authResponse == null) throw new Exception("Credenciales inválidas");

DinaupClient = new Dinaup.MyAppClient(_appSettings, authResponse);

AuthResponse trae TenantConnectionKeyword (la empresa), UserEmail, UserId, LicId, LicSerie, ApiEndpoint, Version, Revision y TwoFactor.

Para reconstruir la sesión en otra petición, guarda la clave de conexión y el correo en MyAppKVClient y usa el tercer constructor:

await _kvClient.SetKVAsync($"session:{sessionId}", $"{authResponse.TenantConnectionKeyword}|{authResponse.UserEmail}");

// En otra petición
var partes = (await _kvClient.GetKVAsync($"session:{sessionId}")).Split('|');
DinaupClient = new Dinaup.MyAppClient(_appSettings, partes[0], partes[1]);

Opera con los datos de su empresa

var rpt = new APIPaisesC();
await rpt.ExecuteQueryAsync(_session.DinaupClient, 1, 1000);

Qué añade MyAppClient

MiembroQué hace
ExecuteReport(ReportRequestParameters, filter)Ejecuta un informe por parámetros y devuelve un ReportResponse.
ExecuteReport(id, QuerySearch, AdvancedFilter, ResultsPerPage, order, adminmode)Ejecuta un informe por id con búsqueda, filtros (List<FilterCondition>), orden y modo administrador. Hay sobrecargas con variables (Dictionary<string, string>) y con LoadDataReportC.
Email, Nombre, IsLoggedDatos de la sesión. MyAppClient implementa IUserSession y queda como DefaultSession del cliente.
LicenseId, UserIdDevuelven Guid.Empty. Para atribuir escrituras a un usuario, usa DinaupContext.WithUser.

Cuentas de usuario

Tu app da de alta usuarios, los activa y gestiona contraseñas sin pantallas de Dinaup. Todos los métodos reciben el userAgent y la IP del usuario final.

MétodoQué hace
Session_RegisterAccountAsync(model, userAgent, userIP)Crea la cuenta temporal y genera el código de activación. model es un AccountCreationModel.
Session_ConfirmAccountRegistrationAsync(model, userAgent, userIP)Activa la cuenta usando el código recibido. model es un AccountActivationModel.
Session_SignInAsync(email, password, userAgent, userIP, verifyPassword = true)Inicia sesión y devuelve un SessionSignInResponse (correcta, 2FA pendiente, rechazada).
Session_CreateLoginCodeAsync(email, userAgent, userIP) y Session_SignInWithCodeAsync(email, code, userAgent, userIP)Acceso sin contraseña: envía un código y entra con él.
Session_CheckTwoFactor(sessionId, code, userAgent, userIP)Verifica el código 2FA de una sesión pendiente. sessionId es Guid.
Session_GetDetailsAsync(sessionId, userAgent, userIP)Detalles de una sesión activa.
Session_SignOutAsync(sessionId, userAgent, userIP)Cierra la sesión.
Session_RecoverPasswordAsync(email, userAgent, userIP)Inicia la recuperación de contraseña por correo.
Session_CreatePasswordRecoveryCodeAsync(model, userAgent, userIP)Genera un código de recuperación. model es un PasswordRecoveryModel.
Session_ChangePasswordWithCodeAsync(userEmail, code, newPassword, userAgent, userIP)Cambia la contraseña con ese código.

Para cambiar la contraseña de un usuario, genera el código con Session_CreatePasswordRecoveryCodeAsync y cámbiala con Session_ChangePasswordWithCodeAsync. Los modelos de entrada (Dinaup.Models) validan sus campos antes de llamar al servidor.

Almacenamiento clave-valor de la app

MyAppKVClient guarda pares clave-valor de tu aplicación: configuración, indicadores y sesiones de tus usuarios.

bool guardado = await _kvClient.SetKVAsync("clave", "valor");
string valor = await _kvClient.GetKVAsync("clave");   // "" si no existe
  • Texto plano, hasta 10 MB por clave; por encima, SetKVAsync lanza InvalidOperationException. Un error HTTP lanza HttpRequestException.
  • Las claves son de la aplicación, no de cada empresa: prefija lo que sea de una empresa o una sesión (session:{id}, config:{empresa}).
  • Como comprobación de estado (AddCheck<MyAppKVClient>), escribe la clave myappkvclient.health en cada sonda y responde Unhealthy si falla. Ver ASP.NET Core.

Los pares de una empresa van en otro almacén: Clave-valor de la empresa.

En esta página