Files
SnowLuma/.pnpm-store/v11/files/b2/bab492656a390a45bf9c5dcf6bd53e8759d268b2c0e36f4f0add59d7ab467ac41b4748d1670c1c8bdc445d4f30035a544fd5ea9a932612584fd21fe0e2796a
TanTan 2a69b23b47
Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

100 lines
3.7 KiB
Plaintext

import { HTTPException } from "../../http-exception.js";
import { timingSafeEqual } from "../../utils/buffer.js";
import { auth } from "../../utils/basic-auth.js";
//#region src/middleware/basic-auth/index.ts
/**
* Basic Auth Middleware for Hono.
*
* @see {@link https://hono.dev/docs/middleware/builtin/basic-auth}
*
* @param {BasicAuthOptions} options - The options for the basic authentication middleware.
* @param {string} options.username - The username for authentication.
* @param {string} options.password - The password for authentication.
* @param {string} [options.realm="Secure Area"] - The realm attribute for the WWW-Authenticate header.
* @param {Function} [options.hashFunction] - The hash function used for secure comparison.
* @param {Function} [options.verifyUser] - The function to verify user credentials.
* @param {string | object | MessageFunction} [options.invalidUserMessage="Unauthorized"] - The invalid user message.
* @param {Function} [options.onAuthSuccess] - Callback function called on successful authentication.
* @returns {MiddlewareHandler} The middleware handler function.
* @throws {HTTPException} If neither "username and password" nor "verifyUser" options are provided.
*
* @example
* ```ts
* const app = new Hono()
*
* app.use(
* '/auth/*',
* basicAuth({
* username: 'hono',
* password: 'ahotproject',
* })
* )
*
* app.get('/auth/page', (c) => {
* return c.text('You are authorized')
* })
* ```
*
* @example
* ```ts
* // With onAuthSuccess callback
* app.use(
* '/auth/*',
* basicAuth({
* username: 'hono',
* password: 'ahotproject',
* onAuthSuccess: (c, username) => {
* c.set('user', { name: username, role: 'admin' })
* console.log(`User ${username} authenticated`)
* },
* })
* )
* ```
*/
const basicAuth = (options, ...users) => {
const usernamePasswordInOptions = "username" in options && "password" in options;
const verifyUserInOptions = "verifyUser" in options;
if (!(usernamePasswordInOptions || verifyUserInOptions)) throw new Error("basic auth middleware requires options for \"username and password\" or \"verifyUser\"");
if (!options.realm) options.realm = "Secure Area";
if (!options.invalidUserMessage) options.invalidUserMessage = "Unauthorized";
if (usernamePasswordInOptions) users.unshift({
username: options.username,
password: options.password
});
return async function basicAuth(ctx, next) {
const requestUser = auth(ctx.req.raw);
if (requestUser) {
if (verifyUserInOptions) {
if (await options.verifyUser(requestUser.username, requestUser.password, ctx)) {
if (options.onAuthSuccess) await options.onAuthSuccess(ctx, requestUser.username);
await next();
return;
}
} else for (const user of users) {
const [usernameEqual, passwordEqual] = await Promise.all([timingSafeEqual(user.username, requestUser.username, options.hashFunction), timingSafeEqual(user.password, requestUser.password, options.hashFunction)]);
if (usernameEqual && passwordEqual) {
if (options.onAuthSuccess) await options.onAuthSuccess(ctx, requestUser.username);
await next();
return;
}
}
}
const status = 401;
const headers = { "WWW-Authenticate": "Basic realm=\"" + options.realm?.replace(/"/g, "\\\"") + "\"" };
const responseMessage = typeof options.invalidUserMessage === "function" ? await options.invalidUserMessage(ctx) : options.invalidUserMessage;
const res = typeof responseMessage === "string" ? new Response(responseMessage, {
status,
headers
}) : new Response(JSON.stringify(responseMessage), {
status,
headers: {
...headers,
"content-type": "application/json"
}
});
throw new HTTPException(status, { res });
};
};
//#endregion
export { basicAuth };