On this page

Security

Security in Portway is layered: tokens decide who gets in, scopes and environments decide what they can reach, and network rules decide where requests may go. This page walks through each layer in turn, from authentication down to a pre-deployment checklist you can run before going live.

Note

This page must be read as a sensible starting point; rather than a policy to follow. It is worth aligning them with your organisation's security policies before exposing Portway to production traffic.

Authentication

All API requests require a Bearer token:

http
Authorization: Bearer your-token-here

Tokens are generated using cryptographically secure random values and stored encrypted on disk. Each token is bound to a username for audit trail purposes.

First-run token

On first run, Portway generates an initial token and writes it to tokens/YOUR_SERVER_NAME.txt:

json
{
  "Username": "SERVER-NAME",
  "Token": "base64-encoded-secure-token",
  "AllowedScopes": "*",
  "AllowedEnvironments": "*",
  "ExpiresAt": "Never",
  "CreatedAt": "2024-01-01 00:00:00"
}

Caution

This file is highly sensitive: it carries a token with full scope and environment access. Remove it from disk immediately after recording the token somewhere secure. Use the Web UI to manage all subsequent tokens.

Authorization

Scope control

Restrict a token to specific endpoints using the AllowedScopes field:

Pattern Access
* All endpoints
Products,Orders Named endpoints only
Product* All endpoints matching the prefix
Company/Employees Specific namespaced endpoint
Company/* All endpoints in a namespace
GET:Products Single endpoint, single method

Environment control

Restrict a token to specific environments using AllowedEnvironments:

Pattern Access
* All environments
prod Single environment
dev,test Named environments
dev* All environments matching the prefix

Endpoint-level restrictions

Individual endpoints enforce their own environment and visibility constraints:

json
{
  "DatabaseObjectName": "SensitiveData",
  "AllowedEnvironments": ["prod"],
  "IsPrivate": true,
  "AllowedMethods": ["GET"]
}

Both token-level and endpoint-level restrictions must pass for a request to succeed. See Environments, access control for the full matrix.

Network security

IP restrictions

Configure allowed hosts and blocked IP ranges in environments/network-access-policy.json:

json
{
  "allowedHosts": [
    "localhost",
    "127.0.0.1",
    "your-internal-server.local"
  ],
  "blockedIpRanges": [
    "10.0.0.0/8",
    "172.16.0.0/12",
    "192.168.0.0/16"
  ]
}

Security headers

Portway adds these headers to all responses automatically:

Header Value
X-Content-Type-Options nosniff
X-Frame-Options DENY
Strict-Transport-Security max-age=31536000
Referrer-Policy strict-origin-when-cross-origin
Content-Security-Policy default-src 'self'; object-src 'none'; ...

Secrets management

Automatic encryption

Portway encrypts plaintext secrets in settings.json files on next startup. Connection strings and authentication values written in plaintext become PWENC:... format. The original value is no longer stored.

Automatic encryption applies only to per-environment settings.json files and the MCP configuration store (mcp.db). It does not rewrite appsettings.json, so values placed there (such as WebUi:AdminApiKey) stay in plaintext.

Web UI admin key

Never store a real admin key in appsettings.json. The shipped file intentionally contains the placeholder INSECURE-CHANGE-ME-admin-api-key, which Portway rejects in production: Web UI authentication is disabled and an error is logged until a real key is provided.

Supply the key through the environment instead:

yaml
# docker-compose.yml
environment:
  - WebUi__AdminApiKey=${PORTWAY_ADMIN_KEY}
powershell
[Environment]::SetEnvironmentVariable("WebUi__AdminApiKey", "<your-key>", "Machine")

Generate a strong key (32+ characters; shorter keys log a warning at startup):

bash
openssl rand -base64 48
powershell
[Convert]::ToBase64String((1..48 | ForEach-Object { Get-Random -Maximum 256 }))

With Azure Key Vault configured (KEYVAULT_URI), the key can also be served from the vault through the standard ASP.NET Core configuration pipeline. Environment variables and Key Vault values override anything in appsettings.json.

The Settings page in the Web UI shows the current key strength (not set / placeholder / weak / strong) under Security Posture.

Azure Key Vault

Store connection strings and server names in Azure Key Vault instead of settings.json:

powershell
$env:KEYVAULT_URI = "https://your-keyvault.vault.azure.net/"
bash
export KEYVAULT_URI="https://your-keyvault.vault.azure.net/"

Create secrets named by environment:

  • {environment}-ConnectionString
  • {environment}-ServerName
  • {environment}-Headers (JSON string)

Portway fetches these at startup and uses them identically to file-based configuration.

SQL Server permissions

When using Windows Authentication (NTLM) via IIS, configure the database account with minimum required permissions:

sql
USE [master];
GO
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'DOMAIN\USER_NAME')
BEGIN
    EXEC ('CREATE LOGIN [DOMAIN\USER_NAME] FROM WINDOWS;');
END
GO
USE [YourDatabase];
GO
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'DOMAIN\USER_NAME')
BEGIN
    CREATE USER [DOMAIN\USER_NAME] FOR LOGIN [DOMAIN\USER_NAME];
END
GO
ALTER ROLE [db_datareader] ADD MEMBER [DOMAIN\USER_NAME];  -- Read tables/views
ALTER ROLE [db_datawriter] ADD MEMBER [DOMAIN\USER_NAME];  -- Write (insert/update/delete)
GRANT EXECUTE TO [DOMAIN\USER_NAME];                       -- Stored procedures
GRANT VIEW DEFINITION TO [DOMAIN\USER_NAME];               -- Schema metadata
GO

Grant only the roles your endpoints require. A read-only deployment needs only db_datareader.

Logging and auditing

Security events are logged at Warning or Debug level:

txt
[DBG] Invalid token: {masked}
[WRN] IP {IP} has exceeded rate limit, blocking for {period}

Enable request traffic logging to capture headers and bodies for security analysis:

json
{
  "RequestTrafficLogging": {
    "Enabled": true,
    "CaptureHeaders": true,
    "IncludeRequestBodies": true
  }
}

See Monitoring for traffic logging configuration details.

Pre-deployment checklist

  • HTTPS binding configured in IIS
  • IIS Application Pool using minimum-privilege identity
  • WebUi__AdminApiKey set via environment variable or Key Vault (32+ random characters, never in appsettings.json)
  • Azure Key Vault configured (if applicable)
  • Initial token file removed from disk
  • Tokens created with specific scopes and environments
  • Rate limiting configured
  • Firewall rules reviewed
  • Security headers verified with a response inspection tool

Incident response: compromised token

  1. Open the Web UI and navigate to Access Tokens
  2. Click Rotate Token on the affected entry to invalidate it and generate a replacement
  3. Update all applications using the compromised token with the new value
  4. Enable traffic logging if not already active to monitor for continued unauthorized activity:
    json
    {
      "RequestTrafficLogging": { "Enabled": true, "CaptureHeaders": true }
    }
  5. Document the incident for your security audit trail

INFO

Manage all tokens in the Web UI under Tokens. The Tokens page lists all active (non-revoked, non-expired) tokens with their scope and environment restrictions.

WARNING

Token revocation is permanent. A revoked token cannot be reactivated. Create a new token for any affected user or system.

Next steps

Last updated: 2026-07-23