Adjusted internal documentation links and updated references within various Markdown files to reflect a new directory structure for audit and meta documents. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 4dee684a-a4b9-4c32-a818-3a27dae1b027 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 971b8c75-c322-4ede-98ea-eb5ae31d4e5c Replit-Helium-Checkpoint-Created: true
18 KiB
Authorization and Security Requirements Specification
Document Version: 1.0
Last Updated: January 2026
Status: Draft
Related Documents: Security Audit, API Layer Audit
1. Purpose
This document formally specifies the authentication and authorization requirements for Nightscout's API system. It serves as a contract for:
- Client developers - Understanding how to authenticate with Nightscout
- Maintainers - Preserving security behavior during refactoring
- Testers - Validating security behavior against formal requirements
- Security auditors - Understanding the expected security posture
2. Terminology
| Term | Definition |
|---|---|
| API_SECRET | A shared secret (minimum 12 characters) used for admin-level authentication |
| API_SECRET Hash | The SHA-1 or SHA-512 digest of the API_SECRET, transmitted instead of the raw secret |
| Access Token | A subject-specific token derived from the subject name and a digest of the subject ID |
| JWT | JSON Web Token signed with a dedicated JWT signing key for time-limited authentication |
| JWT Signing Key | A separate key (loaded from randomString file or set via setJWTKey) used to sign/verify JWTs |
| Subject | An entity (user, device, application) that can authenticate |
| Role | A named collection of permissions that can be assigned to subjects |
| Permission | An Apache Shiro-style string defining allowed actions (e.g., api:entries:read) |
| Shiro Trie | A data structure for efficient wildcard permission matching |
3. Authentication Requirements
3.1 API_SECRET Authentication
REQ-AUTH-001: API_SECRET Minimum Length
The API_SECRET environment variable MUST be at least 12 characters long.
| Input | Expected Behavior | Requirement ID |
|---|---|---|
API_SECRET ≥ 12 chars |
Server starts, secret is valid | REQ-AUTH-001a |
API_SECRET < 12 chars |
Server logs error, secret is null | REQ-AUTH-001b |
API_SECRET not set |
Server runs with limited functionality | REQ-AUTH-001c |
Implementation Reference: lib/server/env.js
if (readENV('API_SECRET').length < consts.MIN_PASSPHRASE_LENGTH) {
env.err.push({desc: 'API_SECRET should be at least ' + consts.MIN_PASSPHRASE_LENGTH + ' characters'});
}
REQ-AUTH-002: API_SECRET Transmission
The API_SECRET MUST be transmitted as a hash (SHA-1 or SHA-512), never in plaintext.
| Method | Header/Parameter | Format | Requirement ID |
|---|---|---|---|
| Header | api-secret |
SHA-1 (40 chars) or SHA-512 (128 chars) hex string | REQ-AUTH-002a |
| Query | ?secret= |
SHA-1 (40 chars) or SHA-512 (128 chars) hex string | REQ-AUTH-002b |
| Body | body.secret |
SHA-1 (40 chars) or SHA-512 (128 chars) hex string | REQ-AUTH-002c |
Example (SHA-1):
API_SECRET: "this is my long pass phrase"
SHA-1 Hash: "b723e97aa97846eb92d5264f084b2823f57c4aa1"
Note: Either SHA-1 or SHA-512 hashes are accepted per REQ-AUTH-003.
REQ-AUTH-003: API_SECRET Hash Algorithms
The system MUST accept both SHA-1 and SHA-512 hashes of the API_SECRET.
| Algorithm | Hash Length | Requirement ID |
|---|---|---|
| SHA-1 | 40 hex chars | REQ-AUTH-003a |
| SHA-512 | 128 hex chars | REQ-AUTH-003b |
Rationale: SHA-512 support provides a migration path to stronger hashing.
REQ-AUTH-004: API_SECRET Authorization Level
A valid API_SECRET grants full admin permissions (*).
Shiro Permission: * (all permissions)
3.2 Access Token Authentication
REQ-AUTH-010: Access Token Format
Access tokens MUST be derived from the subject name and a digest computed by the enclave.
Format: {abbreviation}-{digest_prefix}
| Component | Description | Requirement ID |
|---|---|---|
| Abbreviation | First 10 alphanumeric chars of subject name, lowercase | REQ-AUTH-010a |
| Digest Prefix | First 16 chars of digest from enclave.getSubjectHash(subject._id) |
REQ-AUTH-010b |
Implementation Detail: The digest is computed as SHA-1 of apiKeySHA1 + subject._id, where apiKeySHA1 is the SHA-1 hash of the API_SECRET. This double-hashing provides an additional layer of indirection.
Reference: lib/server/enclave.js:getSubjectHash()
Example:
Subject Name: "Loop App"
Access Token: "loopapp-a1b2c3d4e5f6g7h8"
REQ-AUTH-011: Access Token Locations
Access tokens MAY be provided in the following locations:
| Location | Priority | Requirement ID |
|---|---|---|
api-secret header |
1 (checked if not API_SECRET hash) | REQ-AUTH-011a |
?token= query parameter |
2 | REQ-AUTH-011b |
body.token |
3 | REQ-AUTH-011c |
REQ-AUTH-012: Access Token Resolution
When a valid access token is provided, the system MUST:
- Locate the corresponding subject
- Retrieve the subject's roles
- Merge subject roles with default roles
- Return combined permissions
3.3 JWT Authentication
REQ-AUTH-020: JWT Generation
The system MUST generate JWTs when a valid access token is provided to the authorization endpoint.
| Endpoint | Method | Input | Output | Requirement ID |
|---|---|---|---|---|
/api/v2/authorization/request/{token} |
GET | Access token | JWT | REQ-AUTH-020a |
JWT Payload:
{
"accessToken": "subject-access-token",
"iat": 1705000000,
"exp": 1705003600
}
REQ-AUTH-021: JWT Signature
JWTs MUST be signed using HMAC-SHA256 with a dedicated JWT signing key.
Implementation Detail: The signing key is stored in secrets[jwtKey] and is loaded from a randomString file in the cache directory, or can be set via env.enclave.setJWTKey(). This is separate from the API_SECRET.
Reference: lib/server/enclave.js:signJWT(), lib/server/enclave.js:readKey()
REQ-AUTH-022: JWT Expiration
JWTs MUST have an expiration time. Default: 8 hours.
Implementation Detail: The default lifetime is '8h' as defined in enclave.signJWT(). This can be overridden by passing a custom lifetime parameter.
Reference: lib/server/enclave.js:58
REQ-AUTH-023: JWT Validation
When a JWT is provided, the system MUST:
- Verify the signature using the JWT signing key (same key used in REQ-AUTH-021)
- Check expiration time
- Extract the access token from payload
- Resolve permissions via access token
Reference: lib/server/enclave.js:verifyJWT()
REQ-AUTH-024: JWT Transmission
JWTs MUST be transmitted via the Authorization header.
Format: Authorization: Bearer {jwt}
| Input | Expected Behavior | Requirement ID |
|---|---|---|
| Valid JWT | Extract access token, resolve permissions | REQ-AUTH-024a |
| Expired JWT | Return 401 Unauthorized | REQ-AUTH-024b |
| Invalid signature | Return 401 Unauthorized | REQ-AUTH-024c |
| Malformed JWT | Return 401 Unauthorized | REQ-AUTH-024d |
4. Authorization Requirements
4.1 Role-Based Access Control
REQ-AUTHZ-001: Default Roles
The system MUST provide the following built-in roles:
| Role Name | Permissions | Description | Requirement ID |
|---|---|---|---|
admin |
* |
Full access | REQ-AUTHZ-001a |
denied |
(none) | No permissions | REQ-AUTHZ-001b |
status-only |
api:status:read |
Read status only | REQ-AUTHZ-001c |
readable |
*:*:read |
Read all data | REQ-AUTHZ-001d |
careportal |
api:treatments:create |
Create treatments | REQ-AUTHZ-001e |
devicestatus-upload |
api:devicestatus:create |
Upload device status | REQ-AUTHZ-001f |
activity |
api:activity:create |
Create activity records | REQ-AUTHZ-001g |
REQ-AUTHZ-002: Custom Roles
Administrators MUST be able to create custom roles with arbitrary permission sets.
Storage: MongoDB collection auth_roles
REQ-AUTHZ-003: Default Permissions
Unauthenticated requests MUST receive permissions based on AUTH_DEFAULT_ROLES environment variable.
| Setting | Effect | Requirement ID |
|---|---|---|
readable |
Unauthenticated can read all data | REQ-AUTHZ-003a |
denied |
Unauthenticated have no permissions | REQ-AUTHZ-003b |
| Comma-separated roles | Merge permissions from listed roles | REQ-AUTHZ-003c |
4.2 Shiro Permission Model
REQ-AUTHZ-010: Permission Format
Permissions MUST follow the Apache Shiro format: domain:action:instance
Examples:
api:entries:read - Read entries via API
api:treatments:create - Create treatments
api:*:* - All API operations
* - Full admin access
REQ-AUTHZ-011: Wildcard Matching
The permission system MUST support wildcard matching at any level.
| Pattern | Matches | Requirement ID |
|---|---|---|
* |
All permissions | REQ-AUTHZ-011a |
api:*:* |
All API operations | REQ-AUTHZ-011b |
api:entries:* |
All entry operations | REQ-AUTHZ-011c |
*:*:read |
All read operations | REQ-AUTHZ-011d |
REQ-AUTHZ-012: Permission Checking
Permission checks MUST use the Shiro Trie data structure for efficient wildcard matching.
5. Brute-Force Protection Requirements
5.1 IP-Based Delay List
REQ-BRUTE-001: Failed Authentication Tracking
The system MUST track failed authentication attempts by IP address.
Implementation Reference: lib/authorization/delaylist.js
REQ-BRUTE-002: Progressive Delay
After a failed authentication attempt, subsequent requests from the same IP MUST be delayed.
| Parameter | Default Value | Configurable | Requirement ID |
|---|---|---|---|
| Delay per failure | 5000ms | Yes (settings.authFailDelay) |
REQ-BRUTE-002a |
| Delay accumulation | Cumulative | No | REQ-BRUTE-002b |
| Max delay | No limit | No | REQ-BRUTE-002c |
Behavior:
1st failure: 5 second delay
2nd failure: 10 second delay (cumulative)
3rd failure: 15 second delay (cumulative)
...
REQ-BRUTE-003: Delay Expiration
Failed request entries SHOULD be cleaned up after a period of inactivity.
| Parameter | Value | Requirement ID |
|---|---|---|
| Expiration age | 60 seconds after last delay (FAIL_AGE) |
REQ-BRUTE-003a |
| Cleanup mechanism | One-shot setTimeout after 30 seconds | REQ-BRUTE-003b |
Implementation Note: The current implementation uses a single setTimeout(30000) call at module initialization to clean up entries older than FAIL_AGE (60 seconds). This is a one-shot cleanup, not a recurring interval. Entries created after the cleanup runs may persist until server restart. This is a known limitation.
Reference: lib/authorization/delaylist.js:45-53
REQ-BRUTE-004: Successful Authentication Clears Delay
A successful authentication MUST immediately clear the delay for that IP.
REQ-BRUTE-005: Failed Authentication Notification
Failed authentication attempts MUST trigger an admin notification.
Notification Content:
- Title: "Failed authentication"
- Message: IP address and warning about potential misconfiguration
6. Subject Management Requirements
6.1 Subject CRUD Operations
REQ-SUBJ-001: Subject Creation
Subjects MUST be creatable via the admin API.
Required Fields:
| Field | Type | Description | Requirement ID |
|---|---|---|---|
name |
String | Display name for the subject | REQ-SUBJ-001a |
roles |
Array | List of role names assigned | REQ-SUBJ-001b |
Auto-generated Fields:
| Field | Type | Description |
|---|---|---|
_id |
ObjectID | Unique identifier |
created_at |
ISO 8601 | Creation timestamp |
accessToken |
String | Generated access token |
digest |
String | Token digest for matching |
REQ-SUBJ-002: Subject Modification
Subjects MUST be modifiable via the admin API.
REQ-SUBJ-003: Subject Deletion
Subjects MUST be deletable via the admin API.
Behavior: Once a subject is deleted, its access token becomes unusable because the subject lookup will fail. There is no explicit token revocation mechanism; invalidation occurs because the subject record no longer exists in the database.
Note: Existing JWTs containing the deleted subject's access token will fail on the next permission resolution when the subject cannot be found.
6.2 Role Management
REQ-ROLE-001: Role Creation
Custom roles MUST be creatable via the admin API.
Required Fields:
| Field | Type | Description |
|---|---|---|
name |
String | Unique role name |
permissions |
Array | List of Shiro-format permissions |
REQ-ROLE-002: Built-in Role Behavior
Built-in roles (admin, denied, readable, etc.) are defined in code and merged with database roles at runtime.
Note: The current implementation does not explicitly protect built-in roles from deletion attempts via the admin API. Deleting a built-in role from the database has no effect since the role is re-added from storage.defaultRoles on reload. This behavior is implementation-specific and may change.
7. Socket.IO Authentication Requirements
7.1 Storage Namespace
REQ-SOCK-001: Subscription Authentication
The /storage WebSocket namespace MUST require authentication for data subscriptions.
Message Format:
socket.emit('subscribe', {
accessToken: 'subject-access-token',
collections: ['entries', 'treatments']
});
REQ-SOCK-002: Per-Collection Authorization
Access to collections via WebSocket MUST respect the subject's permissions.
| Permission | Required For |
|---|---|
api:entries:read |
Subscribe to entries |
api:treatments:read |
Subscribe to treatments |
api:treatments:create |
dbAdd to treatments |
7.2 Alarm Namespace
REQ-SOCK-010: Alarm Subscription
The /alarm namespace MUST require a valid access token for subscription.
8. Error Handling Requirements
8.1 Authentication Errors
REQ-ERR-001: Unauthorized Response
Invalid authentication MUST return HTTP 401 Unauthorized.
Response Format:
{
"status": 401,
"message": "Unauthorized"
}
REQ-ERR-002: Forbidden Response
Valid authentication with insufficient permissions MUST return HTTP 403 Forbidden.
9. Traceability Matrix
| Requirement | Test File | Test Case | Status |
|---|---|---|---|
| REQ-AUTH-001a | security.test.js |
"should work fine set" | Covered |
| REQ-AUTH-001b | security.test.js |
"should not work short" | Covered |
| REQ-AUTH-002a | security.test.js |
"should work fine set" | Covered |
| REQ-AUTH-003a | verifyauth.test.js |
SHA-1 verification | Covered |
| REQ-AUTH-003b | verifyauth.test.js |
SHA-512 verification | Covered |
| REQ-AUTH-011a | api.security.test.js |
"Data load should succeed with token in place of a secret" | Covered |
| REQ-AUTH-011b | api.security.test.js |
"Data load should succeed with GET token" | Covered |
| REQ-AUTH-020a | api.security.test.js |
"Should return a JWT on token" | Covered |
| REQ-AUTH-024a | api.security.test.js |
"Data load should succeed with a bearer token" | Covered |
| REQ-AUTH-024c | api.security.test.js |
"Data load fail succeed with a false bearer token" | Covered |
| REQ-AUTHZ-003b | api.security.test.js |
"Data load should fail unauthenticated" | Covered |
| REQ-BRUTE-002 | verifyauth.test.js |
"should fail unauthorized and delay subsequent attempts" | Covered |
| REQ-BRUTE-004 | Implicit in verifyauth.test.js |
Successful auth clears delay | Implicit |
| REQ-SOCK-001 | N/A | WebSocket subscription auth | Not Covered |
| REQ-SOCK-002 | N/A | Per-collection authorization | Not Covered |
| REQ-SUBJ-001 | N/A | Subject creation | Not Covered |
| REQ-ROLE-001 | N/A | Role creation | Not Covered |
10. Coverage Gaps and Recommendations
10.1 Identified Gaps
| Gap | Priority | Recommendation |
|---|---|---|
| WebSocket authentication testing | High | Add tests for /storage and /alarm subscription auth |
| Subject/Role CRUD testing | Medium | Add API tests for admin tools endpoints |
| JWT expiration testing | Medium | Add test for expired JWT rejection |
| Permission wildcard testing | Low | Add comprehensive Shiro pattern tests |
10.2 Future Enhancements
| Enhancement | Description | Priority |
|---|---|---|
| Token expiration | Add expiration to access tokens | Medium |
| Refresh tokens | Add JWT refresh mechanism | Low |
| Audit logging | Log all auth events for compliance | Medium |
| OIDC Actor Identity | External identity provider integration with verified actor tracking | High |
OIDC Actor Identity Proposal
A comprehensive RFC has been created for integrating OpenID Connect and OAuth 2.0 identity management into Nightscout Core. This enables:
- Verified actor tracking - Replace freeform
enteredBywith cryptographically-verified identities - Care coordination - Know exactly who performed each action (Mom, Dad, school nurse)
- Delegation support - Track when actions are performed on behalf of others
- Audit trails - HIPAA-grade compliance for clinical settings
- Automation safety - Distinguish human decisions from automated actions (Loop, OpenAPS)
See OIDC Actor Identity Proposal for:
- Full architecture and protocol flows
- JWT claims specification
- Actor lookup collection schema
- Migration path for
enteredBy - Test plan and implementation readiness
11. Version History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | January 2026 | Nightscout Team | Initial specification |
12. References
- Security Audit - Security analysis and recommendations
- API Layer Audit - API endpoint inventory
- Modernization Roadmap - OIDC/OAuth2 plans
lib/authorization/- Implementation source code