index.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. const jwt = require("jsonwebtoken");
  2. const UnauthorizedError = require("./errors/UnauthorizedError");
  3. const unless = require("koa-unless");
  4. const async = require("async");
  5. const set = require("lodash.set");
  6. const isFunction = require("lodash.isfunction");
  7. const wrapIsRevokedInAsync = isRevoked => {
  8. return async function(_, __) {
  9. return isRevoked || false;
  10. };
  11. };
  12. function wrapStaticSecretInAsync(secret) {
  13. return async function(_, __) {
  14. return secret;
  15. };
  16. }
  17. module.exports = function(options) {
  18. if (!options || !options.secret) throw new Error("secret should be set");
  19. let secretAsync = options.secret;
  20. if (!isFunction(secretAsync)) {
  21. secretAsync = wrapStaticSecretInAsync(secretAsync);
  22. }
  23. let isRevokedAsync = options.isRevoked;
  24. if (!isFunction(isRevokedAsync)) {
  25. isRevokedAsync = wrapIsRevokedInAsync(isRevokedAsync);
  26. }
  27. const property = options.property || options.userProperty || "user";
  28. const credentialsRequired =
  29. typeof options.credentialsRequired === "undefined"
  30. ? true
  31. : options.credentialsRequired;
  32. const middleware = async function(ctx, next) {
  33. let token;
  34. if (ctx.method === "OPTIONS" && ctx.get("access-control-request-headers")) {
  35. const hasAuthInAccessControl = !!~ctx
  36. .get("access-control-request-headers")
  37. .split(",")
  38. .map(function(header) {
  39. return header.trim();
  40. })
  41. .indexOf("authorization");
  42. if (hasAuthInAccessControl) {
  43. return await next();
  44. }
  45. }
  46. if (options.getToken && typeof options.getToken === "function") {
  47. try {
  48. token = options.getToken(ctx);
  49. } catch (e) {
  50. throw e;
  51. }
  52. } else if (ctx.get("authorization")) {
  53. const parts = ctx.get("authorization").split(" ");
  54. if (parts.length == 2) {
  55. const scheme = parts[0];
  56. const credentials = parts[1];
  57. if (/^Bearer$/i.test(scheme)) {
  58. token = credentials;
  59. } else {
  60. if (credentialsRequired) {
  61. throw new UnauthorizedError("credentials_bad_scheme", {
  62. message: "Format is Authorization: Bearer [token]"
  63. });
  64. } else {
  65. return await next();
  66. }
  67. }
  68. } else {
  69. throw new UnauthorizedError("credentials_bad_format", {
  70. message: "Format is Authorization: Bearer [token]"
  71. });
  72. }
  73. }
  74. if (!token) {
  75. if (credentialsRequired) {
  76. throw new UnauthorizedError("credentials_required", {
  77. message: "No authorization token was found"
  78. });
  79. } else {
  80. return await next();
  81. }
  82. }
  83. let dtoken;
  84. try {
  85. dtoken = jwt.decode(token, { complete: true }) || {};
  86. } catch (err) {
  87. throw new UnauthorizedError("invalid_token", err);
  88. }
  89. const arity = secretAsync.length;
  90. let secret;
  91. if (arity == 4) {
  92. secret = await secretAsync(ctx, dtoken.header, dtoken.payload);
  93. } else {
  94. // arity == 3
  95. secret = await secretAsync(ctx, dtoken.payload);
  96. }
  97. const decoded = await new Promise((resolve, reject) => {
  98. jwt.verify(token, secret, options, function(err, decoded) {
  99. if (err) {
  100. reject(new UnauthorizedError("invalid_token", err));
  101. } else {
  102. resolve(decoded);
  103. }
  104. });
  105. });
  106. const result = await new Promise(async (resolve, reject) => {
  107. let revoked;
  108. try {
  109. revoked = await isRevokedAsync(ctx, dtoken.payload);
  110. } catch (err) {
  111. reject(err);
  112. }
  113. if (revoked) {
  114. reject(
  115. new UnauthorizedError("revoked_token", {
  116. message: "The token has been revoked."
  117. })
  118. );
  119. } else {
  120. resolve(decoded);
  121. }
  122. });
  123. set(ctx.state, property, result);
  124. await next();
  125. };
  126. middleware.unless = unless;
  127. middleware.UnauthorizedError = UnauthorizedError;
  128. return middleware;
  129. };
  130. module.exports.UnauthorizedError = UnauthorizedError;