开放式银行为用户提供了一种更轻松地访问自己的银行帐户信息的方法,例如通过第三方应用程序。这是通过允许第三方金融服务提供商通过使用 API 访问银行客户的财务数据来实现的,从而实现相关各方之间的安全通信。

银行和金融机构利用开放银行业务为其客户提供增强服务、提高透明度和更加个性化的银行体验的一些著名示例:

其他示例包括 Truelayer、Trading 212、Plaid、Revolut 和 Mint。这些组织都以各种方式利用开放银行业务,从账户聚合到支付处理和个人财务管理。请查看开放式银行跟踪器了解更多信息。

监控开放银行 API

就 API 旅程而言,开放式银行业务错综复杂的连续交互对监控的要求可能相当高。 身份验证、授权、加密数据传输层意味着单个事务将涉及多个步骤和多个协同工作的 API 端点,每个端点都受前一步骤的约束。这意味着监控一个孤立的端点是不够的,我们宁愿将整个流程视为一个整体,同时仍然提供正确的信息,以防我们无法快速了解这个复杂的交换中出现了什么问题。

为了更好地了解监控开放银行流程带来的挑战,让我们看一下 Klarna XS2A 应用程序

<块引用>

开放银行 XS2A 应用程序处理所有消费者交互。只要 API 流需要消费者输入,例如选择消费者银行或对银行进行强客户身份验证,就会使用它。此外,根据所选流程,XS2A 应用程序可用于选择特定银行帐户或授权帐户到帐户付款。

现在,让我们将流程分解为其组成步骤:

<部分>
逐步分解

  1. 开始会话:该过程以向 Klarna 的会话启动端点发出 PUT 请求开始,我们在其中设置了必要的参数开放银行会议。语言首选项、银行详细信息、用户信息以及访问各种帐户服务的同意范围都在这里定义,因此会话范围从一开始就被定义,不能被劫持用于任何其他目的。
  2. 启动帐户详细信息流程:获取会话 ID 后,我们继续执行另一个 PUT 请求,这次是启动帐户详细信息流程。通过进一步指定帐户标识符和加密密钥,我们确保所有通信都是安全的。
  3. 选择测试银行:随后是 POST 请求,旨在为用户选择银行。此步骤模拟客户在 Klarna 游乐场内的银行选择。
    a。 [可选]获取流配置 接下来,我们执行 GET 请求来检索流的当前状态。此步骤可确保事务序列按预期进行。
  4. 加密响应:此阶段涉及发送使用 AES 和 RSA 加密算法加密的响应。使用 POST 请求将加密的负载发送回 API 端点,其中包括 RSA 加密的 AES 密钥和 AES 加密的数据。
    这种多层加密方法确保加密数据只能由具有相应 RSA 私钥的 API 端点解密,并且 AES 密钥在整个传输过程中始终受到保护。
  5. 完整流程:此步骤通过将获取的重定向详细信息发布回 Klarna 的 API、确认 API 操作并向前推进流程来绑定交易流程。
  6. 用户银行帐户选择:发送第五步中获取的帐户值。为了安全起见,我们必须对帐号进行加密。
  7. 获取同意:发出POST请求以确保数据访问获得同意,这是开放银行服务的监管要求。响应包括帐户详细信息和余额的 URL,这些 URL 将在后续请求中使用。
  8. 获取帐户详细信息和余额:最终的POST请求涉及获取用户的帐户详细信息和余额,表示交易流程完成。这些请求验证同意令牌并返回特定帐户信息。

使用 PlayWright 测试单个 API 端点

我们刚刚分析的流程由 8 个步骤和 11 个请求组成。

如果我们要编写一个 Playwright 脚本来单独测试第一个请求,它可能看起来像这样:

JavaScript

 

const { test } = require('@playwright/test'); const auth_token = process.env.KOSMA_AUTH_TOKEN; 常量 psu_ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, 如 Gecko) Chrome/80.0.3987.122 Safari/537.36'; const psu_ip = '10.20.30.40'; test.describe('Klarna 开放银行', () => { test('XS2A API 流程', async ({ request }) => { const startSession = 等待 request.put(`https://authapi.openbanking.playground.klarna.com/xs2a/v1/sessions`, { 数据: { 语言: 'en', _选定的_银行:{ 银行代码:'000000', 国家/地区代码:'GB', }, 电源:{ ip_地址:psu_ip, 用户代理:psu_ua, }, 同意范围:{ 帐户:{}, 帐户详细资料: {}, 余额:{}, 交易:{}, 转移: {}, _insights_refresh:{}, 寿命:30, }, _aspsp_access: 'prefer_psd2', _redirect_return_url: 'http://test/auth', 键:{ hsm: '--- xxx ---', aspsp_data: '--- xxx ---', }, }, 标题:{ '内容类型':'应用程序/json', 授权:‘Token’+auth_token, }, }); const startSessionResponse = 等待 startSession.json(); const session_id = startSessionResponse.data.session_id; }); });

请注意,我们需要导出设置为 jsbn.js。):

JavaScript

 

const { RSA } = require('./jsbn.js');
const axios = require('axios');
const crypto = require('crypto');
const CryptoJS = require("crypto-js");

函数 findModAndExp(xs2a_form_key) {

  // Base64解码函数
  函数 b64Decode(str) {
    str = str.replace(/-/g, '+').replace(/_/g, '/');
    while (str.length % 4) {
      str += '=';
    }
    return Buffer.from(str, 'base64').toString('utf8');;
  }

  // 将 JWT 分成三个部分
  const parts = xs2a_form_key.split('.');
  const header = JSON.parse(b64Decode(parts[0]));
  const 负载 = JSON.parse(b64Decode(parts[1]));
  常量签名=零件[2];

  // 从 JWK 对象中提取模值
  const 模数=有效载荷.模数;
  const 指数 = Payload.exponent;

  返回 {
    “模数”:模数,
    ‘指数’:指数
  };
}

函数generateRandomHexString(byteLength) {
  // 创建一个包含随机字节的缓冲区
  const buf = crypto.randomBytes(byteLength);

  // 将缓冲区转换为十六进制字符串
  让 res = '';
  for (让 i = 0; i < buf.length; i++) {
    res += ('0' + (buf[i] & 0xff).toString(16)).slice(-2);
  }
  返回资源;
}

函数加密(公钥,明文){
  如果(!公钥){
    throw new Error('没有给出加密公钥或给出的加密公钥格式错误');
  }
  var { 模数, 指数 } = findModAndExp(publicKey)
  const iv =generateRandomHexString(16);
  const keyHex =generateRandomHexString(256 / 8);
  const key = CryptoJS.enc.Hex.parse(keyHex);
  const 加密 = CryptoJS.AES.encrypt(plainText, key, { iv: CryptoJS.enc.Hex.parse(iv) });
  const ciphertext = crypto.toString();

  const rsa = new RSA.key();
  rsa.setPublic(模数,指数);
  // 加密数据
  const cryptoByRsa = rsa.encrypt(keyHex);
  const cryptoKeyBytes = CryptoJS.enc.Hex.parse(encryptedByRsa);
  // 将加密后的密钥转换为base64
  const 加密密钥 = 加密KeyBytes.toString(CryptoJS.enc.Base64);
  return { ct: 密文, iv: iv, ek: 加密密钥 };
}

异步函数 sendEncryptedResponse(lastResponse, responseForm, auth_token) {
  const publicKey = LastResponse.result.key
  const 加密数据 = 加密(公钥,responseForm);
  让数据= JSON.stringify(加密数据);

  尝试 {
    常量响应 = 等待 axios({
      方法:'发布',
      maxBodyLength:无穷大,
      网址:lastResponse.next,
      标题:{
        “内容类型”:“应用程序/json”,
        授权:
          “令牌”+ auth_token,
      },
      数据:数据
    });
    返回响应.data.data
  } 捕获(错误){
    抛出新的错误(错误);
  }
}

module.exports = { sendEncryptedResponse,generateRandomHexString,加密,findModAndExp }

使用 Checkly 监控流量

为了确保流程整体可靠地运行,我们需要定期运行测试,使其成为有效的监控检查。我们的检查可能需要从多个位置运行并且肯定需要绑定到一个或多个 警报渠道Checkly CLI 使我们能够快速入门,定义所有这些无需离开代码编辑器。

现在,让我们初始化一个 Checkly CLI 项目并复制我们的从上面的脚本进入它。为了节省您的时间,我们已经为您完成了此操作 .

请注意我们如何使用 适当的构造

JavaScript

 

从 'path' 导入 * 作为路径;
从“checkly/constructs”导入{MultiStepCheck};
从 './alertChannels' 导入 { emailChannel, callChannel };

新的 MultiStepCheck('xs2a-flow-check', {
name: 'Klarna 开放银行 - XS2A API 流程',
警报频道:[电子邮件频道,呼叫频道],
静音:假,
代码: {
入口点:path.join(__dirname, 'xs2a.spec.ts'),
},
});

这指向 xs2a.spec.ts,其中包含我们的 Playwright 规范,用于端到端测试整个 API 流程。

只要检查失败,Checkly 就会使用链接的 emailChannelcallChannel 与我们联系:

JavaScript

 

从 'checkly/constructs' 导入 {EmailAlertChannel };
从 'checkly/constructs' 导入 { PhoneCallAlertChannel };

常量发送默认值 = {
发送失败:真,
发送恢复:真,
发送降级:假,
sslExpiry:真,
sslExpiryThreshold: 30,
};

导出 const emailChannel = new EmailAlertChannel('email-channel-1', {
address: 'user@email.com', // 替换为您的电子邮件地址
...发送默认值,
});

导出 const callChannel = new PhoneCallAlertChannel('call-channel-1', {
phoneNumber: '+31061234567890', // 替换为您的电话号码
});

当然,这些只是示例; Checkly 能够通过多种渠道发出警报,从电子邮件和短信到通过 Slack 和 MSTeams 的 Pagerduty 和 OpsGenie。

我们的检查还继承了在 config.checkly.ts 中的项目级别设置的检查默认值:

JavaScript

 

从 'checkly' 导入 { DefineConfig }
从 'checkly/constructs' 导入 {Frequency}

常量配置=定义配置({
  项目名称:'OpenBanking CLI 项目',
  LogicalId: 'openbanking-cli-project',
  repoUrl: 'https://github.com/checkly-solutions/checkly-open-banking',
  检查:{
    位置:['us-east-1'、'us-east-2'、'us-west-1']、
    并行运行:真,
    频率:频率.EVERY_1M,
    标签:['开放银行'],
    运行时 ID: '2023.09',
    checkMatch: '**/*.check.ts',
    浏览器检查:{
      testMatch: '**/__checks__/*.spec.ts',
    },
  },
})

导出默认配置

请注意将从调度策略 runParallel频率 参数运行检查的位置。我们确保我们的检查在多个地点同时进行高频率运行,以彻底监控面向客户的重要流程。

就代码而言,一切都已准备就绪,但如果我们希望检查真正起作用,我们仍然需要将 Playwright 规范中的 KOSMA_AUTH_TOKEN 环境变量提供给 Checkly。我们可以轻松地做到这一点:

打字稿

 

npx checkly env add KOSMA_AUTH_TOKEN  -l

我们现在已准备好将检查实际部署到 Checkly:

打字稿

 

npx 检查部署

登录我们的 Checkly 帐户,我们可以看到我们的检查现在正在按计划运行:

schedule

对于每个调用,我们现在可以看到详细的报告,显示检查中每个请求的时间、结果和其他请求详细信息:

测试报告

我们的链接警报通道也已部署:

Checkly

当我们的开放式银行业务流程中断时,Checkly 现在会向我们发送电子邮件通过电话给我们打电话,让我们能够跳转到一份详细报告,显示此步骤中失败的步骤复杂的流程。这很方便。

总结

糟糕的 API 性能和困难不仅会影响最终用户,还会引起行业标准组织和监管机构的担忧。由于开放银行 API 处理非常敏感的数据,因此持续监控这些流是数据安全和用户满意度的关键。

在这篇博文中,我们提供了有关使用 Playwright 和 Checkly 测试和监控 Klarna Open Banking XS2A API 流程的详细指南。我们还向您展示了如何加密响应以确保安全、设置监控检查、创建故障通知警报通道以及将检查部署到 Checkly 进行定期监控。为了帮助您入门,我们设置了此 GitHub 存储库 .

通过结合 Checkly 和 Playwright,您可以确保您的开放式银行流程正常运行,并在出现问题时及时收到警报。

Comments are closed.