Skip to content

Commit

Permalink
Add 'enabled' configuration to bypass auth middleware globally
Browse files Browse the repository at this point in the history
  • Loading branch information
eelkevdbos committed Sep 12, 2023
1 parent b968b60 commit 792d65e
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 33 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,11 @@ Alternatively, a function can be provided that returns `true` if the context (an
Default: `false`

A boolean that determines whether CORS preflight requests should be skipped.

### enabled

`boolean`

Default: `true`

A boolean that determines whether basic auth should be enabled. If set to `false`, will disable the `onRequest` handler.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eelkevdbos/elysia-basic-auth",
"version": "1.2.0",
"version": "1.3.0",
"description": "Basic auth for Elysia.js",
"repository": "https://github.com/eelkevdbos/elysia-basic-auth",
"author": {
Expand Down
10 changes: 10 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,13 @@ describe('basicAuth multi-realm', () => {
expect(await realmBResponse.text()).toEqual('Realm B')
})
})

describe('basicAuth enabled', () => {
it("doesn't require auth if disabled", async () => {
const app = new Elysia()
.use(basicAuth({ enabled: false }))
.get('/private', () => 'private')

expect((await app.handle(req('/private'))).status).toEqual(200)
})
})
66 changes: 34 additions & 32 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,13 @@ type CredentialsMap = Record<
BasicAuthCredentials
>

function newCredentialsMap(
option: BasicAuthOptions['credentials']
): CredentialsMap {
if (Array.isArray(option)) {
return option.reduce((mapping, credentials) => {
return { ...mapping, [credentials.username]: credentials }
}, {})
}

if ('file' in option) {
return fs
.readFileSync(option.file, 'utf-8')
.split('\n')
.reduce((m, l) => {
const [username, password] = l.split(':')
if (!username || !password) return m
return { ...m, [username]: { username, password } }
}, {})
}

if ('env' in option) {
return (process.env[option.env] || '').split(';').reduce((m, cStr) => {
const [username, password] = cStr.split(':')
if (!username || !password) return m
return { ...m, [username]: { username, password } }
}, {})
}

throw new Error('Invalid credentials option')
}

export type BasicAuthCredentialOptions =
| { env: string }
| { file: string }
| BasicAuthCredentials[]

export type BasicAuthOptions = {
enabled: Boolean
credentials: BasicAuthCredentialOptions
header: string
realm: string
Expand All @@ -69,6 +39,7 @@ class BasicAuthError extends Error {
}

const defaultOptions: BasicAuthOptions = {
enabled: true,
credentials: { env: 'BASIC_AUTH_CREDENTIALS' },
header: 'Authorization',
realm: 'Secure Area',
Expand All @@ -78,6 +49,37 @@ const defaultOptions: BasicAuthOptions = {
skipCorsPreflight: false,
}

function newCredentialsMap(
option: BasicAuthOptions['credentials']
): CredentialsMap {
if (Array.isArray(option)) {
return option.reduce((mapping, credentials) => {
return { ...mapping, [credentials.username]: credentials }
}, {})
}

if ('file' in option) {
return fs
.readFileSync(option.file, 'utf-8')
.split('\n')
.reduce((m, l) => {
const [username, password] = l.split(':')
if (!username || !password) return m
return { ...m, [username]: { username, password } }
}, {})
}

if ('env' in option) {
return (process.env[option.env] || '').split(';').reduce((m, cStr) => {
const [username, password] = cStr.split(':')
if (!username || !password) return m
return { ...m, [username]: { username, password } }
}, {})
}

throw new Error('Invalid credentials option')
}

/**
* Timing safe string comparison
*/
Expand Down Expand Up @@ -194,7 +196,7 @@ export function basicAuth(userOptions: Partial<BasicAuthOptions> = {}) {
}
})
.onRequest(ctx => {
if (inScope(ctx) && !skipRequest(ctx.request)) {
if (options.enabled && inScope(ctx) && !skipRequest(ctx.request)) {
const authHeader = ctx.request.headers.get(options.header)
if (!authHeader || !authHeader.toLowerCase().startsWith('basic ')) {
throw new BasicAuthError('Invalid header', options.realm)
Expand Down

0 comments on commit 792d65e

Please sign in to comment.