0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 10:51:01 +00:00
hono/deno_dist/middleware/basic-auth/index.ts

71 lines
2.1 KiB
TypeScript
Raw Normal View History

import { HTTPException } from '../../http-exception.ts'
import type { HonoRequest } from '../../request.ts'
import type { MiddlewareHandler } from '../../types.ts'
2022-07-02 06:09:45 +00:00
import { timingSafeEqual } from '../../utils/buffer.ts'
import { decodeBase64 } from '../../utils/encode.ts'
const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/
const USER_PASS_REGEXP = /^([^:]*):(.*)$/
2023-03-19 09:19:01 +00:00
const utf8Decoder = new TextDecoder()
const auth = (req: HonoRequest) => {
2022-07-02 06:09:45 +00:00
const match = CREDENTIALS_REGEXP.exec(req.headers.get('Authorization') || '')
if (!match) {
return undefined
}
let userPass = undefined
// If an invalid string is passed to atob(), it throws a `DOMException`.
try {
userPass = USER_PASS_REGEXP.exec(utf8Decoder.decode(decodeBase64(match[1])))
} catch {} // Do nothing
2022-07-02 06:09:45 +00:00
if (!userPass) {
return undefined
}
return { username: userPass[1], password: userPass[2] }
}
export const basicAuth = (
options: { username: string; password: string; realm?: string; hashFunction?: Function },
...users: { username: string; password: string }[]
): MiddlewareHandler => {
2022-07-02 06:09:45 +00:00
if (!options) {
throw new Error('basic auth middleware requires options for "username and password"')
}
if (!options.realm) {
options.realm = 'Secure Area'
}
users.unshift({ username: options.username, password: options.password })
return async (ctx, next) => {
2022-07-02 06:09:45 +00:00
const requestUser = auth(ctx.req)
if (requestUser) {
for (const user of users) {
const usernameEqual = await timingSafeEqual(
user.username,
requestUser.username,
options.hashFunction
)
const passwordEqual = await timingSafeEqual(
user.password,
requestUser.password,
options.hashFunction
)
if (usernameEqual && passwordEqual) {
await next()
return
}
}
}
const res = new Response('Unauthorized', {
2022-07-02 06:09:45 +00:00
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="' + options.realm?.replace(/"/g, '\\"') + '"',
},
})
throw new HTTPException(401, { res })
2022-07-02 06:09:45 +00:00
}
}