Security
Notiway secures the notification pipeline at multiple layers: authentication at the connection boundary, tenant validation at the subscription boundary, and built-in protections like rate limiting, CORS, and HSTS at the transport layer.
Security Layers
flowchart TD
C["Client"]
subgraph Transport["Transport Security"]
HSTS["HSTS"]
CORS["CORS"]
RL["Rate Limiting"]
end
subgraph Connection["Connection Security"]
AUTH["Authentication Plugin"]
end
subgraph Subscription["Subscription Security"]
TV["Tenant Validation Plugin"]
end
C --> Transport --> Connection --> Subscription
Every request passes through all layers in order. A failure at any layer terminates the request — the client never reaches the next layer.
Authentication
Every incoming connection is authenticated before the WebSocket upgrade. The configured Auth plugin inspects the initial HTTP request and either allows or rejects the connection.
On success, the plugin produces an identity containing at least a user ID. This identity determines automatic audience membership (Global, User, and Connection audiences) and is carried for the entire connection lifetime.
If authentication fails, the gateway rejects the connection and the WebSocket upgrade never happens.
Only one Auth plugin is active at a time. See Auth Plugins for available options, client-side setup, and configuration.
Tenant Validation
In multi-tenant deployments, tenant validation controls which tenants a client is allowed to join. When a client calls Subscribe to join a Tenant audience, the Tenant Validation plugin verifies that the authenticated user actually belongs to that tenant.
Without tenant validation, any authenticated client could subscribe to any tenant audience and receive its notifications.
Tenant validation also applies to tenant-scoped groups. A client must first pass validation for a tenant before it can join any group scoped to that tenant.
flowchart TD
C["Client calls Subscribe<br/>(audienceType: 2, audienceValue: 'acme-corp')"]
C --> V{"Tenant Validation Plugin"}
V -->|"Client belongs to tenant"| J["Client joins tenant:acme-corp<br/>Persisted notifications replayed"]
V -->|"Client does NOT belong"| R["Subscribe rejected<br/>Client does not join"]
Only one Tenant Validation plugin is active at a time. See Tenant Validation Plugins for available options and configuration.
Transport Security
HSTS
Notiway enforces HTTP Strict Transport Security with subdomain inclusion and preload enabled. Browsers that have connected once will refuse plaintext HTTP connections for subsequent visits.
CORS
CORS is configurable per deployment:
- Restricted mode — only whitelisted origins can establish connections. Credentials are allowed for authenticated SignalR connections. This is the recommended mode for production.
- Open mode — any origin can connect. Useful for development and testing.
The gateway only accepts GET and POST methods — the minimum required by SignalR’s negotiation and WebSocket upgrade flow.
Rate Limiting
The gateway applies per-IP rate limiting using a fixed window strategy. Each IP address is tracked independently.
Default configuration:
| Setting | Value |
|---|---|
| Window size | 10 seconds |
| Requests per window | 100 per IP |
| Rejection status | 429 Too Many Requests |
When a client exceeds the limit, the gateway rejects further requests with 429 and includes a X-RateLimit-Limit response header. Normal access resumes when the current window expires.
Rate limiting applies to all HTTP requests, including the initial SignalR negotiation and WebSocket upgrade.
Connection Security Flow
The full security flow from initial HTTP request to receiving notifications:
sequenceDiagram
participant Client
participant Gateway as Notiway Gateway
participant Auth as Auth Plugin
participant TV as Tenant Validation Plugin
Client->>Gateway: HTTP request with credentials
Gateway->>Gateway: HSTS / CORS / Rate Limit checks
Gateway->>Auth: Validate credentials
alt Authentication fails
Auth-->>Gateway: Reject
Gateway-->>Client: 401 Unauthorized
else Authentication passes
Auth-->>Gateway: Identity (userId, claims)
Gateway-->>Client: WebSocket upgrade
Note over Gateway,Client: Auto-joins Global, User, Connection audiences
end
Client->>Gateway: Subscribe(tenant: "acme-corp")
Gateway->>TV: Validate membership(tenantId, userId, claims)
alt Validation fails
TV-->>Gateway: Unauthorized
Gateway-->>Client: Subscribe rejected
else Validation passes
TV-->>Gateway: Success
Gateway-->>Client: Joined tenant:acme-corp
Note over Gateway,Client: Persisted notifications replayed
end
Security Best Practices
Transport
- Always use HTTPS/WSS in production. SignalR negotiates the transport protocol over the initial HTTP connection
- Configure TLS termination at your load balancer or reverse proxy
- Restrict CORS origins to your application domains. Avoid open mode in production
- Set the
Forwarded/X-Forwarded-Protoheaders if Notiway sits behind a proxy
Authentication
- Never deploy with a development auth plugin (e.g. NoAuth) in production
- Ensure credentials are refreshed on reconnect to avoid expired sessions
- Refer to your chosen Auth Plugin documentation for plugin-specific security recommendations
Tenant Isolation
- Enable tenant validation in any multi-tenant deployment
- Remember that tenant-scoped groups inherit tenant validation. A client cannot join a group within a tenant it hasn’t been validated for
Infrastructure
- Run Notiway in a private network. Only expose the WebSocket endpoint through a load balancer or reverse proxy
- Use the broker’s native access controls (IAM policies for SNS/SQS, ACLs for Redis/RabbitMQ) to restrict who can publish notifications
- Pass secrets through environment variables or a secret manager. Never hardcode credentials in configuration files