Services
The Infrastructure package provides a suite of cross-cutting services that encapsulate business logic shared across the Alert.CA solution. These services are registered via dedicated extension methods and cover the full alert lifecycle, auditing, external communications, Microsoft Graph integration, notifications, and security.
Registration Overview
Each service has its own extension method — there is no combined registration call for security services.
AlertService
The AlertService orchestrates the full alert lifecycle — from anomaly location creation through subscriber matching and notification dispatch.
Registration
services.AddAlertService(configuration);
Binds NotificationOptions and ESRIOptions, then registers its own repository dependencies internally (ILocationService, ILocationRepository, IAlertSubscriptionRepository, INotificationRepository, IAlertMessageRepository, IUserNotificationRepository, IAlertMessageUserRepository, IAlertMessageAgencyRepository, IAlertMessageLocationRepository, IUserSqlRepository) and IAlertService.
Alert Lifecycle
Key Methods
| Method | Description |
|---|---|
CreateAnomalyLocationAsync(ESRIEntity) | Create anomaly location from ESRI polygon |
CreateAnomalyLocationAsync(PointEntity) | Create anomaly location from lat/lon/radius |
CreateLocationForSubscriptionAsync(ESRIEntity/PointEntity) | Create a location for subscription use (2 overloads) |
CreateAlertSubscription(AlertSubscriptionDTO) | Create alert subscription |
UpdateAlertSubscription(AlertSubscriptionDTO) | Update subscription |
DeleteSubscriptionById(int) | Delete subscription |
GetSubscriptionByEntraUserIdAsync(string) | Get user's subscriptions with locations |
PauseAlertSubscriptions(string, bool) | Pause/resume all subscriptions for a user |
GetPreDefinedLocations() | Get predefined location options grouped by type |
CreateAlertMessageAsync(AlertMessageDTO) | Full alert creation: anomaly location → alert message → associate recipients |
GetAlertMessageById(int) | Retrieve alert message by ID |
CreateNotificationFromAlertMessage(AlertMessage) | Find intersecting subscribers → filter by urgency bitmask and day/time → create notification + user notifications |
GetAlertRecipientsByLocationId(int) | Get recipient Entra user IDs for a location |
Configuration
{
"NotificationOptions": {
"Endpoint": ""
},
"ESRIOptions": {
"AlertEndpoint": "",
"CamerasEndpoint": "",
"PulsePointEndpoint": "",
"ApiKey": "",
"OrgId": "",
"ClientId": "",
"Secret": ""
}
}
AuditService
The AuditService provides structured audit logging with transaction grouping support.
Registration
services.AddAuditService();
No configuration parameter — registers IAuditService as a scoped service. Depends on IAuditEventRepository being available in the container (registered by AddAlertCASQLDatabaseServices).
Key Methods
| Method | Description |
|---|---|
CreateSingleAudit(AuditEventCreate audit, string? transactionId) | Log a single audit event, auto-generates transaction ID if not provided |
CreateTransaction(IEnumerable<AuditEventCreate> audits) | Log multiple events under a shared transaction ID |
The AuditEventCreate model captures: EntraUserId, EntraDisplayName, EntraEmail, CompanyName, ActionName, ActionDescription, ActionPath, ActionQuery, ActionParameters, ActionNotes, ActionSource.
Communication Services
Two services handle outbound communications via Azure Communication Services:
Registration
services.AddCommunicationServices(configuration);
Registers SmsClient and EmailClient as singletons (created from the CommunicationServicesUrl), then ISmsService and IEmailService as scoped services.
Configuration
{
"CommunicationServicesOptions": {
"CommunicationServicesUrl": "",
"CognitiveServicesUrl": "",
"SendingPhoneNumber": "",
"SendingEmail": ""
}
}
SmsService
Sends SMS notifications with automatic chunking for large recipient lists.
Interface: ISmsService
| Method | Description |
|---|---|
SendBulkAsync(IEnumerable<string> phoneNumbers, string message) | Send SMS to multiple recipients — automatically chunks lists over 100 for the Azure SDK limit |
EmailService
Sends email notifications with support for HTML content, CC/BCC, and file attachments.
Interface: IEmailService
| Method | Description |
|---|---|
SendEmailsAsync(subject, body, toUsers, ccUsers, bccUsers, htmlBody?, attachments?) | Send email to IEnumerable<EntraUserEntity> recipients |
SendEmailsAsync(subject, body, toUsers, ccUsers, bccUsers, htmlBody?, attachments?) | Send email to IEnumerable<User> recipients (SQL User model overload) |
Both overloads support optional htmlBody (falls back to HTML-encoded plain text) and IEnumerable<EmailAttachmentDto> attachments.
PhoneCallService (Not Auto-Registered)
Makes outbound phone calls via Azure Communication Services CallAutomationClient with Cognitive Services integration for text-to-speech. Not registered by AddCommunicationServices — must be manually registered if needed.
Interface: IPhoneCallService
| Method | Description |
|---|---|
CallTargetAsync(string phoneNumber, string hostAppCallbackUri) | Initiates a phone call to the target number; the hostAppCallbackUri receives call event callbacks |
GetCallConnection(CallAutomationEventBase callEvent) | Retrieves the CallConnection for an in-progress call to perform further operations (e.g., play text-to-speech, hang up) |
GraphService
The GraphService integrates with Microsoft Graph for Entra ID user directory operations.
Registration
services.AddGraphServices(configuration);
Creates a GraphServiceClient with ClientSecretCredential and registers IGraphService as a scoped service.
Configuration
{
"EntraOptions": {
"TenantId": "",
"ClientId": "",
"ClientSecret": ""
}
}
Key Methods
| Method | Description |
|---|---|
GetOneUserById(string entraId) | Get single user by Entra Object ID |
GetUsersByIds(List<string> entraIds) | Get multiple users by IDs (filtered query) |
GetAllUsers() | Get all users — handles Graph API pagination via OdataNextLink |
GetAllCompanies() | Get distinct company names across all users |
GetUsersByCompanies(List<string> companyNames) | Get users filtered by company name — handles pagination |
All methods return EntraUserEntity objects (or EntraCompanyEntity for companies). The service selects a fixed set of user properties: id, displayName, givenName, surname, jobTitle, companyName, mail, mobilePhone, officeLocation, preferredLanguage, userPrincipalName, businessPhones.
NotificationService
The NotificationService manages in-app notification retrieval and status updates.
Registration
services.AddNotificationService(configuration);
Binds NotificationOptions and registers INotificationService as a scoped service.
Configuration
{
"NotificationOptions": {
"Endpoint": ""
}
}
Key Methods
| Method | Description |
|---|---|
GetNotificationWithRecipientsAsync(int notificationId) | Returns NotificationDTO with recipients, sender, and alert message details (lat/lon, map image URL, first image URL) |
UpdateNotificationStatus(int notificationId, NotificationStatus newStatus) | Update notification status |
The NotificationService internally resolves INotificationRepository, IUserNotificationRepository, IUserSqlRepository, and IAlertMessageRepository to assemble the full notification DTO.
ApiKeyValidator
Validates API keys for service-to-service authentication (non-OAuth scenarios, e.g., Azure Function triggers).
Registration
services.AddApiKeyValidator(configuration);
Binds FunctionApiKeyOptions and registers IApiKeyValidator as a scoped service.
Configuration
{
"FunctionApiKeyOptions": {
"FunctionApiKey": ""
}
}
Key Methods
| Method | Description |
|---|---|
ValidateApiKey(HttpRequestData req) | Validate x-api-key header from Azure Functions isolated worker requests |
ValidateApiKey(HttpRequest req) | Validate x-api-key header from ASP.NET Core requests |
CreateUnauthorizedResponse(HttpRequestData req) | Create a 401 Unauthorized response for Azure Functions |
JWTService
The JWTService manages OAuth2/OIDC authorization flows using Azure AD B2C CIAM (alertcalifornia.ciamlogin.com) with certificate-based client assertions.
Registration
services.AddJWTService(configuration);
Registered as a singleton. Also registers HttpClient via AddHttpClient().
Configuration
{
"JWTOptions": {
"TenantId": "",
"FrontendRedirectUri": "",
"FrontendClientId": "",
"FrontendAPIClientId": "",
"FrontendCertificatePEM": "",
"FrontendCertificateThumbprint": "",
"AdminRedirectUri": "",
"AdminClientId": "",
"AdminAPIClientId": "",
"AdminCertificatePEM": "",
"AdminCertificateThumbprint": ""
}
}
Key Methods
| Method | Description |
|---|---|
GetFrontendAuthorizeRequest() | Build OAuth2 authorize URL for frontend app |
GetAdminAuthorizeRequest() | Build OAuth2 authorize URL for admin app |
GetFrontendTokensAsync(string code, string state) | Exchange authorization code for tokens (frontend) |
GetAdminTokensAsync(string code, string state) | Exchange authorization code for tokens (admin) |
GetFrontendClientAssertion() | Generate RSA-signed JWT client assertion for frontend |
GetAdminClientAssertion() | Generate RSA-signed JWT client assertion for admin |
The service uses certificate-based client authentication — PEM certificates are loaded from configuration, and client assertions are RSA-signed JWTs sent as client_assertion in token requests.
EncryptionService
The EncryptionService provides AES-CBC symmetric encryption with RSA key management via Azure Key Vault.
Registration
services.AddEncryptionService(configuration, credential);
At startup, loads the RSA key from Key Vault and decrypts the stored symmetric key (CameraDataKey) using RSA-OAEP. The decrypted AES key is held in memory for the lifetime of the singleton.
Configuration
{
"KeyVaultOptions": {
"KeyVaultName": "",
"Url": "",
"CameraKeyName": "",
"CameraDataKey": ""
}
}
Encryption Flow
Key Methods
| Method | Description |
|---|---|
EncryptAsync(string plainText) | Generate random IV → AES-CBC encrypt → prepend IV → return Base64 |
DecryptAsync(string encryptedText) | Extract IV from first 16 bytes → AES-CBC decrypt remainder |
SetSymmetricKey() | Generate new AES key → encrypt with RSA-OAEP → store in Key Vault as secret |