0
0
mirror of https://github.com/honojs/hono.git synced 2024-11-21 18:18:57 +01:00

feat(basic-auth): added custom response message option (#3371)

* feat(basic-auth): added custom response message option

* feat(basic-auth): using specific MessageFunction type
This commit is contained in:
Marcel Overdijk 2024-09-11 04:10:17 +02:00 committed by GitHub
parent a86f3cea5f
commit c50be25c9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 121 additions and 7 deletions

View File

@ -83,6 +83,42 @@ describe('Basic Auth by Middleware', () => {
return auth(c, next)
})
app.use(
'/auth-custom-invalid-user-message-string/*',
basicAuth({
username,
password,
invalidUserMessage: 'Custom unauthorized message as string',
})
)
app.use(
'/auth-custom-invalid-user-message-object/*',
basicAuth({
username,
password,
invalidUserMessage: { message: 'Custom unauthorized message as object' },
})
)
app.use(
'/auth-custom-invalid-user-message-function-string/*',
basicAuth({
username,
password,
invalidUserMessage: () => 'Custom unauthorized message as function string',
})
)
app.use(
'/auth-custom-invalid-user-message-function-object/*',
basicAuth({
username,
password,
invalidUserMessage: () => ({ message: 'Custom unauthorized message as function object' }),
})
)
app.get('/auth/*', (c) => {
handlerExecuted = true
return c.text('auth')
@ -110,6 +146,24 @@ describe('Basic Auth by Middleware', () => {
return c.text('verify-user')
})
app.get('/auth-custom-invalid-user-message-string/*', (c) => {
handlerExecuted = true
return c.text('auth')
})
app.get('/auth-custom-invalid-user-message-object/*', (c) => {
handlerExecuted = true
return c.text('auth')
})
app.get('/auth-custom-invalid-user-message-function-string/*', (c) => {
handlerExecuted = true
return c.text('auth')
})
app.get('/auth-custom-invalid-user-message-function-object/*', (c) => {
handlerExecuted = true
return c.text('auth')
})
it('Should not authorize', async () => {
const req = new Request('http://localhost/auth/a')
const res = await app.request(req)
@ -226,4 +280,42 @@ describe('Basic Auth by Middleware', () => {
expect(res.status).toBe(401)
expect(await res.text()).toBe('Unauthorized')
})
it('Should not authorize - custom invalid user message as string', async () => {
const req = new Request('http://localhost/auth-custom-invalid-user-message-string')
const res = await app.request(req)
expect(res).not.toBeNull()
expect(res.status).toBe(401)
expect(handlerExecuted).toBeFalsy()
expect(await res.text()).toBe('Custom unauthorized message as string')
})
it('Should not authorize - custom invalid user message as object', async () => {
const req = new Request('http://localhost/auth-custom-invalid-user-message-object')
const res = await app.request(req)
expect(res).not.toBeNull()
expect(res.status).toBe(401)
expect(res.headers.get('Content-Type')).toMatch('application/json; charset=UTF-8')
expect(handlerExecuted).toBeFalsy()
expect(await res.text()).toBe('{"message":"Custom unauthorized message as object"}')
})
it('Should not authorize - custom invalid user message as function string', async () => {
const req = new Request('http://localhost/auth-custom-invalid-user-message-function-string')
const res = await app.request(req)
expect(res).not.toBeNull()
expect(res.status).toBe(401)
expect(handlerExecuted).toBeFalsy()
expect(await res.text()).toBe('Custom unauthorized message as function string')
})
it('Should not authorize - custom invalid user message as function object', async () => {
const req = new Request('http://localhost/auth-custom-invalid-user-message-function-object')
const res = await app.request(req)
expect(res).not.toBeNull()
expect(res.status).toBe(401)
expect(res.headers.get('Content-Type')).toMatch('application/json; charset=UTF-8')
expect(handlerExecuted).toBeFalsy()
expect(await res.text()).toBe('{"message":"Custom unauthorized message as function object"}')
})
})

View File

@ -9,17 +9,21 @@ import type { MiddlewareHandler } from '../../types'
import { auth } from '../../utils/basic-auth'
import { timingSafeEqual } from '../../utils/buffer'
type MessageFunction = (c: Context) => string | object | Promise<string | object>
type BasicAuthOptions =
| {
username: string
password: string
realm?: string
hashFunction?: Function
invalidUserMessage?: string | object | MessageFunction
}
| {
verifyUser: (username: string, password: string, c: Context) => boolean | Promise<boolean>
realm?: string
hashFunction?: Function
invalidUserMessage?: string | object | MessageFunction
}
/**
@ -33,6 +37,7 @@ type BasicAuthOptions =
* @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.
* @returns {MiddlewareHandler} The middleware handler function.
* @throws {HTTPException} If neither "username and password" nor "verifyUser" options are provided.
*
@ -70,6 +75,10 @@ export const basicAuth = (
options.realm = 'Secure Area'
}
if (!options.invalidUserMessage) {
options.invalidUserMessage = 'Unauthorized'
}
if (usernamePasswordInOptions) {
users.unshift({ username: options.username, password: options.password })
}
@ -95,12 +104,25 @@ export const basicAuth = (
}
}
}
const res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="' + options.realm?.replace(/"/g, '\\"') + '"',
},
})
throw new HTTPException(401, { res })
// Invalid user.
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; charset=UTF-8',
},
})
throw new HTTPException(status, { res })
}
}