{
  "info": {
    "_postman_id": "bf42875d-58f0-43ac-947c-4537758fb555",
    "name": "SDWORX-OAuth-Token-Request",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "_exporter_id": "2493693"
  },
  "item": [
    {
      "name": "1 - Discover Token Endpoint",
      "event": [
        {
          "listen": "test",
          "script": {
            "exec": [
              "var json = pm.response.json();",
              "pm.collectionVariables.set('tokenEndpoint', json.token_endpoint);",
              "pm.test('token_endpoint discovered', function() {",
              "    pm.expect(json.token_endpoint).to.be.a('string');",
              "});"
            ],
            "type": "text/javascript"
          }
        }
      ],
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{authority}}/.well-known/openid-configuration",
          "host": [
            "{{authority}}"
          ],
          "path": [
            ".well-known",
            "openid-configuration"
          ]
        }
      },
      "response": []
    },
    {
      "name": "2 - Get Access Token (client_credentials + private_key_jwt)",
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "exec": [
              "(async () => {",
              "  const LIB_URL = 'https://kjur.github.io/jsrsasign/jsrsasign-all-min.js';",
              "",
              "  const log = (...args) => console.log('[private_key_jwt]', ...args);",
              "  const warn = (...args) => console.warn('[private_key_jwt]', ...args);",
              "  const fail = (stage, err) => {",
              "    const message = err && err.message ? err.message : String(err);",
              "    const stack = err && err.stack ? `\\n${err.stack}` : '';",
              "    throw new Error(`[private_key_jwt:${stage}] ${message}${stack}`);",
              "  };",
              "",
              "  try {",
              "    log('Starting private_key_jwt pre-request script');",
              "",
              "    // 1) Download / initialize library",
              "    try {",
              "      if (typeof KJUR === 'undefined' || !KJUR.jws || typeof KEYUTIL === 'undefined') {",
              "        log('Downloading signing library', { url: LIB_URL });",
              "        const res = await pm.sendRequest({ url: LIB_URL, method: 'GET' });",
              "        const status = res && typeof res.code !== 'undefined' ? res.code : 'unknown';",
              "        log('Library download completed', { status });",
              "",
              "        if (!res || res.code !== 200) {",
              "          throw new Error(`Unexpected HTTP status while downloading jsrsasign: ${status}`);",
              "        }",
              "",
              "        const libSource = res.text();",
              "        if (!libSource || !libSource.trim()) {",
              "          throw new Error('Downloaded jsrsasign source is empty');",
              "        }",
              "",
              "        // Patch: jsrsasign references `navigator` which does not exist in the",
              "        // Postman sandbox (Node.js-based). Prepend a shim into the source so",
              "        // it is defined inside the eval scope.\".",
              "        const shimmedSource = 'if(typeof navigator===\"undefined\"){var navigator={appName:\"nodejs\"};}' + '\\n' + libSource;",
              "",
              "            try {",
              "                eval(shimmedSource);",
              "            } catch (evalErr) {",
              "                fail('library-init', evalErr);",
              "            }",
              "      } else {",
              "        log('Signing library already available in sandbox');",
              "      }",
              "",
              "      if (typeof KJUR === 'undefined' || !KJUR.jws || typeof KEYUTIL === 'undefined') {",
              "        throw new Error('jsrsasign globals not available after initialization');",
              "      }",
              "",
              "      log('Signing library initialized', {",
              "        hasKJUR: typeof KJUR !== 'undefined',",
              "        hasJWS: !!(KJUR && KJUR.jws),",
              "        hasKEYUTIL: typeof KEYUTIL !== 'undefined'",
              "      });",
              "    } catch (err) {",
              "      fail('library-download-init', err);",
              "    }",
              "",
              "    // 2) Resolve required variables",
              "    let clientId;",
              "    let tokenEndpoint;",
              "    let privateKeyPem;",
              "",
              "    try {",
              "      clientId = pm.collectionVariables.get('clientId');",
              "      tokenEndpoint = pm.collectionVariables.get('tokenEndpoint');",
              "      privateKeyPem = pm.collectionVariables.get('privateKeyPem');",
              "",
              "      const missing = [",
              "        ['clientId', clientId],",
              "        ['tokenEndpoint', tokenEndpoint],",
              "        ['privateKeyPem', privateKeyPem]",
              "      ]",
              "        .filter(([, value]) => !value || !String(value).trim())",
              "        .map(([name]) => name);",
              "",
              "      log('Resolved variables', {",
              "        clientId: clientId || '(missing)',",
              "        tokenEndpoint: tokenEndpoint || '(missing)',",
              "        privateKeyPemPresent: !!(privateKeyPem && String(privateKeyPem).trim()),",
              "        privateKeyPemLength: privateKeyPem ? String(privateKeyPem).length : 0",
              "      });",
              "",
              "      if (missing.length) {",
              "        throw new Error(`Missing required collection variable(s): ${missing.join(', ')}`);",
              "      }",
              "    } catch (err) {",
              "      fail('required-variables', err);",
              "    }",
              "",
              "    // 3) Normalize / validate PEM without logging secret contents",
              "    let normalizedPem;",
              "    let keyObj;",
              "",
              "    try {",
              "      normalizedPem = String(privateKeyPem)",
              "        .replace(/\\r\\n/g, '\\n')",
              "        .replace(/\\\\r\\\\n/g, '\\n')",
              "        .replace(/\\\\n/g, '\\n')",
              "        .trim();",
              "",
              "      const hasBegin = normalizedPem.includes('-----BEGIN');",
              "      const hasPrivateKeyLabel =",
              "        normalizedPem.includes('-----BEGIN PRIVATE KEY-----') ||",
              "        normalizedPem.includes('-----BEGIN RSA PRIVATE KEY-----') ||",
              "        normalizedPem.includes('-----BEGIN EC PRIVATE KEY-----');",
              "      const hasEnd = normalizedPem.includes('-----END');",
              "      const lineCount = normalizedPem ? normalizedPem.split('\\n').length : 0;",
              "",
              "      log('PEM normalization complete', {",
              "        hasBegin,",
              "        hasEnd,",
              "        hasPrivateKeyLabel,",
              "        lineCount,",
              "        normalizedLength: normalizedPem.length",
              "      });",
              "",
              "      if (!hasBegin || !hasEnd || !hasPrivateKeyLabel) {",
              "        throw new Error('privateKeyPem does not contain a supported PEM private key block');",
              "      }",
              "",
              "      try {",
              "        keyObj = KEYUTIL.getKey(normalizedPem);",
              "      } catch (parseErr) {",
              "        fail('pem-parse', parseErr);",
              "      }",
              "",
              "      if (!keyObj) {",
              "        throw new Error('KEYUTIL.getKey returned no key object');",
              "      }",
              "",
              "      log('PEM parsed successfully', {",
              "        keyType: keyObj.type || '(unknown)',",
              "        curveName: keyObj.curveName || undefined,",
              "        isPrivate: keyObj.isPrivate === true || typeof keyObj.prvKeyHex !== 'undefined'",
              "      });",
              "    } catch (err) {",
              "      fail('pem-normalization-validation', err);",
              "    }",
              "",
              "    // 4) Construct JWT claims / header",
              "    let header;",
              "    let payload;",
              "    let clientAssertion;",
              "",
              "    try {",
              "      const now = Math.floor(Date.now() / 1000);",
              "      const jti = `${now}-${Math.random().toString(36).slice(2)}-${pm.info.requestId}`;",
              "",
              "      // Auto-detect algorithm from key type",
              "      let alg = 'RS256';",
              "      if (keyObj.type === 'EC' || keyObj.curveName) {",
              "        const curveMap = { 'P-256': 'ES256', 'secp256r1': 'ES256', 'P-384': 'ES384', 'secp384r1': 'ES384', 'P-521': 'ES512', 'secp521r1': 'ES512' };",
              "        alg = curveMap[keyObj.curveName] || 'ES256';",
              "        log('Detected EC key', { curve: keyObj.curveName, alg });",
              "      } else {",
              "        log('Detected RSA key', { alg });",
              "      }",
              "",
              "var x5t = pm.collectionVariables.get('x5t');",
              "header = {",
              "    alg: 'RS256',",
              "    typ: 'JWT',",
              "    x5t: x5t,",
              "    kid: x5t",
              "};",
              "",
              "      log('x5t', x5t);",
              "",
              "      header = {",
              "        alg: alg,",
              "        typ: 'JWT',",
              "        x5t: x5t,",
              "        kid: x5t",
              "      };",
              "",
              "      payload = {",
              "        iss: clientId,",
              "        sub: clientId,",
              "        aud: tokenEndpoint,",
              "        jti,",
              "        exp: now + 18000",
              "      };",
              "",
              "      log('JWT claims prepared', {",
              "        clientId,",
              "        tokenEndpoint,",
              "        alg: header.alg,",
              "        typ: header.typ,",
              "        iat: payload.iat,",
              "        exp: payload.exp,",
              "        ttlSeconds: payload.exp - payload.iat,",
              "        jtiPreview: `${String(jti).slice(0, 16)}...`",
              "      });",
              "    } catch (err) {",
              "      fail('jwt-construction', err);",
              "    }",
              "",
              "    // 5) Sign JWT and store assertion",
              "    // NOTE: The request body uses {{clientAssertion}} — this variable name is",
              "    // what we set here. Any reference to {{client_assertion}} elsewhere is a",
              "    // mismatch and should be updated to {{clientAssertion}}.",
              "    try {",
              "      clientAssertion = KJUR.jws.JWS.sign(",
              "        header.alg,",
              "        JSON.stringify(header),",
              "        JSON.stringify(payload),",
              "        normalizedPem",
              "      );",
              "",
              "      if (!clientAssertion || typeof clientAssertion !== 'string') {",
              "        throw new Error('Signing returned an empty or non-string JWT');",
              "      }",
              "",
              "      const segments = clientAssertion.split('.');",
              "      log('JWT signed successfully', {",
              "        segmentCount: segments.length,",
              "        headerPayloadPreview: segments.length >= 2 ? `${segments[0]}.${segments[1].slice(0, 12)}...` : '(unexpected format)',",
              "        totalLength: clientAssertion.length",
              "      });",
              "",
              "      // Set both camelCase and snake_case variants so either body reference works",
              "      pm.collectionVariables.set('clientAssertion', clientAssertion);",
              "      pm.collectionVariables.set('client_assertion', clientAssertion);",
              "      log('Stored clientAssertion (and client_assertion) collection variables', {",
              "        valuePresent: true,",
              "        totalLength: clientAssertion.length",
              "      });",
              "    } catch (err) {",
              "      fail('jwt-signing', err);",
              "    }",
              "",
              "    log('private_key_jwt pre-request script completed successfully');",
              "  } catch (err) {",
              "    warn('Pre-request script failed');",
              "    warn(err && err.message ? err.message : String(err));",
              "    if (err && err.stack) {",
              "      console.error(err.stack);",
              "    }",
              "    throw err;",
              "  }",
              "})();"
            ],
            "type": "text/javascript",
            "packages": {},
            "requests": {}
          }
        },
        {
          "listen": "test",
          "script": {
            "exec": [
              "pm.test('Status code is successful', function () {",
              "    pm.expect(pm.response.code).to.be.oneOf([200, 201]);",
              "});",
              "",
              "var json;",
              "try {",
              "    json = pm.response.json();",
              "} catch (parseError) {",
              "    throw new Error('Response is not valid JSON: ' + pm.response.text());",
              "}",
              "",
              "pm.test('access_token received', function () {",
              "    pm.expect(json.access_token).to.be.a('string').and.not.empty;",
              "});",
              "",
              "if (json.access_token) {",
              "    pm.environment.set('accessToken', json.access_token);",
              "}"
            ],
            "type": "text/javascript",
            "packages": {},
            "requests": {}
          }
        }
      ],
      "request": {
        "auth": {
          "type": "noauth"
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/x-www-form-urlencoded"
          }
        ],
        "body": {
          "mode": "urlencoded",
          "urlencoded": [
            {
              "key": "grant_type",
              "value": "client_credentials",
              "type": "text"
            },
            {
              "key": "client_id",
              "value": "{{clientId}}",
              "type": "text"
            },
            {
              "key": "scope",
              "value": "{{scope}}",
              "type": "text"
            },
            {
              "key": "client_assertion_type",
              "value": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
              "type": "text"
            },
            {
              "key": "client_assertion",
              "value": "{{clientAssertion}}",
              "type": "text"
            }
          ]
        },
        "url": {
          "raw": "{{tokenEndpoint}}",
          "host": [
            "{{tokenEndpoint}}"
          ]
        }
      },
      "response": []
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "packages": {},
        "requests": {},
        "exec": [
          ""
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "packages": {},
        "requests": {},
        "exec": [
          ""
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "authority",
      "value": "https://your-oidc-authority"
    },
    {
      "key": "clientId",
      "value": "39917773-07b13bfbd9954c1c89d794aaf92b54e0"
    },
    {
      "key": "scope",
      "value": "hrssapi"
    },
    {
      "key": "privateKeyPem",
      "value": "value"
    },
    {
      "key": "tokenEndpoint",
      "value": ""
    },
    {
      "key": "clientAssertion",
      "value": ""
    },
    {
      "key": "client_assertion",
      "value": ""
    },
    {
      "key": "x5t",
      "value": ""
    }
  ]
}