hono
Version:
Web framework built on Web Standards
83 lines (82 loc) • 2.9 kB
JavaScript
// src/middleware/bearer-auth/index.ts
import { HTTPException } from "../../http-exception.js";
import { timingSafeEqual } from "../../utils/buffer.js";
var TOKEN_STRINGS = "[A-Za-z0-9._~+/-]+=*";
var PREFIX = "Bearer";
var HEADER = "Authorization";
var bearerAuth = (options) => {
if (!("token" in options || "verifyToken" in options)) {
throw new Error('bearer auth middleware requires options for "token"');
}
if (!options.realm) {
options.realm = "";
}
if (options.prefix === void 0) {
options.prefix = PREFIX;
}
const realm = options.realm?.replace(/"/g, '\\"');
const prefixRegexStr = options.prefix === "" ? "" : `${options.prefix} +`;
const regexp = new RegExp(`^${prefixRegexStr}(${TOKEN_STRINGS}) *$`);
const wwwAuthenticatePrefix = options.prefix === "" ? "" : `${options.prefix} `;
const throwHTTPException = async (c, status, wwwAuthenticateHeader, messageOption) => {
const headers = {
"WWW-Authenticate": wwwAuthenticateHeader
};
const responseMessage = typeof messageOption === "function" ? await messageOption(c) : messageOption;
const res = typeof responseMessage === "string" ? new Response(responseMessage, { status, headers }) : new Response(JSON.stringify(responseMessage), {
status,
headers: {
...headers,
"content-type": "application/json; charset=UTF-8"
}
});
throw new HTTPException(status, { res });
};
return async function bearerAuth2(c, next) {
const headerToken = c.req.header(options.headerName || HEADER);
if (!headerToken) {
await throwHTTPException(
c,
401,
`${wwwAuthenticatePrefix}realm="${realm}"`,
options.noAuthenticationHeaderMessage || "Unauthorized"
);
} else {
const match = regexp.exec(headerToken);
if (!match) {
await throwHTTPException(
c,
400,
`${wwwAuthenticatePrefix}error="invalid_request"`,
options.invalidAuthenticationHeaderMessage || "Bad Request"
);
} else {
let equal = false;
if ("verifyToken" in options) {
equal = await options.verifyToken(match[1], c);
} else if (typeof options.token === "string") {
equal = await timingSafeEqual(options.token, match[1], options.hashFunction);
} else if (Array.isArray(options.token) && options.token.length > 0) {
for (const token of options.token) {
if (await timingSafeEqual(token, match[1], options.hashFunction)) {
equal = true;
break;
}
}
}
if (!equal) {
await throwHTTPException(
c,
401,
`${wwwAuthenticatePrefix}error="invalid_token"`,
options.invalidTokenMessage || "Unauthorized"
);
}
}
}
await next();
};
};
export {
bearerAuth
};