NBS Unified Platform — App User API Reference

Audience: App Users — including App Owner, App Manager, App Member, and M2M (Machine-to-Machine) clients

Base URL: https://{api-gateway-domain}/{stage}

Authentication: All endpoints require a valid Bearer token in the Authorization header (except public auth endpoints).

User ID Format: User IDs are Amazon aliases (e.g., johndoe, janesmith), NOT email addresses.


Table of Contents

  1. Role Overview
  2. Authentication (Auth)
  3. Human User Authentication (OAuth 2.0 PKCE)
  4. M2M Authentication (Cognito Client Credentials)
  5. App Discovery
  6. File Operations
  7. Delegation Management
  8. User Search
  9. External Audit Logging
  10. Error Handling
  11. Permission & Access Control Reference
  12. Environment Configuration

Note on Login Tracking: Login events are automatically recorded during the token exchange flow (POST /auth/exchange-token). There is no need to call a separate login tracking API.


1. Role Overview

The NBS platform defines three app-level roles with different permission scopes:

Role Description Key Capabilities
App Owner Full data access across the entire app Access all directories (.public, .private, and all user directories), create delegations for others, batch copy files, view all delegations
App Manager Mid-level access with subordinate visibility Access own directory + subordinates' directories + .public, file operations within scope
App Member Standard user with personal workspace Access own directory + .public (read-only), file operations in own directory
M2M Client Machine-to-machine service account Authenticated via Cognito client credentials, can call external audit API and other authorized endpoints

Note: App and user management (adding/removing users, updating roles, etc.) is performed by the App Admin role, which is documented in the Platform Admin API Reference. The App Owner role described here focuses on data access privileges within the app.

Effective Role via Delegation

Users may have an effective role higher than their native role through active delegations: - A member with a FULL delegation from an owner gets effectiveRole: "owner" - A member with a READ_ONLY delegation can read (but not write) the grantor's directories - The effectiveRole is returned in token exchange/verify responses


2. Authentication (Auth)

2.1 Human User Authentication (OAuth 2.0 PKCE)

These endpoints handle the OAuth 2.0 PKCE flow for human users. They are public (no Bearer token required).

Note: The GET /auth/config endpoint has been removed. OAuth configuration (client_id, authorization_endpoint, scope) is now embedded in config.json deployed per environment to S3. See Environment Configuration for staging and production values.


2.1.2 Exchange Authorization Code for Tokens

Exchange an OAuth authorization code for access, ID, and refresh tokens. This is the primary login endpoint.

POST /auth/exchange-token

Request Body:

{
  "code": "authorization_code_from_redirect",
  "code_verifier": "pkce_code_verifier_string",
  "redirect_uri": "https://your-app.example.com/callback",
  "app_id": "my-app",
  "app_name": "My Application"
}
Field Type Required Description
code string OAuth authorization code from redirect
code_verifier string PKCE code verifier (generated during authorization)
redirect_uri string Must exactly match the redirect URI used in authorization request
app_id string Application ID to check permissions against
app_name string Display name (used for login tracking)

Response 200 OK:

{
  "data": {
    "access_token": "eyJhbGciOi...",
    "id_token": "eyJhbGciOi...",
    "refresh_token": "eyJhbGciOi...",
    "token_type": "Bearer",
    "expires_in": 3600,
    "expires_at": "2026-03-21T09:00:00.000Z",
    "user_info": {
      "sub": "johndoe",
      "email": "johndoe@amazon.com",
      "fullname": "John Doe",
      "department": "NBS",
      "manager": "Jane Smith",
      "appId": "my-app",
      "userRole": "member",
      "effectiveRole": "owner",
      "activeDelegations": [
        {
          "grantorId": "janesmith",
          "grantorRole": "owner",
          "delegationType": "FULL",
          "delegationId": "uuid-..."
        }
      ]
    }
  }
}

Key Fields in user_info: - sub — User's Amazon alias (e.g., johndoe) - userRole — Native role in the app (owner, manager, member, or null for platform apps) - effectiveRole — Highest role considering active delegations - activeDelegations — List of active delegations elevating the user's permissions

Access Mode Behavior: - Whitelist apps: User must have explicit permission → 403 if not authorized - Public apps: All authenticated users get access with default role member - Platform apps (app-lobby, app-store): Skip permission check, userRole: null

Error Responses: - 400 — Missing required parameters - 401 — Invalid or expired authorization code (INVALID_CODE) - 403 — User not authorized for whitelist app (ACCESS_DENIED)

Note: Login events are automatically tracked during token exchange.


2.1.3 Refresh Access Token

Refresh an expired access token using a refresh token.

POST /auth/refresh-token

Headers:

Authorization: Bearer <refresh_token>

Request Body:

{
  "app_id": "my-app"
}
Field Type Required Description
app_id string Application ID for role re-evaluation

Response 200 OK:

{
  "data": {
    "access_token": "eyJhbGciOi...",
    "id_token": null,
    "refresh_token": "eyJhbGciOi...",
    "token_type": "Bearer",
    "expires_in": 3600,
    "expires_at": "2026-03-21T09:00:00.000Z",
    "user_info": {
      "sub": "johndoe",
      "fullname": "John Doe"
    }
  }
}

Important: Amazon Federate does NOT return id_token on refresh — only access_token + refresh_token. The user_info in the refresh response contains only sub and fullname (looked up from the organization table). The frontend should use access_token for Bearer auth (OAuth 2.0 best practice).

Error Responses: - 400 — Missing parameters or token decryption error - 401 — Invalid or expired refresh token (INVALID_REFRESH_TOKEN) - 403 — User no longer authorized for the app


2.1.4 Verify Token (Introspection)

Verify a token and return trusted identity context from the database. Use this to validate session state.

POST /auth/verify-token

Headers:

Authorization: Bearer <access_token>

Request Body:

{
  "app_id": "my-app"
}

Response 200 OK:

{
  "data": {
    "valid": true,
    "user_info": {
      "sub": "johndoe",
      "appId": "my-app",
      "userRole": "member",
      "effectiveRole": "owner",
      "activeDelegations": [...],
      "fullname": "John Doe"
    }
  }
}

Note: The user_info in verify-token response contains sub, fullname, appId, userRole, effectiveRole, and activeDelegations. Identity claims like email, department, and manager are only available in the initial exchange-token response.

Error Responses: - 401 TOKEN_EXPIRED — Token has expired (client should call refresh-token) - 401 INVALID_TOKEN — Token signature invalid or tampered (re-login required) - 403 ACCESS_DENIED — User not in app whitelist (no retry) - 500 SYSTEM_ERROR — Database/KMS failure


Login Tracking: Login events are automatically recorded during the POST /auth/exchange-token flow. There is no need for app users to call a separate login tracking API.


2.2 M2M Authentication (Cognito Client Credentials)

M2M (Machine-to-Machine) clients authenticate using AWS Cognito Client Credentials flow.

How M2M Authentication Works

  1. Obtain a Cognito access token using your client_id and client_secret via the Cognito token endpoint
  2. Include the token as Authorization: Bearer <cognito_access_token>
  3. Add ?auth_source=cognito_m2m query parameter to all API calls
  4. The client_id from the Cognito token is used as the user's sub (identity)

M2M Request Format

POST /apps/{appId}/audit/log?auth_source=cognito_m2m
Authorization: Bearer <cognito_access_token>
Content-Type: application/json

{
  "operation": "data_export",
  "status": "success",
  "operationDetails": { "recordCount": 1500 }
}

Important: The auth_source=cognito_m2m query parameter tells the API to verify the token against Cognito JWKS instead of Federate JWKS.

M2M Permissions

M2M clients are identified by their client_id (which becomes their sub). To grant an M2M client access to an app: 1. Register the M2M client's client_id as a user in the app's permissions table 2. Assign appropriate role (owner, manager, or member) 3. The M2M client can then call any API endpoint its role permits


3. App Discovery

3.1 List My Apps

Get all apps accessible by the current authenticated user.

GET /apps

Headers:

Authorization: Bearer <access_token>

Query Parameters:

Parameter Type Default Description
includeDeleted boolean false Include soft-deleted apps

Behavior by Role: - Platform Admin: Returns ALL apps across the platform with userRole: "platform_admin" - Regular User: Returns whitelist apps (where user has explicit permission) + all public apps

Response 200 OK:

{
  "data": {
    "apps": [
      {
        "appId": "my-app",
        "appName": "My Application",
        "description": "A data insights tool",
        "status": "active",
        "accessMode": "whitelist",
        "category": "data-insights",
        "userRole": "owner",
        "userCount": 15,
        "adminCount": 2,
        "entryUrl": "https://my-app.example.com",
        "iconUrl": "https://presigned-url...",
        "createdAt": "2026-01-15T10:00:00.000Z"
      },
      {
        "appId": "public-dashboard",
        "appName": "Public Dashboard",
        "status": "active",
        "accessMode": "public",
        "userRole": "member",
        "userCount": 0,
        "adminCount": 1
      }
    ],
    "role": "app_user",
    "totalApps": 2
  }
}

Note: For platform admins, role is "platform_admin" and all apps are returned.


4. File Operations

All file operation endpoints require app-level permissions and are scoped to {appId}.

Directory Structure

Each app has the following directory structure in S3:

{appId}/
├── .private/          ← Owner-only access (read/write)
├── .public/           ← All members can read; only owners can write
├── {userId}/          ← Personal workspace for each user
│   ├── subfolder/
│   └── document.pdf
└── {anotherUserId}/

Access Control Summary

Your Role .private .public Own Directory Other User's Directory
Owner Read ✅ Write ✅ Read ✅ Write ✅ Read ✅ Write ✅ Read ✅ Write ✅ (active only)
Manager Read ✅ Read ✅ Write ✅ Subordinates: Read ✅ Write ✅
Member Read ✅ Read ✅ Write ✅

Delegation: If you have an active delegation from another user, you can access their directories based on the delegation type (FULL = read+write, READ_ONLY = read only).


4.1 List Files

List files and directories within an app.

GET /apps/{appId}/files

Required Permission: app:files:list

Query Parameters:

Parameter Type Default Description
directory string Target directory (required for members, optional for owners/managers)
recursive boolean false List all files recursively (owner only)
search string Wildcard pattern for filename matching (e.g., *report*.xlsx)
includeDeleted boolean false Include soft-deleted files (owner only)
continuationToken string Pagination token
maxKeys integer 1000 Maximum number of keys to return

Examples:

# Member: list own files
GET /apps/my-app/files?directory=johndoe

# Member: list files in a subfolder
GET /apps/my-app/files?directory=johndoe/reports

# Member: list public files
GET /apps/my-app/files?directory=.public

# Owner: list all top-level directories
GET /apps/my-app/files

# Owner: recursive search across all directories
GET /apps/my-app/files?recursive=true&search=*quarterly*.xlsx

# Manager: list scoped directories (own + subordinates + .public)
GET /apps/my-app/files

Response 200 OK (with directory specified):

{
  "data": {
    "files": [
      {
        "key": "my-app/johndoe/report.pdf",
        "filename": "report.pdf",
        "size": 1048576,
        "lastModified": "2026-03-20T15:30:00.000Z",
        "owner": "johndoe"
      }
    ],
    "directories": [
      {
        "directory": "reports",
        "prefix": "my-app/johndoe/reports/"
      }
    ],
    "totalFiles": 1,
    "totalDirectories": 1,
    "totalSize": 1048576,
    "prefix": "my-app/johndoe/"
  }
}

Response 200 OK (Manager scoped list, no directory):

{
  "data": {
    "directories": [
      { "directory": "johndoe", "prefix": "my-app/johndoe/" },
      { "directory": "subordinate1", "prefix": "my-app/subordinate1/" },
      { "directory": ".public", "prefix": "my-app/.public/" }
    ],
    "totalDirectories": 3,
    "prefix": "my-app/"
  }
}

Error Responses: - 400 MISSING_DIRECTORY — Directory parameter required for non-owner users - 403 ACCESS_DENIED — No access to the requested directory - 403 PERMISSION_DENIED — Recursive/includeDeleted only for owners


4.2 Upload File (Get Upload URL)

Generate a presigned S3 PUT URL for file upload.

POST /apps/{appId}/files/upload

Required Permission: app:files:upload

Request Body:

{
  "directory": "johndoe",
  "filename": "quarterly-report.pdf",
  "contentType": "application/pdf",
  "expires_in": 900
}
Field Type Required Description
directory string Target directory (your alias, .public, or subdirectory path)
filename string Filename (no slashes allowed)
contentType string MIME type (e.g., application/pdf, image/png)
expires_in integer URL expiration in seconds (default: 900, max: 3600)

Response 200 OK:

{
  "data": {
    "uploadUrl": "https://nbs-data-storage.s3.ap-southeast-2.amazonaws.com/my-app/johndoe/quarterly-report.pdf?X-Amz-...",
    "expiresIn": 900,
    "requiredHeaders": {
      "Content-Type": "application/pdf"
    },
    "s3Key": "my-app/johndoe/quarterly-report.pdf"
  }
}

How to Upload:

# Use the presigned URL with the EXACT Content-Type from requiredHeaders
curl -X PUT \
  -H "Content-Type: application/pdf" \
  --data-binary @quarterly-report.pdf \
  "https://nbs-data-storage.s3.ap-southeast-2.amazonaws.com/my-app/johndoe/quarterly-report.pdf?X-Amz-..."

⚠️ Critical: The Content-Type header in your upload request MUST exactly match the contentType you specified. Mismatched Content-Type will result in a 403 SignatureDoesNotMatch error.

Error Responses: - 400 MISSING_CONTENT_TYPE — contentType is required - 400 INVALID_FILENAME — Filename contains invalid characters - 403 ACCESS_DENIED — No write access to the directory


4.3 Download File (Get Download URL)

Generate a presigned S3 GET URL for file download.

POST /apps/{appId}/files/download

Required Permission: app:files:download

Request Body:

{
  "directory": "johndoe",
  "filename": "quarterly-report.pdf",
  "expires_in": 300
}
Field Type Required Description
directory string Source directory (including subdirectory path if needed)
filename string Filename to download (no slashes)
expires_in integer URL expiration in seconds (default: 300, max: 3600)

Response 200 OK:

{
  "data": {
    "downloadUrl": "https://nbs-data-storage.s3.ap-southeast-2.amazonaws.com/my-app/johndoe/quarterly-report.pdf?X-Amz-...",
    "expiresIn": 300,
    "filename": "quarterly-report.pdf"
  }
}

Error Responses: - 403 ACCESS_DENIED — No read access to the directory - 404 NOT_FOUND — File not found


4.4 Delete File

Delete a file from S3.

DELETE /apps/{appId}/files

Required Permission: app:files:delete

Request Body:

{
  "directory": "johndoe",
  "filename": "old-report.pdf"
}
Field Type Required Description
directory string Directory containing the file
filename string Filename to delete (no slashes)

Response 200 OK:

{
  "data": {
    "message": "File deleted successfully",
    "deletedKey": "my-app/johndoe/old-report.pdf"
  }
}

Error Responses: - 403 ACCESS_DENIED — No write access to the directory


4.5 Copy File (Server-Side)

Copy a single file within the same app using S3 server-side copy.

POST /apps/{appId}/files/copy

Required Permission: app:files:copy

Request Body:

{
  "sourceDirectory": ".public/templates",
  "sourceFilename": "baseline.xlsx",
  "targetDirectory": "johndoe/reports",
  "targetFilename": "my-baseline.xlsx"
}
Field Type Required Description
sourceDirectory string Source directory (can include subdirectory path)
sourceFilename string Source filename (no slashes)
targetDirectory string Target directory (can include subdirectory path)
targetFilename string Target filename (no slashes)

Access Control: - You need read access to the source directory - You need write access to the target directory - Members can copy from .public or own directory to own directory

Response 200 OK:

{
  "data": {
    "sourceKey": "my-app/.public/templates/baseline.xlsx",
    "targetKey": "my-app/johndoe/reports/my-baseline.xlsx",
    "message": "File copied successfully"
  }
}

4.6 Batch Copy Files (Owner Only)

Batch copy all files from one directory to another. Owner only.

POST /apps/{appId}/files/copy-all

Required Permission: app:files:copy + Owner role

Request Body:

{
  "sourceDirectory": "departing-user",
  "targetDirectory": ".private/backup-20260321"
}
Field Type Required Description
sourceDirectory string Source directory to copy from
targetDirectory string Target directory to copy to

Response 200 OK:

{
  "data": {
    "copiedFiles": [
      { "sourceKey": "my-app/departing-user/file1.pdf", "targetKey": "my-app/.private/backup-20260321/file1.pdf" }
    ],
    "failedFiles": [],
    "totalCopied": 42
  }
}

Error Responses: - 403 PERMISSION_DENIED — Only app owners can batch copy


5. Delegation Management

Delegations allow users to temporarily grant their file access permissions to other users.

Delegation Types

Type Permissions Granted Use Case
FULL list, upload, download, delete Vacation coverage, full handover
READ_ONLY list, download Audit access, review-only sharing

How Delegation Works

  1. Grantor (the person sharing access) must have a native role in the app (not delegated)
  2. Delegatee (the person receiving access) must be an active user in the app
  3. Delegations can have an optional expiry date
  4. The delegatee's effectiveRole is elevated to the grantor's role (for FULL delegations)
  5. Transitive delegation is not allowed (you cannot delegate delegated permissions)

5.1 Create Self-Delegation

Delegate your own permissions to another user.

POST /apps/{appId}/delegations/self

Access: Any authenticated app user with a native role

Request Body:

{
  "delegateeId": "colleague",
  "delegationType": "FULL",
  "expiry": "2026-04-01T00:00:00.000Z"
}
Field Type Required Description
delegateeId string Amazon alias of the person receiving the delegation
delegationType string FULL or READ_ONLY
expiry string ISO 8601 expiry timestamp (no expiry if omitted)

Response 201 Created:

{
  "data": {
    "delegationId": "uuid-...",
    "appId": "my-app",
    "grantorId": "johndoe",
    "delegateeId": "colleague",
    "delegationType": "FULL",
    "status": "active",
    "expiry": "2026-04-01T00:00:00.000Z",
    "createdAt": "2026-03-21T08:00:00.000Z",
    "createdBy": "johndoe"
  }
}

Error Responses: - 400 — Missing fields, self-delegation (delegateeId = yourself), invalid type - 403 — No native role, or attempting transitive delegation - 404 — Delegatee not found or inactive in the app - 409 — Duplicate active delegation already exists


5.2 Query My Delegations

Get all delegations where you are either the grantor or delegatee.

GET /apps/{appId}/delegations/mine

Access: Any authenticated app user

Query Parameters:

Parameter Type Default Description
status string active active, revoked, or all

Response 200 OK:

{
  "data": {
    "delegations": [
      {
        "delegationId": "uuid-...",
        "appId": "my-app",
        "grantorId": "johndoe",
        "delegateeId": "colleague",
        "delegationType": "FULL",
        "status": "active",
        "expiry": "2026-04-01T00:00:00.000Z",
        "createdAt": "2026-03-21T08:00:00.000Z",
        "createdBy": "johndoe"
      }
    ]
  }
}

5.3 Revoke Delegation

Revoke an active delegation. Can be done by the grantor, delegatee, or app owner.

DELETE /apps/{appId}/delegations/{delegationId}

Access: Grantor, delegatee, or App Owner

Response 200 OK:

{
  "data": {
    "message": "Delegation revoked successfully",
    "delegationId": "uuid-..."
  }
}

Error Responses: - 400 — Already revoked - 403 — Not authorized to revoke (not grantor, delegatee, or owner) - 404 — Delegation not found


6.1 Search App Users

Search for active users within an app (for delegation target selection, etc.).

GET /apps/{appId}/users/search?query=john

Access: Any authenticated app user

Query Parameters:

Parameter Type Required Description
query string Search prefix for userId or substring for name

Response 200 OK:

{
  "data": {
    "users": [
      {
        "userId": "johndoe",
        "alias": "johndoe",
        "name": "John Doe",
        "email": "johndoe@amazon.com",
        "role": "owner"
      }
    ]
  }
}

Note: Returns up to 20 matching users. Only active users are returned.


6.2 Search Organization Users (App Admin)

Search the organization directory for users to add to an app.

GET /apps/{appId}/org-users/search?query=john

Access: Users with app:users:manage permission (App Admin)

Query Parameters:

Parameter Type Required Description
query string Search prefix for userId or substring for employeeName

Response 200 OK:

{
  "data": {
    "users": [
      { "alias": "johndoe", "name": "John Doe", "status": "active" },
      { "alias": "johnsmith", "name": "John Smith", "status": "active" }
    ]
  }
}

Note: Only non-deleted organization members are returned. Returns up to 20 results.


6.3 Get Organization User Info

Look up a specific user's organization context by exact userId. Returns the user's direct manager and all active direct reportees (including each reportee's own reportee count).

GET /org/users?userId={userId}

Access: Any authenticated user (no admin role required)

Query Parameters:

Parameter Type Required Description
userId string Exact Amazon alias to look up (case-sensitive)

Example:

GET /org/users?userId=johndoe

Response 200 OK:

{
  "userId": "johndoe",
  "alias": "johndoe",
  "name": "John Doe",
  "email": "johndoe@amazon.com",
  "manager": {
    "userId": "janesmith",
    "name": "Jane Smith"
  },
  "reportees": [
    {
      "userId": "alicejones",
      "name": "Alice Jones",
      "reporteeCount": 3
    },
    {
      "userId": "boblee",
      "name": "Bob Lee",
      "reporteeCount": 0
    }
  ],
  "activeReporteeCount": 2
}

Response Fields:

Field Type Description
userId string Amazon alias (same as alias)
alias string Amazon alias
name string Full display name from org table
email string Derived as {userId}@amazon.com
manager object | null Direct manager's userId and name; null if top of hierarchy
reportees array All active direct reports, each with userId, name, and reporteeCount
activeReporteeCount integer Total count of active direct reports
reportees[].reporteeCount integer Number of active direct reports that this reportee themselves manages

Manager Derivation Logic:

The manager is determined from the pre-computed managerId field written at sync time: - If level_N == userId, then level_{N-1} is the manager - If the user appears at level_1 (top of hierarchy), manager is null - If the user does not appear in any level field, level_4 is treated as the manager

Error Responses: - 400 VALIDATION_ERRORuserId parameter is missing or empty - 401 — Missing or invalid Bearer token - 404 NOT_FOUND — User not found in organization table or has status: deleted


7. External Audit Logging

This endpoint allows registered apps (both human users and M2M clients) to write user operation logs to the platform's audit system.

7.1 Log External Audit Event

POST /apps/{appId}/audit/log

Access: Any authenticated user (Federate) or M2M client (Cognito)

For M2M clients, add ?auth_source=cognito_m2m:

POST /apps/{appId}/audit/log?auth_source=cognito_m2m

Request Body:

{
  "operation": "data_export",
  "status": "success",
  "operationDetails": {
    "recordCount": 1500,
    "exportFormat": "csv",
    "targetBucket": "analytics-output"
  },
  "filePath": "exports/2026-03/quarterly-report.csv",
  "errorMessage": ""
}
Field Type Required Description
operation string Operation name (alphanumeric, _, :, ., -, /; max 100 chars)
status string success, failed, error, or partial (default: success)
operationDetails object Free-form JSON metadata (max 4 KB serialized)
filePath string Related file path (max 500 chars, no control characters)
errorMessage string Error description when status ≠ success (max 1000 chars)

Validation Rules: - operation must match ^[a-zA-Z0-9_:./-]{1,100}$ - operationDetails serialized JSON must be ≤ 4 KB - filePath must not contain control characters and ≤ 500 chars - appId is taken from the URL path (trusted), never from the body

Response 200 OK:

{
  "data": {
    "message": "Audit log recorded",
    "userId": "johndoe",
    "appId": "my-app",
    "operation": "data_export",
    "timestamp": "2026-03-21T08:00:00.000Z"
  }
}

Source Tagging: - Human user (Federate): source = "external_app" - M2M client (Cognito): source = "external_app_m2m"

Error Responses: - 400 — Missing operation, invalid characters, or payload too large - 401 — Unable to determine userId from token - 404 — App not found in registry


8. Error Handling

All error responses follow a consistent format:

{
  "statusCode": 403,
  "body": {
    "error": "Access denied: You can only access your own directory and the .public directory",
    "errorCode": "ACCESS_DENIED",
    "errorId": "ERR-uuid-..."
  }
}

Common Error Codes

HTTP Status Error Code Description Action
400 VALIDATION_ERROR Missing or invalid parameters Fix request parameters
400 MISSING_CONTENT_TYPE contentType required for upload Add contentType to body
400 MISSING_DIRECTORY Directory required for non-owner Add directory parameter
400 INVALID_FILENAME Filename contains invalid characters Fix filename
401 INVALID_TOKEN Token expired or invalid Refresh token or re-login
401 TOKEN_EXPIRED Token has expired Call refresh-token endpoint
403 ACCESS_DENIED No access to resource Check your role and directory access
403 PERMISSION_DENIED Feature restricted to higher role Contact app admin
404 NOT_FOUND Resource not found Verify resource exists
409 CONFLICT Resource already exists Check for duplicates
500 INTERNAL_ERROR Server-side error Retry or contact admin

9. Permission & Access Control Reference

9.1 Role Permissions

Data Access Permissions (applicable to all app-level roles):

Permission Owner Manager Member Description
app:files:list List files in accessible directories
app:files:upload Generate upload presigned URLs
app:files:download Generate download presigned URLs
app:files:delete Delete files in accessible directories
app:files:copy Copy files between accessible directories

App Admin Permissions (App Admin only — see Platform Admin API Reference for details):

Permission Description
app:admin Administrative operations (manage app settings)
app:users:manage Add/remove/update users in the app
app:config:view View app configuration and user list

9.2 Directory Access Matrix

Directory Owner Manager Member
.private Read ✅ Write ✅
.public Read ✅ Write ✅ Read ✅ Write ❌ Read ✅ Write ❌
Own directory Read ✅ Write ✅ Read ✅ Write ✅ Read ✅ Write ✅
Subordinate's directory N/A Read ✅ Write ✅ N/A
Other user's directory Read ✅ Write ✅ (active)
Soft-deleted user dir Read ✅ Write ❌

9.3 Delegation Permission Mapping

Delegation Type Permissions Granted
FULL app:files:list, app:files:upload, app:files:download, app:files:delete
READ_ONLY app:files:list, app:files:download

9.4 Delegation Directory Access

When accessing directories via delegation, the access is based on the grantor's role:

Grantor Role Delegatee Can Access
Owner All directories (.private, .public, all user dirs)
Manager Grantor's own directory + grantor's subordinates' directories
Member Grantor's own directory only

9.5 Access Mode Behavior

Access Mode Who Can Access How to Add Users
Whitelist Only users explicitly added to the app Add via POST /apps/{appId}/users
Public All authenticated users (auto member role) Only owner role can be explicitly added

9.6 M2M Client Access

M2M clients follow the same permission model as human users:

  1. Register the M2M client_id as a user in the app
  2. Assign a role (owner, manager, or member)
  3. Use ?auth_source=cognito_m2m on all API calls
  4. The client_id serves as the user's identity (sub)

Supported M2M Operations: - External audit logging (POST /apps/{appId}/audit/log) - File operations (if registered with appropriate role) - Any endpoint that accepts @require_auth authentication


10. Environment Configuration

OAuth configuration is embedded in config.json (deployed per environment to S3). The GET /auth/config endpoint has been removed.

Field Staging / Experimental Production
API Endpoint https://nbsaihub.ags.amazon.dev/prod https://ai.wwgs.amazon.dev/prod
client_id nbsaihubstaging ai-app-store
authorization_endpoint https://idp-integ.federate.amazon.com/api/oauth2/v1/authorize https://idp.federate.amazon.com/api/oauth2/v1/authorize
scope openid profile email openid profile email
redirect_uri https://nbsaihub.ags.amazon.dev/callback.html https://ai.wwgs.amazon.dev/callback.html

Example config.json for production:

{
    "api_gateway_url": "https://ai.wwgs.amazon.dev/prod",
    "oauth_config": {
        "client_id": "ai-app-store",
        "authorization_endpoint": "https://idp.federate.amazon.com/api/oauth2/v1/authorize",
        "scope": "openid profile email",
        "response_type": "code"
    }
}

Pass oauth_config from config.json to the NBSAuth constructor:

const config = await fetch('./config.json').then(r => r.json());
const auth = new NBSAuth({
    apiEndpoint: config.api_gateway_url,
    redirectUri: window.location.origin + '/callback.html',
    appId: 'your-app-id',
    oauthConfig: config.oauth_config  // Required — no /auth/config API call needed
});