Skip to content

Commit

Permalink
fix(oas-normalize): support for basic auth in url strings (#928)
Browse files Browse the repository at this point in the history
| 🚥 Resolves readmeio/rdme#1152 |
| :------------------- |

## 🧰 Changes

When we moved `oas-normalize` off `node-fetch` and to native `fetch` we
lost the ability to supply basic auth credentials directly in the URL
string as `fetch` doesn't support that.
  • Loading branch information
erunion authored Jan 25, 2025
1 parent 491d63a commit 85b060a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
3 changes: 2 additions & 1 deletion packages/oas-normalize/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ export default class OASNormalize {
return resolve(this.file.toString());

case 'url':
const resp = await fetch(utils.normalizeURL(this.file)).then(res => res.text());
const { url, options } = utils.prepareURL(this.file);
const resp = await fetch(url, options).then(res => res.text());
return resolve(resp);

case 'path':
Expand Down
31 changes: 26 additions & 5 deletions packages/oas-normalize/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,34 @@ export function isBuffer(obj: any) {
}

/**
* Converts GitHub blob URLs to raw URLs
* Deconstruct a URL into a payload for a `fetch` request.
*
*/
export function normalizeURL(url: string) {
if (url.startsWith('https://github.com/') && url.includes('/blob/')) {
return url.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/');
export function prepareURL(url: string) {
const options: RequestInit = {};
const u = new URL(url);

// `fetch` doesn't support supplying basic auth credentials in the URL so we need to move them
// into a header.
if (u.username || u.password) {
options.headers = {
Authorization: `Basic ${btoa(`${u.username}:${u.password}`)}`,
};

u.username = '';
u.password = '';
}
return url;

// Transform GitHub sources into their raw content URLs.
if (u.host === 'github.com' && u.pathname.includes('/blob/')) {
u.host = 'raw.githubusercontent.com';
u.pathname = u.pathname.replace('/blob/', '/');
}

return {
url: u.toString(),
options,
};
}

/**
Expand Down
18 changes: 16 additions & 2 deletions packages/oas-normalize/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ describe('#load', () => {
['OpenAPI 3.0', '3.0'],
['OpenAPI 3.1', '3.1'],
])('%s support', (_, version) => {
let json;
let yaml;
let json: Record<string, unknown>;
let yaml: string;

beforeEach(async () => {
json = await import(`@readme/oas-examples/${version}/json/petstore.json`).then(r => r.default);
Expand Down Expand Up @@ -67,6 +67,20 @@ describe('#load', () => {
await expect(o.load()).resolves.toStrictEqual(json);
});

it('should support URLs with basic auth', async () => {
nock('https://@example.com', {
reqheaders: {
Authorization: `Basic ${btoa('username:password')}`,
},
})
.get(`/api-${version}.json`)
.reply(200, json);

const o = new OASNormalize(`https://username:[email protected]/api-${version}.json`);

await expect(o.load()).resolves.toStrictEqual(json);
});

it('should convert GitHub repo URLs to raw URLs', async () => {
nock('https://raw.githubusercontent.com')
.get('/readmeio/oas-examples/main/3.0/json/petstore.json')
Expand Down

0 comments on commit 85b060a

Please sign in to comment.