NBS 开发者集成指南

面向开发者的实用指南,帮助构建与 NBS 平台集成的 Web 或客户端应用程序。


目录

  1. 概述
  2. 开发流程
  3. SSO 集成 (auth.js / NBSAuth)
  4. 文件服务集成 (nbs-file-client.js / NBSFileClient)
  5. 审计服务集成 (REST API)
  6. 认证模式
  7. 基于角色的访问控制
  8. 环境配置
  9. 常见问题排查
  10. 完整示例

1. 概述

NBS 平台为第三方应用提供三项核心服务:

开发者通过两个 JavaScript 库进行集成:

用途
auth.js NBSAuth SSO 认证、令牌管理、会话处理
nbs-file-client.js NBSFileClient 文件上传、下载、删除、列表

注意: 请勿使用 nbs-platform-client.js — 该库仅用于平台管理操作,不适用于第三方应用集成。

审计日志无需 JS 库 — 只需简单的 REST API 调用即可。


2. 开发流程

从注册到上线的分步流程:

步骤 1 — 联系平台管理团队

提交应用注册申请,需提供以下信息:

信息项 说明 是否必填
应用名称 应用的显示名称 必填
应用描述 简要说明应用功能 必填
分类 data-insights / task-management / workflow-automation / seller-engagement-content-generation 必填
入口 URL 用户访问应用的地址,如 http://localhost:3000https://your-app.harmony.a2z.com/。注册时可留空,部署后再更新。 可选
访问模式 public(所有已认证用户)或 whitelist(仅受邀用户) 必填
初始管理员 用户 ID / 登录别名列表 必填

步骤 2 — 应用注册

平台管理员在注册表中创建您的应用,并提供: - appId — kebab-case 格式的标识符(例如 my-cool-app),用于所有 API 调用 - 实验环境 API 端点 URL — 用于集成测试

注意: 生产环境 API 端点将在您准备上线时提供。

步骤 3 — 配置用户和角色

在测试之前,需要配置用户: - 应用管理员(Owner) 可以通过应用管理控制台自行管理用户 — 无需平台管理员参与 - Public 模式: 所有已认证用户均可访问。您仍可为特定用户分配 ownermanager 角色以获得更高权限。 - Whitelist 模式: 应用管理员通过应用管理控制台添加用户。每个用户获得一个角色:ownermanagermember

步骤 4 — 集成平台服务

根据您的应用架构(浏览器端或客户端/服务器端),集成所需的服务。请参阅以下详细指南:

提示: 您的 API 端点应可配置(例如通过 config.json 或环境变量),以便在实验环境和生产环境之间轻松切换。

步骤 5 — 在实验环境中测试

使用实验环境 API 端点进行测试: - SSO 登录/登出流程 - 文件上传、下载、列表 - 审计日志记录 - 基于角色的访问(使用 owner、manager、member 账户测试)

步骤 6 — 发布

两种部署选项:

选项 方式 适用场景
A:平台托管 将源代码提供给平台团队。他们会部署到平台 S3 存储桶的 apps/{appId}/ 目录下,通过 CloudFront 提供服务。 简单的静态 Web 应用,无自定义后端
B:自托管 在您自己的基础设施上托管(Harmony、EC2 等)。将入口 URL 提供给平台管理员以更新应用注册表。 有自定义后端、需求复杂的应用

步骤 7 — 上线

  1. 平台管理员提供生产环境 API 端点
  2. 将应用的 API 端点配置更新为生产环境
  3. 平台管理员在注册表中激活应用
  4. 用户现在可以通过 NBS App Store 访问您的应用

3. SSO 集成

模块作用: SSO(单点登录)集成使您的应用能够利用 NBS 平台的统一身份认证体系,用户无需为每个应用单独注册和登录。通过集成 SSO,您的应用可以获取用户身份信息(姓名、邮箱、角色等),并基于角色控制功能访问权限。

适用场景: - 需要识别用户身份的任何 Web 或客户端应用 - 需要基于角色(owner/manager/member)展示不同功能的应用 - 需要调用 NBS 文件服务或审计服务的应用(这些服务要求 SSO 认证) - 后端自动化服务需要以服务身份调用 NBS API(M2M 场景)

NBS 支持两种认证路径,适用于不同场景:

认证路径 令牌类型 使用场景 适用对象
Federate OAuth 2.0 PKCE Federate JWT (RS256) 交互式用户会话 — 用户通过浏览器登录 Web 应用、SPA、任何用户点击"登录"的 UI
Cognito M2M (Client Credentials) Cognito Access Token (RS256) 服务间调用 — 无人工用户参与 后端服务、定时任务、CI/CD 流水线、自动化脚本

3.1 Federate 认证 — 浏览器端(Web 应用)

适用于用户通过浏览器交互登录的 Web 应用。auth.js 提供了 NBSAuth 类,处理完整的 OAuth 2.0 PKCE 流程。

平台托管应用(部署在 /apps/{appId}/ 下):使用平台统一回调地址 /callback.html。这样无需为每个子应用在 Federate 中单独注册回调 URL — 所有平台托管应用只需一个回调地址。

自托管应用(部署在自己的域名上):使用自己页面的 URL 作为回调地址,并在 Federate 中注册。

最简配置

<script src="./auth.js"></script>
<script>
async function initApp() {
    // 1. 加载配置
    const resp = await fetch('./config.json');
    const config = await resp.json();

    // 2. 创建认证实例
    const auth = new NBSAuth({
        apiEndpoint: config.api_gateway_url,
        redirectUri: window.location.origin + '/callback.html',  // 平台统一回调地址
        appId: 'your-app-id',
        appName: 'Your App Name'
    });

    // 3. 初始化(从后端获取 OAuth 配置)
    await auth.initialize();

    // 4. 检查认证状态(令牌通过 sessionStorage 从平台回调页共享)
    if (!auth.isAuthenticated()) {
        await auth.login(); // 重定向到 OAuth → 平台回调 → 返回本页面
        return;
    }

    // 5. 验证此应用的令牌(服务器端验证)
    const userInfo = await auth.verifyToken('your-app-id');

    // 5. 检查认证状态
    if (!auth.isAuthenticated()) {
        await auth.login(); // 重定向到 OAuth 登录页面
        return;
    }

    // 6. 验证此应用的令牌(服务器端验证)
    const userInfo = await auth.verifyToken('your-app-id');
    // userInfo: { sub, appId, userRole, fullname, email, effectiveRole, activeDelegations }

    // 7. 应用就绪
    console.log('Welcome', userInfo.fullname);
}

initApp();
</script>

生产就绪模式(带遮罩层 UI)

为了提供更好的用户体验,使用登录遮罩层在认证过程中显示状态信息:

<script src="./auth.js"></script>

<!-- 登录遮罩层 -->
<div id="ssoOverlay" style="display:none; position:fixed; inset:0; background:rgba(245,245,247,0.97);
    backdrop-filter:blur(20px); z-index:99999; align-items:center; justify-content:center; flex-direction:column;">
    <div style="text-align:center; max-width:400px; padding:48px 40px; background:white;
        border-radius:24px; box-shadow:0 8px 40px rgba(0,0,0,0.12);">
        <div style="font-size:48px; margin-bottom:16px;">🔐</div>
        <h2 style="font-size:24px; font-weight:600; margin-bottom:8px;">Your App Name</h2>
        <p id="ssoStatusMsg" style="font-size:16px; color:#515154; margin-bottom:32px;">Verifying identity...</p>
        <button id="ssoLoginBtn" onclick="window._auth && window._auth.login()" style="display:none;
            width:100%; background:#0071e3; color:white; border:none; border-radius:12px;
            padding:16px 24px; font-size:16px; font-weight:600; cursor:pointer;">
            Sign In with SSO
        </button>
    </div>
</div>

<script>
const APP_ID = 'your-app-id';
const APP_NAME = 'Your App Name';

function showOverlay(msg, showBtn) {
    const overlay = document.getElementById('ssoOverlay');
    overlay.style.display = 'flex';
    document.getElementById('ssoStatusMsg').textContent = msg;
    document.getElementById('ssoLoginBtn').style.display = showBtn ? 'block' : 'none';
}

function hideOverlay() {
    document.getElementById('ssoOverlay').style.display = 'none';
}

document.addEventListener('DOMContentLoaded', async () => {
    try {
        showOverlay('Loading configuration...', false);
        const resp = await fetch('./config.json');
        const config = await resp.json();

        const auth = new NBSAuth({
            apiEndpoint: config.api_gateway_url,
            redirectUri: window.location.origin + '/callback.html',  // 平台统一回调地址
            appId: APP_ID,
            appName: APP_NAME
        });
        window._auth = auth;

        await auth.initialize();

        if (auth.isAuthenticated()) {
            showOverlay('Verifying identity...', false);
            try {
                const userInfo = await auth.verifyToken(APP_ID);
                hideOverlay();
                onAppReady(auth, userInfo, config);
            } catch (e) {
                if (e.message === 'TOKEN_EXPIRED') {
                    await auth.refreshAccessToken();
                    const userInfo = await auth.verifyToken(APP_ID);
                    hideOverlay();
                    onAppReady(auth, userInfo, config);
                } else if (e.message === 'FORBIDDEN_ACCESS') {
                    showOverlay('You do not have access to this app.', false);
                    return;
                } else {
                    throw e;
                }
            }
        } else {
            showOverlay('Please sign in to continue.', true);
            return;
        }
    } catch (err) {
        console.error('SSO init failed:', err);
        showOverlay('Initialization failed. Please refresh.', true);
    }
});

function onAppReady(auth, userInfo, config) {
    console.log('Welcome', userInfo.fullname, '| Role:', userInfo.userRole);
    // 您的应用逻辑从这里开始
}
</script>

NBSAuth API 参考

构造函数

const auth = new NBSAuth({
    apiEndpoint: 'https://...',    // 必填 — API Gateway 端点 URL
    redirectUri: '...',            // 必填 — OAuth 重定向 URI(平台托管应用使用 '/callback.html')
    appId: 'my-app',              // 可选 — 用于登录追踪的应用 ID
    appName: 'My App',            // 可选 — 用于登录追踪的应用名称
    usePersistentStorage: false    // 可选 — true = localStorage,false = sessionStorage(默认)
});

方法

Method Returns 描述
initialize() Promise<void> /auth/config 获取 OAuth 配置。启动时调用一次。
login() Promise<void> 将浏览器重定向到 OAuth 授权页面(PKCE 流程)。
handleCallback() Promise<boolean> 用授权码交换令牌。成功返回 true
isAuthenticated() boolean 检查存储中是否存在令牌(不进行验证)。
verifyToken(appId) Promise<Object> 调用 /auth/verify-token — 从后端返回可信的 user_info推荐的应用访问检查方式。
getValidAccessToken() Promise<string> 返回有效的访问令牌。如果令牌将在 5 分钟内过期,自动刷新。
getAccessToken() string\|null 返回当前访问令牌,不检查是否需要刷新。
getUserInfo() Object\|null 返回上次令牌交换/验证时缓存的 user_info 对象。
refreshAccessToken() Promise<Object> 手动刷新访问令牌。使用互斥锁防止并发刷新。
isTokenExpired(bufferSeconds) boolean 如果令牌将在 bufferSeconds 秒内过期,返回 true
ensureValidSession(appId) Promise<boolean> 组合检查:验证 + 自动刷新 + 失败时自动重定向。
logout() void 清除存储中的所有令牌并重定向到登录页面。

verifyToken 响应对象

{
    sub: 'user-id',              // 用户标识符
    appId: 'my-app',            // 验证令牌所针对的应用
    userRole: 'member',          // 在此应用中的角色:'owner'、'manager' 或 'member'
    fullname: 'John Doe',       // 显示名称
    email: 'john@example.com',  // 电子邮件地址
    effectiveRole: 'member',    // 有效角色(考虑委托关系)
    activeDelegations: []       // 活跃的委托条目
}

重要说明

3.2 Federate 认证 — 服务器端(客户端/服务器应用)

适用于有后端服务器(Node.js、Python、Java 等)的应用,服务器直接验证 Federate JWT 并代表用户调用 NBS API。

流程:

浏览器 → 您的后端 → NBS API(携带 Bearer 令牌)

工作原理:

  1. 您的前端获取 Federate 访问令牌(在浏览器中使用 NBSAuth,或使用您自己的 OAuth 流程)
  2. 前端将令牌发送到您的后端(例如通过 Cookie 或 Authorization 头)
  3. 您的后端在调用 NBS API 时携带该令牌:
# Python 示例 — 从后端调用 NBS API
import requests

NBS_API = "https://your-api-endpoint.execute-api.region.amazonaws.com/prod"

def list_files(access_token, app_id, directory=""):
    resp = requests.get(
        f"{NBS_API}/apps/{app_id}/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={"directory": directory}
    )
    resp.raise_for_status()
    return resp.json()

def log_audit(access_token, app_id, operation, details=None):
    resp = requests.post(
        f"{NBS_API}/apps/{app_id}/audit/log",
        headers={
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json"
        },
        json={
            "operation": operation,
            "status": "success",
            "operationDetails": details or {}
        }
    )
    resp.raise_for_status()
    return resp.json()

要点: - NBS API Gateway Lambda Authorizer 会验证 Federate JWT — 您的后端无需自行验证令牌 - 令牌中的 sub 声明标识用户身份 - 所有 NBS API 端点接受 Authorization: Bearer <federate_access_token> - 令牌刷新由您的后端负责 — 当访问令牌过期时,使用刷新令牌调用 POST /auth/refresh-token

3.3 Cognito M2M 认证(服务间通信)

适用于自动化服务、定时任务、CI/CD 流水线,或任何无人工用户参与的场景。

何时使用 M2M: - 后端批处理(例如每晚生成报表) - CI/CD 流水线将构建产物上传到 NBS 文件存储 - 监控系统的自动化审计日志记录 - 服务间集成,您的后端在无用户会话的情况下调用 NBS API

前提条件: - 联系平台管理团队获取 Cognito M2M 客户端凭证(client_id + client_secret) - M2M 客户端已在与 NBS 平台关联的 Cognito User Pool 中注册

步骤 1 — 获取 M2M 访问令牌:

import requests

COGNITO_TOKEN_ENDPOINT = "https://your-cognito-domain.auth.region.amazoncognito.com/oauth2/token"
CLIENT_ID = "your-m2m-client-id"
CLIENT_SECRET = "your-m2m-client-secret"

def get_m2m_token():
    resp = requests.post(
        COGNITO_TOKEN_ENDPOINT,
        data={
            "grant_type": "client_credentials",
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
            "scope": "openid"
        },
        headers={"Content-Type": "application/x-www-form-urlencoded"}
    )
    resp.raise_for_status()
    return resp.json()["access_token"]

步骤 2 — 使用 auth_source=cognito_m2m 调用 NBS API:

NBS_API = "https://your-api-endpoint.execute-api.region.amazonaws.com/prod"

def upload_file_m2m(token, app_id, directory, filename, content_type):
    """通过 M2M 认证获取预签名上传 URL"""
    resp = requests.post(
        f"{NBS_API}/apps/{app_id}/files/upload?auth_source=cognito_m2m",
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        },
        json={
            "directory": directory,
            "filename": filename,
            "contentType": content_type
        }
    )
    resp.raise_for_status()
    return resp.json()  # 包含 uploadUrl、requiredHeaders、s3Key

def log_audit_m2m(token, app_id, user_id, operation, details=None):
    """代表用户记录审计事件(M2M 必须在请求体中提供 userId)"""
    resp = requests.post(
        f"{NBS_API}/apps/{app_id}/audit/log?auth_source=cognito_m2m",
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        },
        json={
            "operation": operation,
            "status": "success",
            "operationDetails": details or {},
            "userId": user_id  # M2M 必填 — 标识最终用户
        }
    )
    resp.raise_for_status()
    return resp.json()

Federate 认证与 M2M 认证的主要区别:

方面 Federate(用户) Cognito M2M(服务)
查询参数 无(默认) ?auth_source=cognito_m2m
用户身份 从令牌 sub 中提取 必须在请求体中提供 userId(用于审计)
令牌来源 OAuth 2.0 PKCE 流程(浏览器) Client Credentials Grant(服务器)
令牌刷新 POST /auth/refresh-token 从 Cognito 请求新令牌
使用场景 交互式用户会话 自动化/后台处理

4. 文件服务集成

模块作用: 文件服务为您的应用提供安全的、按应用隔离的文件存储能力。每个应用拥有独立的文件空间,支持基于角色的目录访问控制、文件上传/下载/删除、软删除与恢复、分页列表和通配符搜索。所有文件操作通过 S3 Presigned URL 完成,无需直接访问 S3。

适用场景: - 需要为用户提供个人文件存储空间的应用(如文档管理、报表系统) - 需要团队共享文件的协作应用(通过 .public/ 目录) - 需要 Owner 分发文件给团队成员的管理应用 - 需要批量文件处理的自动化服务(通过 M2M 认证)

nbs-file-client.js 提供了 NBSFileClient 类,用于所有文件操作。它需要一个 NBSAuth 实例进行认证。

配置

<script src="./auth.js"></script>
<script src="./nbs-file-client.js"></script>
<script>
// SSO 初始化完成后:
const fileClient = new NBSFileClient(config.api_gateway_url, auth);
</script>

NBSFileClient API 参考

构造函数

const fileClient = new NBSFileClient(apiEndpoint, authClient);
// apiEndpoint — API Gateway 端点 URL(字符串)
// authClient  — 一个 NBSAuth 实例(必须已初始化并完成认证)

方法

Method Returns 描述
getUserApps() Promise<{ apps, role, totalApps }> 列出用户可访问的所有应用。
uploadFile(appId, path, file) Promise<{ key, message }> 上传文件。path 格式:"directory/filename""filename"
downloadFile(appId, path) Promise<Blob> 下载文件。返回 Blob 对象。
deleteFile(appId, path) Promise<{ message }> 软删除文件。
listFiles(appId, options) Promise<Object> 列出文件,支持过滤和分页。
listTeamSharedFiles(appId) Promise<Array> 列出 .public/ 目录中的文件。
getUserRole(appId) Promise<string\|null> 返回 'owner''manager''member'null

listFiles 选项

await fileClient.listFiles('my-app', {
    directory: 'user1',           // 目录路径(默认:根目录)
    recursive: false,             // 递归列出所有文件(仅 owner)
    search: '*report*.xlsx',      // 通配符搜索模式
    includeDeleted: false,        // 包含已软删除的文件(仅 owner)
    maxKeys: 1000,                // 每页最大结果数
    continuationToken: null       // 上一次响应的分页令牌
});

目录结构

每个应用在 S3 中的目录布局如下:

{appId}/
├── .private/          # 仅 Owner — 完全读写权限
├── .public/           # 所有角色 — 读写权限
└── {userId}/          # 个人目录
                       #   owner:可查看所有用户目录
                       #   member:仅可查看自己的目录

文件上传示例

const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const userId = auth.getUserInfo().sub;

try {
    const result = await fileClient.uploadFile('my-app', `${userId}/${file.name}`, file);
    console.log('Uploaded:', result.key);
} catch (err) {
    console.error('Upload failed:', err.message);
}

文件下载示例

async function downloadFile(appId, filePath, downloadName) {
    const blob = await fileClient.downloadFile(appId, filePath);
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = downloadName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
}

// 用法
await downloadFile('my-app', `${userId}/report.xlsx`, 'report.xlsx');

文件列表示例

// 列出用户自己目录中的文件
const result = await fileClient.listFiles('my-app', { directory: userId });
console.log('Files:', result.files);
console.log('Folders:', result.folders);

// 分页列表
let token = null;
do {
    const page = await fileClient.listFiles('my-app', {
        directory: userId,
        maxKeys: 100,
        continuationToken: token
    });
    console.log('Page:', page.files);
    token = page.isTruncated ? page.nextContinuationToken : null;
} while (token);

Presigned URL 的 IP 限制

当平台启用了 IP 限制功能时,presigned URL 会被锁定到客户端的 IP 地址(CIDR /24 范围)。这可以防止 URL 被分享——为某个用户生成的 presigned URL 如果从其他 IP 访问会返回 403。

浏览器端应用: 无需额外处理。平台会自动从请求中提取客户端 IP 并限制 presigned URL。

客户端/服务器应用: 您的后端必须将终端用户的真实 IP 转发到 NBS API,以确保 presigned URL 被限制到正确的 IP。通过传递 X-Forwarded-For 头实现:

# Python 后端示例 — 将客户端 IP 转发到 NBS API
def get_download_url(user_token, app_id, directory, filename, client_ip):
    resp = requests.post(
        f"{NBS_API}/apps/{app_id}/files/download",
        headers={
            "Authorization": f"Bearer {user_token}",
            "Content-Type": "application/json",
            "X-Forwarded-For": client_ip  # 转发终端用户的真实 IP
        },
        json={"directory": directory, "filename": filename}
    )
    return resp.json()["downloadUrl"]

如果您的后端有自己的 API Gateway 并附加了 Lambda Authorizer: API Gateway 可能会在 authorizer 和主 Lambda 之间修改 X-Forwarded-For 头。为确保原始客户端 IP 被保留,让您的 authorizer 在返回的 context 中传递原始 IP(例如 originalXFForiginalSourceIp 字段)。NBS 平台自带的 authorizer Lambda 已经实现了这一点——如果您使用的是 NBS authorizer Lambda,则无需额外操作。

关键规则: 请求链路中的每个代理或网关都必须通过 X-Forwarded-For 转发原始客户端 IP。如果任何一个环节丢弃了这个头,presigned URL 将被限制到错误的 IP,终端用户的下载将返回 403 错误。

自动 401 重试

所有 NBSFileClient 方法自动处理令牌过期。收到 401 响应时,客户端会: 1. 调用 auth.refreshAccessToken() 获取新令牌 2. 使用新令牌重试原始请求一次

您的应用代码中无需手动编写重试逻辑。


5. 审计服务集成

模块作用: 审计服务为您的应用提供结构化的操作日志记录能力,用于合规审计和问题追溯。所有审计记录统一存储在 NBS 平台的审计日志表中,平台管理员可通过审计控制台查询和分析。

适用场景: - 需要记录用户关键操作(如查看报表、导出数据、修改配置)的合规应用 - 需要追踪文件访问记录的安全敏感应用 - 需要从后端服务记录操作日志的自动化系统(通过 M2M 认证)

审计服务是一个 REST API — 无需 JS 库。直接使用 fetch 调用即可。

端点

POST /apps/{appId}/audit/log

认证

在请求头中包含访问令牌作为 Bearer 令牌:

Authorization: Bearer <access_token>

请求体

{
    "operation": "view_report",
    "status": "success",
    "operationDetails": { "reportId": "R-001", "format": "pdf" },
    "filePath": "user1/reports/Q1.pdf",
    "errorMessage": ""
}

字段验证规则

字段 必填 约束
operation 正则:^[a-zA-Z0-9_:./-]{1,100}$
status 可选值:successfailederrorpartial。默认:success
operationDetails JSON 对象,序列化后最大 4 KB
filePath 最大 500 个字符,不含控制字符
errorMessage 最大 1000 个字符

辅助函数

/**
 * 为当前用户记录审计事件。
 *
 * @param {NBSAuth} auth       - 已认证的 NBSAuth 实例
 * @param {string} apiEndpoint - API Gateway URL
 * @param {string} appId       - 您的应用 ID
 * @param {string} operation   - 操作名称(例如 'view_report'、'export_data')
 * @param {Object} [details]   - 可选的操作详情
 * @param {string} [status]    - 可选的状态(默认:'success')
 */
async function logAudit(auth, apiEndpoint, appId, operation, details = {}, status = 'success') {
    const token = await auth.getValidAccessToken();
    const response = await fetch(`${apiEndpoint}/apps/${appId}/audit/log`, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            operation,
            status,
            operationDetails: details
        })
    });

    if (!response.ok) {
        console.error('Audit log failed:', response.status);
    }
}

使用示例

// 记录成功的报表查看
await logAudit(auth, apiEndpoint, 'my-app', 'view_report', {
    reportId: 'R-001',
    format: 'pdf'
});

// 记录失败的导出
await logAudit(auth, apiEndpoint, 'my-app', 'export_data', {
    format: 'csv',
    recordCount: 5000
}, 'failed');

// 记录带文件路径的操作
const token = await auth.getValidAccessToken();
await fetch(`${apiEndpoint}/apps/my-app/audit/log`, {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        operation: 'download_file',
        status: 'success',
        filePath: 'user1/reports/Q1-2024.xlsx',
        operationDetails: { fileSize: '2.4MB' }
    })
});

响应

{
    "message": "Audit log recorded",
    "userId": "user1",
    "appId": "my-app",
    "operation": "view_report",
    "timestamp": "2024-01-15T08:30:00.000Z"
}

6. 认证模式

模式 行为 配置
Public 所有已认证的 NBS 用户均可访问应用。用户默认获得 member 角色。应用管理员仍可为特定用户分配 ownermanager 角色以获得更高权限。 平台管理员在注册时将访问模式设置为 public
Whitelist 仅明确添加的用户可以访问。未授权用户将收到 403 FORBIDDEN_ACCESS 错误。 平台管理员将访问模式设置为 whitelist。应用管理员(owner)通过应用管理控制台管理用户 — 日常用户管理无需平台管理员参与。

7. 基于角色的访问控制

角色 文件访问权限 描述
owner 完全访问所有目录,包括 .private/ 应用管理员 — 可查看所有用户目录,全局管理文件
manager .private/ 外的所有目录 团队负责人 — 可查看所有用户目录,但无法访问私有存储
member 仅限自己的 {userId}/ 目录和 .public/ 普通用户 — 个人存储和共享文件

角色由平台管理员或应用 owner 分配。角色信息通过 verifyToken()userRole 字段返回。


8. 环境配置

环境 用途 API 端点
实验环境 集成测试、开发 由平台团队提供
生产环境 正式用户、真实数据 由平台团队提供

通过更新 config.json 切换环境:

{
    "api_gateway_url": "https://EXPERIMENTAL-ENDPOINT.execute-api.region.amazonaws.com/prod"
}

→ 切换到生产环境:

{
    "api_gateway_url": "https://PRODUCTION-ENDPOINT.execute-api.region.amazonaws.com/prod"
}

提示: 保留单独的 config-experimental.jsonconfig-production.json 文件,在部署时将相应文件复制为 config.json


9. 常见问题排查

问题 原因 解决方案
401 Unauthorized 令牌过期或无效 NBSAuth 会自动刷新令牌。如果问题持续,调用 auth.login() 重新认证。
403 Forbidden 用户不在白名单中 联系平台管理员将用户添加到应用白名单。
CORS 错误 您的域名不在允许的来源列表中 联系平台管理员将您的域名添加到 API Gateway CORS 配置中。
verifyToken 抛出 FORBIDDEN_ACCESS 应用处于 whitelist 模式且用户无权限 显示"无访问权限"提示。用户需由平台管理员或应用 owner 添加。
verifyToken 抛出 TOKEN_EXPIRED 访问令牌已过期 调用 auth.refreshAccessToken() 然后重试 verifyToken()
verifyToken 抛出 SYSTEM_ERROR 后端错误或网络问题 短暂延迟后重试。如果问题持续,检查 API 端点配置和网络连接。
文件上传失败 路径格式不正确或权限不足 确保路径格式为 "directory/filename"。Member 只能上传到自己的 {userId}/ 目录或 .public/
config.json 加载失败 路径错误或 CORS 问题 确保 config.json 与 HTML 文件在同一目录下。检查浏览器开发者工具的网络标签页。

10. SDK 参考包与示例代码

我们提供了一个完整的 SDK 参考包(位于 bs-sdk/ 目录),包含可运行的示例应用,替代内联代码示例。

SDK 目录结构

nbs-sdk/ ├── README.md # 快速开始指南 ├── KIRO_PROMPTS.md # AI 辅助集成提示词(见下方) ├── lib/ # 共享 JS 库(复制到您的项目) │ ├── auth.js # NBSAuth — OAuth 2.0 PKCE 认证 │ └── nbs-file-client.js # NBSFileClient — 文件操作 ├── browser-app/ # 浏览器端 (BS) 示例 │ ├── config.json # API 端点配置 │ └── index.html # 完整演示:SSO + 文件 + 审计 └── server-app/ # 客户端/服务器 (CS) 示例 ├── config.json # API 端点配置 ├── index.html # 前端:SSO 登录,调用后端 └── server.py # Python 后端:Federate 中转 + M2M

浏览器端示例 (rowser-app/)

单个 HTML 文件演示了三项集成(SSO + 文件 + 审计),完全在浏览器中运行。试用方法:

  1. 将 config.json 中的 API 端点更新为您的实验环境地址
  2. 将 index.html 中的 APP_ID 改为您注册的 appId
  3. 运行:python -m http.server 3000(在 rowser-app/ 目录下)
  4. 打开 http://localhost:3000

客户端/服务器示例 (server-app/)

前端 + Python 后端演示两种模式: - Federate 中转: 前端做 SSO 登录,将令牌传给后端,后端中转到 NBS API - M2M(机器对机器): 后端使用自己的 Cognito 凭证调用 NBS API,无需用户会话

试用方法: 1. 更新两个 config.json 的 API 端点 2. 设置环境变量:NBS_API_URL、COGNITO_TOKEN_ENDPOINT、COGNITO_CLIENT_ID、COGNITO_CLIENT_SECRET 3. 运行:pip install requests flask flask-cors && python server.py 4. 打开 http://localhost:3000 访问前端

使用 Kiro 进行 AI 辅助集成

SDK 包含 KIRO_PROMPTS.md,提供了可直接使用的 Kiro(或其他 AI 编程助手)提示词。在编辑器中打开相关示例文件,然后复制粘贴对应的提示词:

提示词 场景 参考文件
Prompt 1 在 Web 应用中集成 Federate SSO(浏览器端) lib/auth.js, rowser-app/index.html
Prompt 2 在客户端/服务器应用中集成 Federate SSO lib/auth.js, server-app/index.html, server-app/server.py
Prompt 3 集成 Cognito M2M 认证(服务间通信) server-app/server.py
Prompt 4 集成 NBS 文件服务 lib/nbs-file-client.js, rowser-app/index.html
Prompt 5 集成 NBS 审计服务 rowser-app/index.html, server-app/server.py
Prompt 6 全量集成(SSO + 文件 + 审计) rowser-app/ 或 server-app/ 全部文件

提示: 在发送提示词之前,先在编辑器中打开上表列出的参考文件,这样 Kiro 就能获取生成准确代码所需的上下文。记得将提示词中的 {YOUR_APP_ID} 等占位符替换为您的实际值。


快速参考

config.json                → { "api_gateway_url": "https://..." }
auth.js                    → NBSAuth 类(SSO、令牌、会话)
nbs-file-client.js         → NBSFileClient 类(文件操作)
POST /apps/{appId}/audit/log → 审计日志(REST,无需库)

最小集成清单:

步骤 操作 必要性
1 复制 auth.js + config.json 必须
2 初始化 NBSAuth,处理回调,验证令牌 必须
3 复制 nbs-file-client.js,创建 NBSFileClient 可选(需要文件服务时)
4 调用审计 REST API 记录操作日志 可选(需要审计时)
5 在实验环境中测试 必须
6 切换到生产环境端点并上线 必须