{"_id":"@craigmcc/oauth-orchestrator","_rev":"5-ca1917acf71b067042d5ff4ffdd81a8c","name":"@craigmcc/oauth-orchestrator","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.0":{"name":"@craigmcc/oauth-orchestrator","version":"1.0.0","description":"Orchestration for basic OAuth2 server side processing.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch --preserveWatchOutput","test":"mocha  src/**/*.test.ts","test:watch":"mocha --watch src/**/*.test.ts","tsc":"tsc"},"repository":{"type":"git","url":"git+https://github.com/craigmcc/basic-oauth-orchestration.git"},"keywords":["node","oauth2","typescript"],"author":{"name":"Craig McClanahan","email":"craigmcc@gmail.com"},"license":"Apache-2.0","bugs":{"url":"https://github.com/craigmcc/basic-oauth-orchestration/issues"},"homepage":"https://github.com/craigmcc/basic-oauth-orchestration#readme","devDependencies":{"@types/chai":"^4.2.14","@types/mocha":"^8.2.0","@types/node":"^14.14.12","chai":"^4.2.0","mocha":"^8.2.1","nodemon":"^2.0.6","ts-node":"^9.1.1","typescript":"^4.1.3"},"dependencies":{},"gitHead":"ca2282a7ce8d3c1243b1ef589e9f51f69c3e565a","_id":"@craigmcc/oauth-orchestrator@1.0.0","_nodeVersion":"14.15.0","_npmVersion":"6.14.9","dist":{"integrity":"sha512-ls3DMAaFn3GThyd7kOxjlIb6Tcy9ass+T4DjVPZL+E+DFlyZjJx9XBYlSRmBsiOn5Wxs6AB7UUemqSuwk/UgNA==","shasum":"f8938efa04471f191799586b17b5952a20d07b35","tarball":"https://registry.npmjs.org/@craigmcc/oauth-orchestrator/-/oauth-orchestrator-1.0.0.tgz","fileCount":50,"unpackedSize":187830,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf2tAdCRA9TVsSAnZWagAA6kIQAJbeSmUeEqcneWk/stJR\nIyPHcJjdXo8IhB+qC5CjkpgiAmIxrBrzzzyAvV8Cfw3jIQ5KFX3k1dBAnY/B\nH8Ortv0UdVHJmCBtlFyJ5M62YUQY01H0nkDgjISUkqS5onPKvrpFwg/i1/Uu\nUA3LVwsJObas5qrqcS1fQRZW0Qv2oD41tyv8Kbxb6RcCf5CxJaFHyUkjJ9Wu\nMoIysZO7zSEqOQKOindLksxL8PGumGxk8O9JzclBlH6gT9XwSXZnLW1zSkQ0\n3d3Aie86POeIWss+8dTD8WzyUpZpkWDRDbWLqo8eTVbHV0CkawSfNJY0O5A3\nDuqyONtsQzQY7O4ydOjYysRvho661Sj0ZSa+BYgDNOMjZQrxL0dHUsX9YR/f\ndjQTYiyKSBe4z/J79SRmGlTTqLxfspvj2yN0jt1wW+tb6j2yQ8MTZBrXlgyL\nREQxC4u8+a1lxeE73NqUCORGdUmfofr1g6QbdPWP2mEstj5dRU0lsOs5u/E1\nJm2Z9Dadq3J1iXclb/XFmLVRHEH8kI/gAwjA/ecAiEDIsQrO4gSs6yzlgvda\n+nM/3D4p9PLXzjRp2IweWub/dQBWqtjufphvVDZ5Oa5PGVD4Vgwr4EGGThof\nrExYM3r7OXdJSckkx37RJhw7++u0URHBaY/8TjGgIKZm7I3oR3j9StiyntKN\nvFPV\r\n=uWYn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAZM/edZzm3C1dgc/2Ba0Idvj1fnqXBXDCOeDz9bZuPzAiB3RXKv/tOpUe0DvZzhTy41oytGrdRNmIoLXg+yGxSesQ=="}]},"_npmUser":{"name":"craigmcc","email":"craigmcc@gmail.com"},"directories":{},"maintainers":[{"name":"craigmcc","email":"craigmcc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/oauth-orchestrator_1.0.0_1608175644582_0.229611163864752"},"_hasShrinkwrap":false},"1.0.1":{"name":"@craigmcc/oauth-orchestrator","version":"1.0.1","description":"Orchestration for basic OAuth2 server side processing.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch --preserveWatchOutput","test":"mocha  src/**/*.test.ts","test:watch":"mocha --watch src/**/*.test.ts","tsc":"tsc"},"repository":{"type":"git","url":"git+https://github.com/craigmcc/basic-oauth-orchestration.git"},"keywords":["node","oauth2","typescript"],"author":{"name":"Craig McClanahan","email":"craigmcc@gmail.com"},"license":"Apache-2.0","bugs":{"url":"https://github.com/craigmcc/basic-oauth-orchestration/issues"},"homepage":"https://github.com/craigmcc/basic-oauth-orchestration#readme","devDependencies":{"@types/chai":"^4.2.14","@types/mocha":"^8.2.0","@types/node":"^14.14.22","chai":"^4.2.0","mocha":"^8.2.1","nodemon":"^2.0.7","ts-node":"^9.1.1","typescript":"^4.1.3"},"dependencies":{},"gitHead":"1e42e094830bcfed10af71d29b494ed3c2eece4d","_id":"@craigmcc/oauth-orchestrator@1.0.1","_nodeVersion":"14.15.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-mmBqjZGZteWJUH28jdp7Bnh/keOHuZG8N6Tn3XqqV8u/rJiltO6nKFLUtYUzp4rbNa8r7n+oIBvkeMQI4ECQtw==","shasum":"83edbacec64037d7ba46cd6363c17eee9571adcb","tarball":"https://registry.npmjs.org/@craigmcc/oauth-orchestrator/-/oauth-orchestrator-1.0.1.tgz","fileCount":50,"unpackedSize":187384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgCeeFCRA9TVsSAnZWagAA30wP/2t+sG1TBdenvC7wFlQv\nLPu7RKfYdWvdyB5nDgdgP8J2lBGkdxGddc5ySq9a4Cuoj9805+Td5XeaZRT3\nzy/l31Hxfv3Bseo6FepZtYwpbKlllRi7cZozNO9qI9DlcOZ5qJzBpfW0ycTW\nqC2MSy+seBwFWWu+azUZCVVB7PcFKRJg4BVxwNTHB4+yJawSyFkn4y+8tEZ1\ngR54dW8SbiOEpsjT/NKfMmidoAWiqVFMUGxJXVrASylaeBs0kIs52bu5eWxZ\nRIK+Y7wGGcGHZjTgD1cN9usMI5tDogXpCY2tbj4WhL7swJglT720rXjUhL5F\noqJh70Op4HnujPHKPsD1FXoHepaxioxjM2oJ2f0I339EKekJ9X9qHRacaW6V\nNTodvJ8Ab+IodTpX8KGqzdspnbaT0iv9apO6UTfNS2dtZPh5jcQ6NRobipgD\nZT75H0ZO98/8iP7spp36uCx5puvv/jKLqNSTQJJk6vsChHGIdHCgvVPFWMO8\ngXC7gpq43ntkCFuaEoJ6Dx90dUryEWJTDr7s71k3IBXP98PCzJUadjLWv8mL\n5lgZHB0WH6FTrg8qhSrEUhjfLKJE7nGOd/60rsYAFS1z5Ji1P4wbO84zp7f2\nTz2+tBeLhCKj33kD9kKXPnEVCz4CyJRdhhv4alk9gFdmqZe/M69FDbPTDEEf\nYlbH\r\n=P46J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFuYI7Z+1qCrM0PRbISi5BDTsMaMI0kX9670GHiZJWtlAiEArcxs7/Z6ChWzD5wStZe4zqPzELwNSIoBe7va8iomM3s="}]},"_npmUser":{"name":"craigmcc","email":"craigmcc@gmail.com"},"directories":{},"maintainers":[{"name":"craigmcc","email":"craigmcc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/oauth-orchestrator_1.0.1_1611261828593_0.6469060767236305"},"_hasShrinkwrap":false},"1.0.2":{"name":"@craigmcc/oauth-orchestrator","version":"1.0.2","description":"Orchestration for basic OAuth2 server side processing.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch --preserveWatchOutput","publish":"npm publish --access public .","test":"mocha  src/**/*.test.ts","test:watch":"mocha --watch src/**/*.test.ts","tsc":"tsc"},"repository":{"type":"git","url":"git+https://github.com/craigmcc/basic-oauth-orchestration.git"},"keywords":["node","oauth2","typescript"],"author":{"name":"Craig McClanahan","email":"craigmcc@gmail.com"},"license":"Apache-2.0","bugs":{"url":"https://github.com/craigmcc/basic-oauth-orchestration/issues"},"homepage":"https://github.com/craigmcc/basic-oauth-orchestration#readme","devDependencies":{"@types/chai":"^4.3.0","@types/mocha":"^10.0.0","@types/node":"^18.11.9","chai":"^4.3.6","mocha":"^10.1.0","nodemon":"^2.0.20","ts-node":"^10.9.1","typescript":"^4.5.5"},"gitHead":"6d5f3d1854659dec3b3773708a4c3b665e1a754a","_id":"@craigmcc/oauth-orchestrator@1.0.2","_nodeVersion":"16.11.1","_npmVersion":"8.0.0","dist":{"integrity":"sha512-3NkwT4PxAn92zpbIVkqSk8YPU9Wj7EM4GxMCNZgEkNVlqiMBd7/T3IzAlao9p9iVvymTJycGptR3SGnhsyWkRA==","shasum":"093f7c6194580d85bb160b07ac7aa1ae549a465c","tarball":"https://registry.npmjs.org/@craigmcc/oauth-orchestrator/-/oauth-orchestrator-1.0.2.tgz","fileCount":52,"unpackedSize":188618,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICUxMWZskZhW9h9YhjKBz9tIKlRVcZuf4NuH7Pqp0+aiAiA1hWL4DZu6kvARWjJlkMRZNCIlw1id5n2+IkRpIh1TLg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjft5mACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqefg/9GPbjVTqVTbZxyQa/jpE1QnzO8ixmvaa3vzLESEkh7671v/MF\r\nz5AZ2lCXf9zcmtqpULaFieLh0yywvkZ2odVtlP5PEZHTS6OJLFMthFdIsRbN\r\nDvmQsFOqpvEfbgqlZ5w68j173DSkijIZKwIQNC2W3pm456H/ETzT2wZeBBs+\r\nprDGjjtw5OGpL8ixmQPx+aZrtpdjjHMqvSWfj4HAEplpD/R8dd0293/EQHDw\r\nCAjEqczTpe9Bm0MRH3VVY8hi3gKSGitp+nU5Iaa92Ij3/BcqrGg3WSpf9BpU\r\newpN6ZvP8QX2hJvLi24dEue43UBdkeg4mR2hGYgU/Y9BYdYkFSvYSycN5NAH\r\nH27A2VyqcDIl1yiQBmZagh3LCEU1YnaXl8aes6W7vlnYjU13KUwyC48DbLBy\r\nv9s/sqPF+UJba5SE5+VJTN/r3x2kd+Unm2+yqjjP3Q49UYmpkemOXUbHgapl\r\nJxduwh6FLNVAlmQg2w8/fLQEotQa5Baq7pydjlXjxxD+rA+zdhLVEpWB1HTB\r\n0aCVY6Eb52kUYA112GwGXLuDD+t0dQfjNWy8xDGFoREZOB+v7ibPLkekG4CQ\r\nZaqWYeFhC3HpD3D5hO53g2H4LN/n8Vq3XT7tQCNTsBONtbN7Sp1INYAr9Qb6\r\nWS0hgSMdjZ0NTN2ICGMzEmQ9yxOfn71NX+I=\r\n=A1uc\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"craigmcc","email":"craigmcc@gmail.com"},"directories":{},"maintainers":[{"name":"craigmcc","email":"craigmcc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/oauth-orchestrator_1.0.2_1669258854395_0.8399400443146714"},"_hasShrinkwrap":false},"1.0.3":{"name":"@craigmcc/oauth-orchestrator","version":"1.0.3","description":"Orchestration for basic OAuth2 server side processing.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch --preserveWatchOutput","publish":"npm publish --access public .","test":"mocha  src/**/*.test.ts","test:watch":"mocha --watch src/**/*.test.ts","tsc":"tsc"},"repository":{"type":"git","url":"git+https://github.com/craigmcc/basic-oauth-orchestration.git"},"keywords":["node","oauth2","typescript"],"author":{"name":"Craig McClanahan","email":"craigmcc@gmail.com"},"license":"Apache-2.0","bugs":{"url":"https://github.com/craigmcc/basic-oauth-orchestration/issues"},"homepage":"https://github.com/craigmcc/basic-oauth-orchestration#readme","devDependencies":{"@types/chai":"^4.3.0","@types/mocha":"^10.0.0","@types/node":"^18.11.9","chai":"^4.3.6","mocha":"^10.1.0","nodemon":"^2.0.20","ts-node":"^10.9.1","typescript":"^4.5.5"},"gitHead":"0dea773b90bbf184661e2e34399afb7ae01a0107","_id":"@craigmcc/oauth-orchestrator@1.0.3","_nodeVersion":"16.11.1","_npmVersion":"8.0.0","dist":{"integrity":"sha512-kdnkkD/Tccom+Azq3x6aK1PgaBBLQYEYR47L3n/FV2irMx+ekYSWGpNFDzb1uhv5QhKHgNGKAzbTdV7Imc87oA==","shasum":"300a46eccb0bb63f6ba88bc254d4137dc65af0fd","tarball":"https://registry.npmjs.org/@craigmcc/oauth-orchestrator/-/oauth-orchestrator-1.0.3.tgz","fileCount":52,"unpackedSize":188618,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICE+pHMM4ZTzeyDhEWVK7GGfHq0N9Vog0oKwWNoq/GNEAiBnMYpZ2aUfPDnwdX+i7xuioQkS7jsuDWXn1VIqHBopCQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj3dEmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpu5hAAni6wSl5gV44Nkr4iiOxtRBYt8Bv5eVB5wvZ3O3SsF/dk+1M1\r\nakMkht3EsKF4k095MTOcLQ0T5GqAsAAzIDrCdNLBRWy999ccDJpuZwz7Qp5m\r\nvf9tttVRZf3P3jC/ZECv4H4095t7006Qhth7jQvcHUh8S9ao2f1fZU4GpyMs\r\njc2UWNGzEih0osp9TVkoHhjdvLqcv1c/5a5x1joDnUoO5mT2M/3I4tSL+Iju\r\nuNh/AdL9yfDL0kaIP/8EsLXdzmsL3JKr3BV00isysNFpjXqkOOKzPA3w0bWq\r\nJz1fYBPBmUmOoksOAeojFB5N/9ckRAt741RooYfldRvRwywnJSSGN5gorrcu\r\nehrVdE8IsihJ1eKzxiAcmTu0YbEwPZQ2b4cxj+bDl/Pew+aVrdYH/7MgrAoN\r\nUzutlApE0vWHUd2GdcY7jA7PRkDYpJYvdmDDzzp7LYM84rTkvVHxjd2lv1yM\r\nkgXSWKOtCTltMzG5U2dv+8LIgwmjiedC1MjI+N75IgjqGP+QF3i31Z7TYtVa\r\nXy5LnTQOA9LV6b7M+861Yti5aS9N5tcUvBu1rxFO4lRjIl9NtY9/C3KJHce4\r\n5HnqCqFPsVkZihvREIoE5iu6IkVt9YkaqcCFSBX/B/HcGiBA+nlpPmHD5xxl\r\nHIMmM4StCKwFUkT4/FA10eVIcitpUoQuCWY=\r\n=1J3X\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"craigmcc","email":"craigmcc@gmail.com"},"directories":{},"maintainers":[{"name":"craigmcc","email":"craigmcc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/oauth-orchestrator_1.0.3_1675481381798_0.3532315111011062"},"_hasShrinkwrap":false},"1.0.4":{"name":"@craigmcc/oauth-orchestrator","version":"1.0.4","description":"Orchestration for basic OAuth2 server side processing.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","build:watch":"tsc --watch --preserveWatchOutput","publish":"npm publish --access public .","test":"mocha  src/**/*.test.ts","test:watch":"mocha --watch src/**/*.test.ts","tsc":"tsc"},"repository":{"type":"git","url":"git+https://github.com/craigmcc/basic-oauth-orchestration.git"},"keywords":["node","oauth2","typescript"],"author":{"name":"Craig McClanahan","email":"craigmcc@gmail.com"},"license":"Apache-2.0","bugs":{"url":"https://github.com/craigmcc/basic-oauth-orchestration/issues"},"homepage":"https://github.com/craigmcc/basic-oauth-orchestration#readme","devDependencies":{"@types/chai":"^4.3.0","@types/mocha":"^10.0.0","@types/node":"^18.11.9","chai":"^4.3.6","mocha":"^10.1.0","nodemon":"^2.0.20","ts-node":"^10.9.1","typescript":"^4.5.5"},"gitHead":"29c4b88473f4f535ad8fb4de44301f5f7c47d8e9","_id":"@craigmcc/oauth-orchestrator@1.0.4","_nodeVersion":"16.11.1","_npmVersion":"8.0.0","dist":{"integrity":"sha512-cV55AAG1AIu1uqEuRNuofnLsI1JuHEwYkjvjYGqJVX1Zs86jl1wiVE1UC/pPB5//E7+0dMdew79AAcZEgFzI/w==","shasum":"1d1009a6856360d79886b776eacc857ba4a5d6ae","tarball":"https://registry.npmjs.org/@craigmcc/oauth-orchestrator/-/oauth-orchestrator-1.0.4.tgz","fileCount":52,"unpackedSize":188619,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwDl0Qhlvp1xn5ihmh019sy88xztEubX1YbaxN/W5IIAIhAJeIiSeyl+dzDs1LjVavMEDfIsEwQBEUsH1l9mkZqpCy"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj4sPEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoOjQ/+OGHLwT7RYZ1GdN+10sK9v+e1OTE+JorfRZf8xnUJhLI/1e7P\r\n/IWBX/VJetx1xOyFaZLmvtvgM34eT8V8vYbk0cBmM1lnWn/Xn3Pro9PueQPl\r\ndPQ7fLcu2Lra17CAAzZUJQMqw87yIcnyGcqs264jn8Ac2aBge4Icn++HEoFG\r\n9+yPybddCOl853N5nLZiNRXF7TWfetlRDMQmRii2TMQaWxJcFs6HIM+W4Fsn\r\nU0J7+IgpjDn9Aw+aTqpGg9qz3hO4OW8ZHTgtSBAbOwlx+2vUYpI+xByarGre\r\nOOG4ih6yNBp/9XLnwGUTAUKcBPbI2xdHArcw1jDS/ZVXtRyZaPlLDFQfewaW\r\nWdyKWoouruBTvgpZ6qD5/Z24VflIXHnAA8++v+arCqluOXy3Y4FCb2FJNppi\r\nxdrk+ip72nN6J2xop//1XZpnPa8aTN6pthLQm0tgBjDi9+9x2iXQqkN4JZmh\r\neg84MY5Q6DA9u/1KqwCRvvb0eH/u0sMEh/ScGb8oaIJNkR5Ojab8Q3tgDa2U\r\nJuNsSPDCTZ4xMmjMMtWdzg/ir8wGmQujEW07hU1kr/QPGOE9yjdjPdS1kl/T\r\ndiQ6/VrGoDzlw5uHJjl1rVY2CVLIx03OIypLew1AgGd7vwt6MJwAGkVyRWzK\r\nRBAciyqFRcTd67eQS/+k2yFAIe/ZXlmKk+o=\r\n=u6DG\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"craigmcc","email":"craigmcc@gmail.com"},"directories":{},"maintainers":[{"name":"craigmcc","email":"craigmcc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/oauth-orchestrator_1.0.4_1675805636195_0.2856356886352409"},"_hasShrinkwrap":false}},"time":{"created":"2020-12-17T03:27:24.528Z","1.0.0":"2020-12-17T03:27:24.836Z","modified":"2023-02-07T21:33:56.445Z","1.0.1":"2021-01-21T20:43:48.698Z","1.0.2":"2022-11-24T03:00:54.593Z","1.0.3":"2023-02-04T03:29:41.961Z","1.0.4":"2023-02-07T21:33:56.351Z"},"maintainers":[{"name":"craigmcc","email":"craigmcc@gmail.com"}],"description":"Orchestration for basic OAuth2 server side processing.","homepage":"https://github.com/craigmcc/basic-oauth-orchestration#readme","keywords":["node","oauth2","typescript"],"repository":{"type":"git","url":"git+https://github.com/craigmcc/basic-oauth-orchestration.git"},"author":{"name":"Craig McClanahan","email":"craigmcc@gmail.com"},"bugs":{"url":"https://github.com/craigmcc/basic-oauth-orchestration/issues"},"license":"Apache-2.0","readme":"# oauth-orchestrator\n\nBasic implementation of OAuth 2.0\n[RFC 6749](https://tools.ietf.org/html/rfc6749), and the corresponding\nusage of Bearer tokens for authorizing incoming requests\n[RFC 6750](https://tools.ietf.org/html/rfc6750).\nIt provides back end functionality, suitable for integration with a\nNode-based web application.\n\nIt supports the **Resource Owner Password Credentials Grant** (Section 4.3)\nand the **Refreshing an Access Token** (Section 6) flows.\n\nThe primary focus is supporting use cases where (in RFC 6749 terminology)\nthe **Authorization Server** (which validates passwords and gives out\naccess and refresh tokens) and the **Resource Server** (which authorizes\nincoming requests and performs your application logic) are in the same\nweb application.  However, it would be straightforward to split these\nresponsibilities into different servers with support for some\nback-channel communication between the two.\n\n## 1. Installation\n\n```bash\nnpm install @craigmcc/oauth-orchestrator\n```\n\n## 2. Features\n\n- Supports *password* and *refresh* token grants for Authorization Server\n  implementations.\n- Supports an `authorize()` method for the Resource Server\n  to verify the validity of an access token (is it a valid\n  token, has it expired, does it possess the scope required for the\n  application function being requested) on each request to a protected\n  API endpoint.\n- Orchestrator is **agnostic** about application-specific concerns, such as:\n  - Where and how user information is stored.\n  - Where and how access token and refresh token information is stored.\n  - What HTTP framework might be in use.\n- Instead, an application integrating this library must provide a small\n  set (6) of handler functions to provide concrete integrations of\n  necessary features.\n\nAuthenticating client applications (via client_id and\nclient_secret properties) is not currently supported.   This is\na likely future addition, but to maintain backwards compatibility\nit will likely remain optional.\n\n## 3.  Technologies\n\nIn contrast to many of the OAuth packages currently available for\nNode-based web applications, Orchestrator strives to be minimalist\nin its requirements.  Indeed, if you peruse the `package.json` file,\ndefining it, you will note that there are **zero** runtime dependencies.\nIt also presumes that a reasonably current set of technologies are available.\n\nAs such, the following technologies form the basis of this library:\n- [NodeJS](https://nodejs.org/en/).  I use version 14.15 or later,\n  although it may work with previous versions.\n- [Typescript](https://www.typescriptlang.org).  Coming out of a\n  primarily Java-based software development career, the object\n  orientation and error catching was very comfortable.  I use\n  version 4.1 or later, transpiling to target **es6** by default.\n  The library should (of course) be usable in pure Javascript\n  environments as well.\n- [Mocha](https://mochajs.org/)  and [Chai](https://www.chaijs.com/)\n  for testing.  This was mostly personal preference, but I like\n  the robust support for async/await based functions out of the\n  box, as well as the cleaner output formats than some other\n  testing libraries.\n\nNo web framework is defined as a dependency - it is up to the\nhandler functions provided by your integration to adapt to\nwhatever request/response support your web framework offers.\nI use [Express](https://expressjs.com) for my own personal\nprojects, where the middleware support makes integration for\nauthorization calls very easy, but it should be possible to\nuse other frameworks as well.\n\n## 4.  Integration Steps:\n\n### 4.1 Developer Notes\n\nFor Typescript-based applications, all the type definitions\nmentioned below are exported by the library, so you can say\nthings like this in your application:\n\n```typescript\nimport {\n  AccessToken,\n  RefreshToken,\n  User } \nfrom \"@craigmcc/oauth-orchestrator\";\n```\n\nFor non-Typescript-based applications, you cannot reference the\ntype definitions in your code, but they are available to read in\na liberally commented text file, which (after installation) will be at\n*node_modules/@craigmcc/oauth-orchestrator/types.d.ts* relative\nto your project directory.  This will help you get the parameter types\nand return values right on your handler function implementations.\n\n### 4.2 Create Persistent Storage Implementations\n\nYour application is responsible for providing persistent storage\n(typically in a database, but that is up to you) for the following\nobject types:\n\n**AccessToken** - Access tokens that are created\n  or retrieved by the Orchestrator.\n\n```typescript\nexport interface AccessToken {\n    expires: Date;              // Timestamp when this token expires\n    scope: string;              // Scope granted to this token\n    token: string;              // Actual access token value\n    userId: Identifier;         // User this token is associated with\n}\n```\n\nThe *token* field of an access token is an opaque string that is\nsend on each request to the Resource Server.  They expire at\na certain time, and can be refreshed or revoked (which is\neffectively a logout operation).\n\nTo minimize security risks, you should\ngenerate reasonably long random character strings as token values.\n\n**RefreshToken** - Refresh tokens that are created\n  or retrieved by the Orchestrator.\n\n```typescript\nexport interface RefreshToken {\n    accessToken: string;        // Access token value this refresh token is for\n    expires: Date;              // Timestamp when this token expires\n    token: string;              // Actual refresh token value\n    userId: Identifier;         // User this token is associated with\n}\n```\n\nIf configured (and it is by default), a user who successfully\nauthenticates will receive both an access token and a refresh\ntoken.  The refresh token will generally have a much longer\nlifetime than an access token, and can (when the access token\nnears its expiration) be exchanged for a new access token and\nrefresh token, without requiring the user to be authenticated\nagain.\n\n**User** - Instances of all users allowed to be authenticated.\n\n```typescript\nexport type Identifier = string | number;\n\nexport interface User {\n    scope: string;              // Space-separated scopes granted to this user.\n    userId: Identifier;         // Unique user identifier.\n}\n```\n\nFor maximum flexibility, an **Identifer** is either a string\nor a number.\n\nThis is all that the Orchestrator needs to keep track of about\nusers, once they have been authenticated.\n- **scope** - A space delimited maximum list of permissions that\n  this user will be granted when they authenticate.  (They can\n  ask for fewer permissions if they want, but that is optional).\n- **userId** - An opaque identifier for this user (typically\n  it is the primary key for your user table, but can be\n  whatever you want as long as it is unique between users).\n  This identifier is used to tie together the tokens that have\n  been granted to this user.\n\nIf you have a much richer model of what a \"user\" is in your\napplication, that is fine.  Because Orchestrator does not\ncare about these details, all you have to do is satisfy the\nobject definition above in order to operate successfully with it.\n\nNote in particular that there are no *username* or *password*\nfields included in this object.  Orchestrator does not need to\nknow or care about those details.  If you use the Password Grant\napproach to OAuth, you'll be requiring the user to specify\nusername and password to perform the authentication, but after\nthat they no longer matter, and are not kept inside.\n\nIndeed, even the mechanism that your application uses\nto perform this authentication is totally up to you as well.\n\nWe will get to how your Resource Server can leverage the `authorize()`\ncapability to check for access on each request later, after we\nset up the Authorization Server integration.\n\n### 4.3 Create Required `OrchestratorHandlers` Object and Handler Implementations\n\nThe definition of an `OrchestratorHandler` is pretty straightforward.\nIt is merely a list that maps implementation-specific handlers to\nnames that the Orchestrator knows, so that it can call out to your\nmethods as needed.  The Typescript-y version of this object is:\n\n```typescript\nexport interface OrchestratorHandlers {\n    authenticateUser: AuthenticateUser;\n    createAccessToken: CreateAccessToken;\n    createRefreshToken: CreateRefreshToken;\n    retrieveAccessToken: RetrieveAccessToken;\n    retrieveRefreshToken: RetrieveRefreshToken;\n    revokeAccessToken: RevokeAccessToken;\n}\n```\n\nIt is likely easiest to create this object in a separate file\n(MyOrchestratorHandlers.ts or whatever), which also includes the\nnon-exported implementations of each handler function.  For more\ncomplex scenarios, you might prefer to separate the handlers into\ntheir own individual files.\n\nNote that all the handler functions return Promises, and are\ntherefore expected to be *async* functions.  Fortunately, pretty\nmuch any libraries you need for database access, password hashing,\nand your web framework will be very comfortable with this.\n\nAs a personal preference, I like using \"await\" over then/catch chains\n(with try/catch blocks to deal with errors as needed), but that style\nchoice us up to you.  There is no support for the older Javascript\nstyle of appending a callback function to the parameters.\n\nThe implementation requirements for each handler are described in the\nfollowing sections (along with the Typescript signature, with\ndefinitions of the parameters and return type).\n\n#### 4.3.1 Authenticate User\n\n```typescript\nexport type AuthenticateUser\n    = (username: string, password: string)\n    => Promise<User>;\n```\n\nFor the Password Grant flow in OAuth, it is presumed that your\napplication will have some sort of login screen that asks for\nusername and password.  These are then submitted to the Orchestrator\nin order to ask the AuthenticateUser handler to actually authenticate\nthese credentials.\n\nThe most common approach to this is to have your application\nprovide an API endpoint (often `/oauth/token` but this\nis up to you) with incoming data that looks like a\n`PasswordTokenRequest` that triggers a call to the\n`AuthenticateUser` handler.  Per the OAuth specification,\nthis request should have property names matching those in\n`PasswordTokenRequest`, with content type\n**application/x-www-form-urlencoded** (i.e. the standard\nformat for form submission).\n\nIf authentication is successful, your handler should return a\n**User** object, as described above. Orchestrator will then\nuse that object to create an access token and (optional, but\nturned on by default) refresh token, which will be returned\nas a `TokenResponse` object (in JSON format).\n\nIf authentication fails (invalid username or password) an\n`InvalidGrantError` will be returned to you, with the underlying\nerror included in the *inner* property.  To avoid showing\npotentially sensitive information to client callers, this\nproperty should be suppressed in any response sent back to\nthe calling application.\n\nIMPLEMENTATION NOTE:  It is **strongly** recommended that you\ndo not store plaintext passwords in a database!  Instead,\nencrypt them with a one-way hash function (I like bcrypt but\nthe choice is yours), and implement your authentication handler\nto verify the submitted password against the hashed version\nretrieved from your database.  Just be sure that you use the\nsame algorithm for hashing and verifying.\n\n#### 4.3.2 Create Access Token\n\n```typescript\nexport type CreateAccessToken\n    = (expires: Date, scope: string, userId: Identifier)\n    => Promise<AccessToken>;\n```\n\nThis handler will be called whenever the Orchestrator needs\nto create a new access token.  This will happen in\nthe following scenarios:\n- A newly logging in user is successfully authenticated,\n  and needs to receive an access token for use on all\n  the subsequent API requests for that user.\n- An existing logged in user has a valid access token, but\n  it is approaching the end of its life.  The client\n  application recognizes this situation, and uses the\n  refresh token it previously received to trigger\n  creation of a new access token and (optional)\n  refresh token.  As a side effect, the old tokens will\n  be revoked so that they are no longer valid.\n\nThe returned access token will be returned to the user,\nand then it's *token* value will be sent with each\nincoming API request, in order to prove the user's\nidentity has been confirmed, and to validate whether\nthe user is allowed to perform the operation being\nrequested (by comparing the *scope* assigned to this\ntoken to that required by the requested operation).\n\nIf your application has problems storing or returning this\ntoken, simply throw an appropriate Error.\n\n#### 4.3.3 Create Refresh Token\n\n```typescript\nexport type CreateRefreshToken\n    = (accessToken: string, expires: Date, userId: Identifier)\n    => Promise<RefreshToken>;\n```\n\nThis handler will be called if Orchestrator is configured\nto return refresh tokens (it is by default), and a refresh token\nwill be created via this call, at the same time that a new\naccess token was created.\n\nIf your application has problems storing or returning this\ntoken, simply throw an appropriate Error.\n\nIf your application turns off refresh token creation, it will\nnot be possible to use such a token to receive new tokens.\nWhen the user's access token eventually expires, calls to\nthe `authorize()` Orchestrator method will start failing\n(if you follow best practices, an HTTP 401 status will be\nreturned to the user), after which they will need to\nreauthenticate to continue using the application.\n\n#### 4.3.4 Retrieve Access Token\n\n```typescript\nexport type RetrieveAccessToken\n    = (token: string)\n    => Promise<AccessToken>;\n```\n\nThis handler will be called whenever an incoming request is\nauthorized (via a call to the `authorize()` method of the\nOrchestrator).  You do not need to worry about checking\nwhether the token has expired or not - Orchestrator will take\ncare of that detail.\n\nIf the requested token does not exist, or your internal\nsystems have problems, throw an approprite Error.\n\nPERFORMANCE NOTE:  This handler will be called **many many**\ntimes - once per API call to a protected resource.  If this\ncreates performance issues, consider using some sort of\nin-memory cache to minimize the number of database calls\nneeded.  Just be sure that you purge the cache entries\nwhen the `RevokeAccessToken` handler is called, to avoid\nstale tokens from being used for additional requests.\n\n#### 4.3.5 Retrieve Refresh Token\n\n```typescript\nexport type RetrieveRefreshToken\n    = (token: string)\n    => Promise<RefreshToken>;\n```\n\nExisting refresh tokens are retrieved by this handler,\nwhenever needed for the Refresh Token flow.\n\nAs usual, if your server environment has problems, or if\nthe requested refresh token does not exist, throw an Error.\n\n#### 4.3.6 Revoke Access Token (and any related Refresh Tokens)\n\n```typescript\nexport type RevokeAccessToken\n    = (token: string)\n    => Promise<void>;\n```\n\nConventionally, your application will offer an endpoint (I like\nto use `DELETE /oauth/token`, but this it is up to you) that\neffectively logs the user off by removing or deactivating the\nspecified access token, along with any associated refresh tokens.\nThe processing logic for this endpoint will call this handler.\nAfterwards, the access token that was used will no longer be\nvalid, and the user will need to log back in again via the\nPassword Grant flow.\n\nWhether you offer such an endpoint or not, you will\nwant to build some sort of scheduled job (daily or whatever) to\nprune expired access tokens (and their corresponding refresh\ntokens, if any).\n\n### 4.4 Create Optional `OrchestratorOptions` To Override Defaults\n\nIf you want to modify some or all of the configuration options\nfor the Orchestrator, you can create an options object that is\npassed to the Orchestrator at instantiation time.  The type\ndefinitions for this object describe the options that can be\nmodified, with default values in square brackets.\n\n```typescript\nexport interface OrchestratorOptions {\n    accessTokenLifetime?: number;       // In seconds [86400, one day]\n    issueRefreshToken?: boolean;        // Issue refresh token with\n                                        // access token? [true]\n    refreshTokenLifetime?: number;      // In seconds [604800, one week]\n}\n```\n\n### 4.5 Instantiate and Configure an `Orchestrator` Singleton\n\nIn the top-level Javascript object that controls your application,\ninclude logic similar to this:\n\n```typescript\nimport { Orchestrator } from \"@craigmcc/oauth-orchestrator\";\nimport { MyOrchestratorHandlers } from \"...wherever...\";\nexport const OAuthOrchestrator: Orchestrator\n  = new Orchestrator(MyOrchestratorHandlers);\n```\n\nThis will make a configured `OAuthOrchestrator` instance available\nto any other part of your application that needs access to it.\n\nIf you want to override some of the configuration properties, pass\na suitable `OrchestratorOptions` object as the second parameter\nto the constructor.\n\n### 4.6 Integrate Authorization Calls In Your Resource Server\n\n#### 4.6.1 Requirements\n\nFor each request to an API that is protected by access restrictions,\nthe goal is to implement a call to the `authorize()` method of the\nOrchestrator.  Two pieces of information are required:\n- The access token value (extracted from the *Authorization*\n  header) for this request.\n- The scope that is required to complete this request.  Knowing this\n  will require an understanding of the scope architecture, to\n  understand what operations are allowed by what scopes.\n\nThe details of how this can be implemented will vary depending on\nthe web framework you are using.  Most such frameworks allow the\nconfiguration of an interceptor of some sort, before the normal\nprocessing of the request, that extracts the access token, and\nknows the scope requirements for that request (based on an\nunderstanding of the scope architecture).\n\n#### 4.6.2 Example Express Middleware\n\nIn a framework like Express, such a technique can be implemented\nby creating \"middleware\" functions that are inserted in the\nprocessing flow for a particular route (or all routes to a\nparticular router, or the entire application).  Using Express\nTypescript declarations, a middleware function that requires\nthe \"admin\" scope might be implemented like this (with supporting\nutility functions shared across middleware implementations):\n\n```typescript\nexport const requireAdmin: RequestHandler =\n    async (req: Request, res: Response, next: NextFunction) => {\n        const token = extractToken(req);\n        if (!token) {\n            throw new Forbidden(\"No access token presented\", \"requireToken\");\n        }\n        const required = \"admin\";\n        await authorizeToken(token, required);\n        res.locals.token = token;\n        next();\n}\n\nconst extractToken = (req: Request) : string | null => {\n  const header: string | undefined = req.header(\"Authorization\");\n  if (!header) {\n    return null;\n  }\n  const fields: string[] = header.split(\" \");\n  if (fields.length != 2) {\n    return null;\n  }\n  if (fields[0] !== \"Bearer\") {\n    return null;\n  }\n  return fields[1];\n}\n\nconst authorizeToken = async (token: string, required: string): Promise<void> => {\n  try {\n    await OAuthOrchestrator.authorize(token, required);\n  } catch (error) {\n    throw error;\n  }\n\n}\n```\n\nNote that the \"required\" scope being specified is a **minimum**\nrequirement.  It is perfectly fine if the access token has been\ngranted wider scope than what is required, but being granted\nless than the required scope will trigger a 403 response.\n\nIn more complex environments, the middleware might need to have\naccess to which information is being requested, which will often\nbe based on the value for a request parameter.  In Express, the\nmiddleware has access to these parameters via `req.params.{name}`,\nwhich can then be used to influence the scope that is being\ndefined as required for this operation.\n\n#### 4.6.3 Express Implementing Per-Route Restrictions\n\nImagine we have a library application, where any user is allowed\nto retrieve information about a particular library, but only an\nadmin is allowed to change it.  The route for the HTTP PUT\nthat supports this function might be configured like this:\n\n```typescript\nLibraryRouter.put(\"/:libraryId\",\n    requireAdmin,\n    async (req: Request, res: Response) => {\n        res.send(await LibraryServices.update(\n            parseInt(req.params.libraryId, 10),\n            req.body\n        ));\n    });\n```\n\nwhere `requireAdmin` is the middleware function we defined above.\nBecause this is placed ahead of the actual update processing,\nit will be called first -- and any attempt by a non-admin to\nperform this operation will have received a 403 response, without\nthe actual business logic of doing the update from having to care\nabout the permissions check.\n\n### 4.7 Client Application Responsibilities\n\n#### 4.7.1 Design Scope Architecture\n\nOne of the most interesting application design tasks is to define\nthe architecture of what *scope* settings should be defined, and\nwhat operations they should allow.\n\nAs a quick review, a scope (in OAuth terms) is a space-delimited\nset of one or more \"permissions\" to do things (other approaches are\npossible, but this is likely to be very common).  In an\nOAuth environment, an access token is associated with such a scope,\nand is typically either:\n- A scope that is associated with the user, based on their\n  role in this application.  This will be used if the request to\n  create an access token does not include a specified scope.\n- A scope that is requested when requesting an access token.\n\nA properly implemented **Resource Server** will deny any request to\nask for more permissions than the user is allowed.  For example,\nan \"admin\" scope might only be assigned to certain individuals, and\nthen be used to limit functionality that a user might try to use.\n\nSimilarly, since a request for an access token might include a\nrequest for a particular scope that is not permitted for that user.\nIn this case, the **Authentication Server** should disallow that request.\n\nScopes can range from simple (for instance, an \"admin\" user versus\na \"regular\" user) to more complex.  Consider an online SaaS app\nthat supports multiple customers, each of which might have their own\nhierarchies of admin and regular users -- but **none** of them should\nbe able to access any of the information for another customer.  This\nrequirement can be implemented in any number of ways.  Using scope\nis an option, but will sometimes require a Resource Server to extract\nrequest parameters from the URL (for example, a customer id), or\n(worse from a performance viewpoint) from data retrieved from the\ndatabase.\n\n### 4.7.2 Implement Login Capabilities\n\nHow does a user request an access token in the first place?  If the\napplication uses the Password Grant flow, this will typically be\nhandled by:\n- Providing a login view that accepts username and password.\n- Formulate a `PasswordTokenRequest` request, and send it to the\n  \"token\" endpoint of the Authorization Server (often `/oauth/token`);\n- Process the returned result, which will typically be:\n  - HTTP Status 200, with a `TokenResponse` response body.  Record\n    the `access_token` and (optional) refresh token, as well as the\n    `scope` assigned to this access token.  You might also choose\n    to record the `expires_in` so that you can warn the user about\n    impending expiration, and/or proactively execute the Refresh\n    Token flow to get a new access token without user intervention.\n  - HTTP Status 401.  Normally this will mean a problem in validating\n    the username and password credentials.  Ask the user to enter\n    them again.\n  - Other HTTP Status.  Probably a server side issue.\n  \nIn any of the non-200 cases, the response body might include a JSON\nstructure such as an `InvalidGrantError`, which will include a\n`message` property with an explanatory message.  However, any such\nresponse means that an access token was not received, so the login\nwas not successful.\n\n### 4.7.3 (Optional) Implement Logout Capabilities\n\nA client application may choose to offer a \"logout\" or \"sign out\"\noption.  Implementing this will typically include:\n- Format a request to the appropriate endpoint that is\n  provided by the server for this purpose.\n- Make sure this request is authorized with the same access\n  token that the user is sending on all normal requests\n  (after all, we don't want the server invalidating access\n  tokens that might actually belong to someone else).\n- Dealing with responses as usual.\n\nIf logout has occurred, the client application should throw away\nany access token information related to this user, so that any\nsubsequent request will trigger a 401 or 403 response that should\nthen trigger having to login again.\n\n### 4.7.4 Include Access Token On Every Protected API Request\n\nWhen sending such a request, include an HTTP *Authorization*\nheader, including the users's access token (if any), like this:\n\n```http request\nAuthorization: Bearer {user-access-token}\n```\n\nNote that the OAuth specification supports a couple of alternative\nways to include the Bearer token (as a query parameter, or as a\nfield in a form submit), but these are problematic from a security\n(query parameters are often logged by the server), or accurate data\nmodelling (polluting the request body with something that is not\nrelated to the actual data being transmitted) perspective.  Therefore,\nthe oauth-orchestrator implementation **only** accepts Bearer tokens.\n\n### 4.7.5 Deal With Authentication or Authorization Responses\n\nIn addition to normal application responses, two particular HTTP\nstatus codes indicate issues with OAuth authentication and\nauthorization issues:\n- HTTP Status 401:  Most likely scenario is that the previously\n  usable access token has expired.  Send the user back to the\n  login mechanism, so they can request a new token.  For extra\n  credit, return them to where they were, perhaps resubmitting\n  the request that failed.\n- HTTP Status 403:  Most likely scenario is that the user submitted\n  an API request that is not allowed by the scope granted to the\n  access token they are using.  This type of request will not be\n  successful unless the user logs in with credentials that allow\n  the required scope.\n\nIn an ideal scenario, the client application design will seek to\nminimize the chances for a 403 response happening, by not even\noffering (through the application's UI) a way to invoke the API\nrequest that is going to fail.  For example, if a user is not\nallowed to request or modify data of a particular type, do not\neven offer the UI that makes such a request possible.\n\nOf course, the Authorization Server is going to enforce scope\nrestrictions under all circumstances, because this application\nmight not be the only one utilizing the server.  Avoiding\nthe possibility of triggering such issues in the first place\nwill lead to a better user experience.\n\nHow do we do this?  As part of the information returned when an\naccess token is received, the scope assigned to that access\ntoken is also returned.  If the client application understands\nthe scope architecture, it can make decisions about what\nfunctions can be offered in the UI, avoiding those that will\ntrigger 403 responses.\n","readmeFilename":"README.md"}