Overview

Prima server configuration is managed through the appsettings.json file located in the Prima installation directory (typically C:\Program Files\FortelineaSoftwareSystems\Prima\Services\PrimaWebService).

Configuration File Structure

The configuration uses the standard ASP.NET Core configuration system. Settings can be overridden using:

  • appsettings.json - Base configuration
  • Environment variables
  • Command-line arguments

Configuration Sections

Use the tabs on the left to learn about configuring each section:

  • Authentication - Windows/LDAP authentication, JWT settings, Identity configuration
  • Image Storage - Local file storage, S3-compatible cloud storage, path configuration
  • Web Server (Kestrel) - URLs, ports, HTTPS/SSL certificates, request limits
  • Database Connection - SQL Server connection strings, Entity Framework settings
Tip: After making configuration changes, restart the Prima Windows service for changes to take effect.

Authentication

Prima supports three authentication methods. The authentication method is determined automatically based on which configuration sections are present and fully configured.

Supported Authentication Methods

Method Use Case Configuration Section
Prima Built-in database authentication (default) No additional configuration required
LDAP Active Directory / LDAP integration LdapConfiguration
Okta Cloud-based identity management OktaConfiguration

Authentication Priority

When multiple authentication methods are configured, Prima uses this priority order:

  1. Okta - If OktaConfiguration is fully configured
  2. LDAP - If LdapConfiguration has AuthenticationType: "Ldap" and is fully configured
  3. Prima - Default fallback (built-in database authentication)

JWT Token Configuration

JWT tokens are used for API authentication regardless of which authentication method is active.

{
  "JwtTokenSettings": {
    "Secret": "your-secret-key-here-minimum-32-characters",
    "RefreshTokenTTL": 2
  }
}
Property Type Description
Secret string Secret key for JWT token signing. Must be changed from default before production use.
RefreshTokenTTL integer Refresh token time-to-live in days (default: 2)
Security: The Secret value must be changed from "REPLACEME" before deploying to production. Use a strong, random string of at least 32 characters.

LDAP / Active Directory Configuration

Configure LDAP authentication to integrate with your organization's Active Directory.

{
  "LdapConfiguration": {
    "AuthenticationType": "Ldap",
    "Host": "ad.company.com",
    "Port": 636,
    "Domain": "company.com",
    "UserStore": "OU=Employees,DC=company,DC=com",
    "Protocol": "LDAPS",
    "AuthType": "Negotiate",
    "Version": 3,
    "TimeOut": "00:01:00",
    "LdapRetryInterval": 1500
  }
}
Required Properties
Property Type Description
AuthenticationType string Must be set to "Ldap" to enable LDAP authentication
Host string LDAP server hostname or IP address (e.g., "ad.company.com")
Protocol string "LDAP" (port 389, unencrypted) or "LDAPS" (port 636, encrypted)
Optional Properties
Property Type Default Description
Port integer 636 or 389 LDAP server port (auto-selected based on Protocol)
Domain string Auto-detected Active Directory domain name (e.g., "company.com")
UserStore string - Distinguished name for user search (e.g., "OU=Users,DC=company,DC=com")
Version integer 3 LDAP protocol version
AuthType string "Negotiate" Authentication method: "Basic", "Negotiate", or "Digest"
TimeOut timespan "01:00:00" LDAP connection timeout
LdapRetryInterval integer 1500 Retry interval in milliseconds
CertificateVerificationMethod string "Default" SSL cert verification: "Default", "Skip", or "CustomFile"
X509CertificatePath string - Path to X.509 certificate (when using "CustomFile")
Auto-Discovery: When Host is not specified, Prima attempts to auto-discover the domain controller using Domain.GetComputerDomain().

Okta Configuration

Configure Okta for cloud-based identity management using OAuth 2.0.

{
  "OktaConfiguration": {
    "Domain": "mycompany.okta.com",
    "ClientId": "0oab1234567890ABC",
    "ClientSecret": "X7aB_cD-eFg_HiJk_lMnOpQrStUvWxYz123456789",
    "AuthorizationServerId": "default",
    "TokenValidationCacheDuration": "00:05:00",
    "ValidateAudience": true,
    "ValidateIssuer": true
  }
}
Required Properties
Property Type Description
Domain string Your Okta tenant domain (e.g., "company.okta.com")
ClientId string OAuth 2.0 application client ID from Okta
ClientSecret string OAuth 2.0 application client secret from Okta
Optional Properties
Property Type Default Description
AuthorizationServerId string "default" Okta authorization server ID
TokenValidationCacheDuration timespan "00:05:00" Duration to cache token validation results
ValidateAudience boolean true Whether to validate the audience claim in tokens
ValidateIssuer boolean true Whether to validate the issuer claim in tokens
Setting Up the Okta Application (Okta Admin Console)

These steps are performed in the Okta Admin Console by your Okta administrator. Prima validates user credentials directly against Okta's token endpoint using the OAuth 2.0 Resource Owner Password grant — users sign in on Prima's own login screen, and Prima verifies the password with Okta behind the scenes. No browser redirect to Okta is involved.

  1. In the Okta Admin Console, go to Applications > Applications and click Create App Integration.
  2. Select OIDC - OpenID Connect as the sign-in method.
  3. Select Web Application as the application type. Prima authenticates as a confidential client and needs both a client ID and a client secret, which this type provides.
  4. Give the application a name (for example, Prima).
  5. Leave the Sign-in redirect URIs field at its default value (e.g. http://localhost/callback). Because Prima never redirects the user's browser to Okta, redirect URIs are not used — Okta simply requires a value in this field. Do not check the Allow wildcard * in sign-in URI redirect box.
  6. Under Assignments, choose which users or groups may use Prima. Users who are not assigned to the application will fail to authenticate.
  7. Save the application, then open its General tab and click Edit in the General Settings section.
  8. Under Grant type, check Resource Owner Password (it may be listed under Advanced). This step is required — without it, every Prima login will fail even when the credentials are correct.
  9. Copy the Client ID and Client Secret from the Client Credentials section and enter them, along with your Okta domain, in the OktaConfiguration section of appsettings.json as shown above.
User accounts: Okta replaces password verification only — each user must still exist as a Prima user, and the Prima username must match the user's Okta sign-in name (typically their email address). Prima sends the Prima username to Okta during login, so a mismatch will cause authentication to fail.
Authorization server: Prima authenticates against the Okta authorization server named by AuthorizationServerId ("default" unless configured otherwise). The default custom authorization server is included with Okta's API Access Management feature; if your Okta org does not have it, set AuthorizationServerId to an authorization server that exists in your org. The openid, profile, and email scopes Prima requests are present on the default server out of the box.
Multi-factor authentication: The Resource Owner Password grant cannot complete MFA challenges. Users covered by an Okta sign-on policy that requires MFA will not be able to log in to Prima. Your Okta administrator may need to configure an authentication policy exception for the Prima application.
Issuer URL: Prima constructs the issuer URL as: https://{Domain}/oauth2/{AuthorizationServerId}
Example: https://company.okta.com/oauth2/default

Configure session cookies for web UI authentication.

{
  "CookieAuthentication": {
    "ExpireTimeSpan": "01:30:00",
    "SlidingExpiration": true
  },
  "CookiePolicyOptions": {
    "Secure": "Always",
    "HttpOnly": "Always",
    "MinimumSameSitePolicy": "Lax"
  }
}
Property Type Default Description
ExpireTimeSpan timespan "01:30:00" Session timeout duration
SlidingExpiration boolean true Extend expiration on each request
Secure string "Always" Cookie transmission: "Always", "None", "SameAsRequest"
HttpOnly string "Always" Prevent JavaScript access: "Always" or "None"
MinimumSameSitePolicy string "Lax" CSRF protection: "None", "Lax", "Strict"
Load Balancer Note: When running behind a load balancer that terminates HTTPS, set "Secure": "None" to allow cookies over the internal HTTP connection.

Configuration Examples

Example 1: LDAP with Active Directory
{
  "LdapConfiguration": {
    "AuthenticationType": "Ldap",
    "Host": "ad.company.com",
    "Port": 636,
    "Domain": "company.com",
    "UserStore": "OU=Employees,DC=company,DC=com",
    "Protocol": "LDAPS",
    "AuthType": "Negotiate",
    "Version": 3,
    "TimeOut": "00:01:00"
  },
  "JwtTokenSettings": {
    "Secret": "your-production-secret-key-at-least-32-chars",
    "RefreshTokenTTL": 2
  }
}
Example 2: Okta Cloud Authentication
{
  "OktaConfiguration": {
    "Domain": "mycompany.okta.com",
    "ClientId": "0oab1234567890ABC",
    "ClientSecret": "X7aB_cD-eFg_HiJk_lMnOpQrStUvWxYz123456789",
    "AuthorizationServerId": "default"
  },
  "JwtTokenSettings": {
    "Secret": "your-production-secret-key-at-least-32-chars",
    "RefreshTokenTTL": 2
  }
}
Example 3: Prima Default (Built-in Database)
{
  "LdapConfiguration": {
    "AuthenticationType": "Prima"
  },
  "JwtTokenSettings": {
    "Secret": "your-production-secret-key-at-least-32-chars",
    "RefreshTokenTTL": 2
  }
}
Tip: After making authentication configuration changes, restart the Prima Windows service for changes to take effect.

Image Storage

Configure where Prima stores digital images including cassette images, slide images, specimen photos, and scanned documents. Prima supports four storage modes: File (local/network paths), S3 (Amazon S3 or compatible services), Azure Blob (Microsoft Azure Blob Storage), and Advanced (different storage per image type).

Storage Modes Overview

Only one storage mode can be active at a time. Configure one of the following in appsettings.json:

Mode Use Case Configuration Key
File Local disk or network share storage (simplest option) ImageStorage.File
S3 Cloud storage using Amazon S3 or compatible services (Digital Ocean Spaces, MinIO) ImageStorage.S3
Azure Blob Cloud storage using Microsoft Azure Blob Storage ImageStorage.AzureBlob
Advanced Different storage location per image type (e.g., slides in S3, documents on network share) ImageStorage.Advanced

File Storage Mode

The simplest configuration stores all images in subdirectories under a single root path.

{
  "ImageStorage": {
    "File": {
      "RootDirectory": "C:\\ProgramData\\Prima\\Storage"
    }
  }
}

Prima automatically creates subdirectories for each image type:

  • CassetteImages/ - Photos of cassettes
  • SlideImages/ - Whole slide images
  • SpecimenImages/ - Specimen gross photos
  • Documents/ - Scanned documents and attachments
Network Shares: You can use a UNC path for network storage: \\\\server\\share\\PrimaStorage (note the escaped backslashes in JSON).

S3 Storage Mode

Store all images in an Amazon S3 bucket or S3-compatible service (Digital Ocean Spaces, MinIO, etc.).

Step 1: Define Credential Profiles

First, create one or more credential profiles that can be referenced by storage configurations:

{
  "StorageCredentialProfiles": [
    {
      "Name": "aws-production",
      "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
      "SecretAccessKey": "${secrets:env:AWS_SECRET_KEY}",
      "Region": "us-east-1",
      "Endpoint": null
    },
    {
      "Name": "digitalocean-spaces",
      "AccessKeyId": "DO00EXAMPLE123",
      "SecretAccessKey": "${secrets:env:DO_SPACES_SECRET}",
      "Region": "nyc3",
      "Endpoint": "https://nyc3.digitaloceanspaces.com"
    }
  ]
}
Property Description
Name Unique identifier for referencing this profile
AccessKeyId AWS access key ID or equivalent for S3-compatible services
SecretAccessKey Secret key (supports secret resolution syntax - see below)
Region AWS region (e.g., us-east-1) or service region
Endpoint Custom endpoint URL for S3-compatible services (leave null for AWS)
Step 2: Configure S3 Storage
{
  "ImageStorage": {
    "S3": {
      "CredentialProfile": "aws-production",
      "BucketName": "prima-images",
      "Prefix": "prod/"
    }
  }
}
Property Description
CredentialProfile Name of a credential profile defined in StorageCredentialProfiles
BucketName S3 bucket name
Prefix Optional prefix for all object keys (useful for multi-tenant or environment separation)
S3 Settings (Optional)

Configure retry behavior and timeouts for S3 and Azure Blob operations:

{
  "S3Settings": {
    "TimeoutSeconds": 30,
    "MaxRetryAttempts": 3,
    "RetryDelayMilliseconds": 500
  }
}

Azure Blob Storage Mode

Store all images in a Microsoft Azure Blob Storage container.

{
  "ImageStorage": {
    "AzureBlob": {
      "ConnectionString": "${secrets:env:AZURE_STORAGE_CONNECTION_STRING}",
      "ContainerName": "prima-images",
      "Prefix": "prod/"
    }
  }
}
Property Description
ConnectionString Azure Storage account connection string (supports secret resolution syntax)
ContainerName Azure Blob container name
Prefix Optional prefix for all blob names (useful for multi-tenant or environment separation)
Connection String Format: You can find your connection string in the Azure Portal under Storage Account → Access Keys. The format is: DefaultEndpointsProtocol=https;AccountName=yourAccount;AccountKey=yourKey;EndpointSuffix=core.windows.net

Advanced Storage Mode

Configure different storage locations for each image type. This is useful for hybrid scenarios, such as keeping high-volume slide images in cloud storage while keeping documents on a local network share.

{
  "ImageStorage": {
    "Advanced": {
      "CassetteImage": {
        "Type": "File",
        "Path": "C:\\Storage\\Cassettes"
      },
      "SlideImage": {
        "Type": "S3",
        "CredentialProfile": "aws-production",
        "BucketName": "prima-slides",
        "Prefix": ""
      },
      "SpecimenImage": {
        "Type": "AzureBlob",
        "ConnectionString": "${secrets:env:AZURE_STORAGE_CONNECTION_STRING}",
        "ContainerName": "prima-specimens",
        "Prefix": ""
      },
      "ScannedDocument": {
        "Type": "File",
        "Path": "\\\\server\\share\\Documents"
      }
    }
  }
}

Each image type can be configured as File, S3, or AzureBlob:

File Target
{ "Type": "File", "Path": "C:\\path\\to\\storage" }
S3 Target
{ "Type": "S3", "CredentialProfile": "name", "BucketName": "bucket", "Prefix": "optional/" }
Azure Blob Target
{ "Type": "AzureBlob", "ConnectionString": "...", "ContainerName": "container", "Prefix": "optional/" }

Secret Resolution

To avoid storing sensitive credentials in plain text, use secret resolution syntax for the SecretAccessKey field. Prima supports multiple secret providers:

Provider Syntax Example
Environment Variable ${secrets:env:VARIABLE_NAME} ${secrets:env:AWS_SECRET_KEY}
Azure Key Vault ${secrets:azure-keyvault:secret-name} ${secrets:azure-keyvault:prima-s3-secret}
AWS Secrets Manager ${secrets:aws-secrets:secret-name} ${secrets:aws-secrets:prima/storage/credentials}
Plain Text (no prefix) wJalrXUtnFEMI/K7MDENG/bPxRfi...
Configuring Secret Providers

If using Azure Key Vault or AWS Secrets Manager, configure the provider connection:

{
  "SecretProviders": {
    "AzureKeyVault": {
      "VaultUri": "https://my-vault.vault.azure.net/"
    },
    "AwsSecretsManager": {
      "Region": "us-east-1"
    }
  }
}
Security Best Practice: Never commit plain text credentials to source control. Use environment variables or a secrets manager for production deployments.

Complete Configuration Example

Here's a complete example using S3 storage with environment variable secrets:

{
  "StorageCredentialProfiles": [
    {
      "Name": "production-s3",
      "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
      "SecretAccessKey": "${secrets:env:PRIMA_S3_SECRET_KEY}",
      "Region": "us-east-1",
      "Endpoint": null
    }
  ],
  "ImageStorage": {
    "S3": {
      "CredentialProfile": "production-s3",
      "BucketName": "my-prima-images",
      "Prefix": "production/"
    }
  },
  "S3Settings": {
    "TimeoutSeconds": 30,
    "MaxRetryAttempts": 3,
    "RetryDelayMilliseconds": 500
  }
}

S3-Compatible Services

Prima works with any S3-compatible storage service. Set the Endpoint property to the service's S3 API URL:

Service Endpoint Example
Amazon S3 null (uses default AWS endpoints)
Digital Ocean Spaces https://nyc3.digitaloceanspaces.com
MinIO https://minio.example.com:9000
Backblaze B2 https://s3.us-west-000.backblazeb2.com
Tip: After making storage configuration changes, restart the Prima Windows service for changes to take effect.

Web Server (Kestrel)

Configure the ASP.NET Core Kestrel web server that hosts Prima's web interface and API.

Server Settings

Documentation in progress. This section will cover:
  • URL and port binding
  • HTTPS/SSL certificate configuration
  • Request size limits
  • Connection timeouts
  • HTTP/2 and HTTP/3 settings

Database Connection

Configure the connection to your SQL Server database.

Connection Settings

Documentation in progress. This section will cover:
  • Connection string format and options
  • Windows vs SQL authentication
  • Connection pooling settings
  • Command timeouts
  • Entity Framework configuration