Skip to content

Commit

Permalink
Merge pull request #223 from BendingBender/feature/basic-auth
Browse files Browse the repository at this point in the history
🏭 Add BasicAuth addon
  • Loading branch information
elbywan authored Jun 1, 2024
2 parents b299791 + 57f46f2 commit 97773d0
Show file tree
Hide file tree
Showing 16 changed files with 365 additions and 89 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,25 @@ wretch("...").addon(AbortAddon()).get().setTimeout(1000).json(_ =>
)
```

### [BasicAuth 🔗](https://elbywan.github.io/wretch/api/modules/addons_basicAuth.BasicAuthAddon.html)

Adds the ability to set the `Authorization` header for the [basic authentication scheme](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme) without the need to manually encode the username/password.

Also, allows using URLs with `wretch` that contain credentials, which would otherwise throw an error.

```js
import BasicAuthAddon from "wretch/addons/basicAuth"

const user = "user"
const pass = "pass"

// Automatically sets the Authorization header to "Basic " + <base64 encoded credentials>
wretch("...").addon(BasicAuthAddon).basicAuth(user, pass).get()

// Allows using URLs with credentials in them
wretch(`https://${user}:${pass}@...`).addon(BasicAuthAddon).get()
```

### [Progress 🔗](https://elbywan.github.io/wretch/api/modules/addons_progress.html)

Adds the ability to monitor progress when downloading a response.
Expand Down
126 changes: 94 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@
"tslint": "^6.1.2",
"typedoc": "^0.25.3",
"typescript": "^5.2.2",
"wait-on": "^7.2.0"
"wait-on": "^7.2.0",
"whatwg-url": "^14.0.0"
},
"jest": {
"testPathIgnorePatterns": [
Expand Down Expand Up @@ -167,4 +168,4 @@
"src/addons/index.ts"
]
}
}
}
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';
import { nodeResolve } from '@rollup/plugin-node-resolve';

const addons = ["abort", "formData", "formUrl", "perfs", "queryString", "progress"]
const addons = ["abort", "basicAuth", "formData", "formUrl", "perfs", "queryString", "progress"]
const middlewares = ["dedupe", "delay", "retry", "throttlingCache"]

const common = {
Expand Down
75 changes: 75 additions & 0 deletions src/addons/basicAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { Config, ConfiguredMiddleware, Wretch, WretchAddon } from "../types.js"

function utf8ToBase64(input: string) {
const utf8Bytes = new TextEncoder().encode(input)
return btoa(String.fromCharCode(...utf8Bytes))
}

export interface BasicAuthAddon {
/**
* Sets the `Authorization` header to `Basic ` + <base64 encoded credentials>.
* Additionally, allows using URLs with credentials in them.
*
* ```js
* const user = "user"
* const pass = "pass"
*
* // Automatically sets the Authorization header to "Basic " + <base64 encoded credentials>
* wretch("...").addon(BasicAuthAddon).basicAuth(user, pass).get()
*
* // Allows using URLs with credentials in them
* wretch(`https://${user}:${pass}@...`).addon(BasicAuthAddon).get()
* ```
*
* @param input - The credentials to use for the basic auth.
*/
basicAuth<T extends BasicAuthAddon, C, R>(
this: T & Wretch<T, C, R>,
username: string,
password: string
): this
}

const makeBasicAuthMiddleware: (config: Config) => ConfiguredMiddleware = config => next => (url, opts) => {
const _URL = config.polyfill("URL")
const parsedUrl = _URL.canParse(url) ? new _URL(url) : null

if (parsedUrl?.username || parsedUrl?.password) {
const basicAuthBase64 = utf8ToBase64(
`${decodeURIComponent(parsedUrl.username)}:${decodeURIComponent(parsedUrl.password)}`,
)
opts.headers = {
...opts.headers,
Authorization: `Basic ${basicAuthBase64}`,
}
parsedUrl.username = ""
parsedUrl.password = ""
url = parsedUrl.toString()
}

return next(url, opts)
}


/**
* Adds the ability to use basic auth with the `Authorization` header.
*
* ```js
* import BasicAuthAddon from "wretch/addons/basicAuth"
*
* wretch().addon(BasicAuthAddon)
* ```
*/
const basicAuth: WretchAddon<BasicAuthAddon> = {
beforeRequest(wretch) {
return wretch.middlewares([makeBasicAuthMiddleware(wretch._config)])
},
wretch: {
basicAuth(username, password) {
const basicAuthBase64 = utf8ToBase64(`${username}:${password}`)
return this.auth(`Basic ${basicAuthBase64}`)
},
},
}

export default basicAuth
2 changes: 1 addition & 1 deletion src/addons/formData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function convertFormData(
recursive: string[] | boolean = false,
config: Config,
formData = config.polyfill("FormData", true, true),
ancestors = [],
ancestors = [] as string[],
) {
Object.entries(formObject).forEach(([key, value]) => {
let formKey = ancestors.reduce((acc, ancestor) => (
Expand Down
2 changes: 2 additions & 0 deletions src/addons/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { default as abortAddon } from "./abort.js"
export type { AbortWretch, AbortResolver } from "./abort.js"
export { default as basicAuthAddon } from "./basicAuth.js"
export type { BasicAuthAddon } from "./basicAuth.js"
export { default as formDataAddon } from "./formData.js"
export type { FormDataAddon } from "./formData.js"
export { default as formUrlAddon } from "./formUrl.js"
Expand Down
Loading

0 comments on commit 97773d0

Please sign in to comment.