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 |
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.
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.
OktaConfiguration section of appsettings.json as shown above.
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.
https://{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.