Prima server configuration is managed through the appsettings.json file located in the Prima installation directory
(typically C:\Program Files\FortelineaSoftwareSystems\Prima\Services\PrimaWebService).
The configuration uses the standard ASP.NET Core configuration system. Settings can be overridden using:
appsettings.json - Base configurationUse the tabs on the left to learn about configuring each section:
Prima supports three authentication methods. The authentication method is determined automatically based on which configuration sections are present and fully configured.
| 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 |
When multiple authentication methods are configured, Prima uses this priority order:
OktaConfiguration is fully configuredLdapConfiguration has AuthenticationType: "Ldap" and is fully configuredJWT 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) |
Secret value must be changed from "REPLACEME" before deploying to production.
Use a strong, random string of at least 32 characters.
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
}
}
| 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) |
| 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") |
Host is not specified, Prima attempts to auto-discover
the domain controller using Domain.GetComputerDomain().
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
}
}
| 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 |
| 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 |
appsettings.jsonhttps://{Domain}/oauth2/{AuthorizationServerId}
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" |
"Secure": "None" to allow cookies over the internal HTTP connection.
{
"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
}
}
{
"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
}
}
{
"LdapConfiguration": {
"AuthenticationType": "Prima"
},
"JwtTokenSettings": {
"Secret": "your-production-secret-key-at-least-32-chars",
"RefreshTokenTTL": 2
}
}
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).
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 |
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 cassettesSlideImages/ - Whole slide imagesSpecimenImages/ - Specimen gross photosDocuments/ - Scanned documents and attachments\\\\server\\share\\PrimaStorage (note the escaped backslashes in JSON).
Store all images in an Amazon S3 bucket or S3-compatible service (Digital Ocean Spaces, MinIO, etc.).
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) |
{
"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) |
Configure retry behavior and timeouts for S3 and Azure Blob operations:
{
"S3Settings": {
"TimeoutSeconds": 30,
"MaxRetryAttempts": 3,
"RetryDelayMilliseconds": 500
}
}
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) |
DefaultEndpointsProtocol=https;AccountName=yourAccount;AccountKey=yourKey;EndpointSuffix=core.windows.net
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:
{ "Type": "File", "Path": "C:\\path\\to\\storage" }
{ "Type": "S3", "CredentialProfile": "name", "BucketName": "bucket", "Prefix": "optional/" }
{ "Type": "AzureBlob", "ConnectionString": "...", "ContainerName": "container", "Prefix": "optional/" }
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... |
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"
}
}
}
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
}
}
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 |
Configure the ASP.NET Core Kestrel web server that hosts Prima's web interface and API.
Configure the connection to your SQL Server database.