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:
parent
a86f3cea5f
commit
c50be25c9e
@ -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"}')
|
||||
})
|
||||
})
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user