Skip to content

Commit

Permalink
Fix username and password encoding in URL
Browse files Browse the repository at this point in the history
This is a Node.js bug, but it's better to fix this in Got.
In the future we may use a different HTTP client,
which doesn't support these properties in URL.

Fixes #1169
Fixes #1317
  • Loading branch information
szmarczak committed Aug 4, 2021
1 parent eda69ff commit d65d0ca
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 13 deletions.
7 changes: 6 additions & 1 deletion source/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1042,7 +1042,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {

private async _makeRequest(): Promise<void> {
const {options} = this;
const {headers} = options;
const {headers, username, password} = options;
const cookieJar = options.cookieJar as PromiseCookieJar | undefined;

for (const key in headers) {
Expand All @@ -1058,6 +1058,11 @@ export default class Request extends Duplex implements RequestEvents<Request> {
headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate';
}

if (username || password) {
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
headers.authorization = `Basic ${credentials}`;
}

// Set cookies
if (cookieJar) {
const cookieString: string = await cookieJar.getCookieString(options.url!.toString());
Expand Down
23 changes: 11 additions & 12 deletions source/core/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1608,44 +1608,43 @@ export default class Options {
get username(): string {
const url = this._internals.url as URL;

if (url) {
return url.username;
}
const value = url ? url.username : this._internals.username;

return this._internals.username;
return decodeURIComponent(value);
}

set username(value: string) {
assert.string(value);

const url = this._internals.url as URL;
const fixedValue = encodeURIComponent(value);

if (url) {
url.username = value;
url.username = fixedValue;
} else {
this._internals.username = value;
this._internals.username = fixedValue;
}
}

get password(): string {
const url = this._internals.url as URL;

if (url) {
return url.password;
}
const value = url ? url.password : this._internals.password;

return this._internals.password;
return decodeURIComponent(value);
}

set password(value: string) {
assert.string(value);

const url = this._internals.url as URL;

const fixedValue = encodeURIComponent(value);

if (url) {
url.password = value;
url.password = fixedValue;
} else {
this._internals.password = value;
this._internals.password = fixedValue;
}
}

Expand Down
16 changes: 16 additions & 0 deletions test/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,19 @@ test('strip port in host header if implicit standard port & protocol (HTTPS)', a
const body = await got('https://httpbin.org/headers').json<{headers: Headers}>();
t.is(body.headers.Host, 'httpbin.org');
});

test('correctly encodes authorization header', withServer, async (t, server, got) => {
server.get('/', echoHeaders);

const {authorization} = await got('', {username: 'test@'}).json();

t.is(authorization, `Basic ${Buffer.from('test@:').toString('base64')}`);
});

test('url passes if credentials contain special characters', withServer, async (t, server, got) => {
server.get('/', echoHeaders);

const {authorization} = await got('', {password: 't$es%t'}).json();

t.is(authorization, `Basic ${Buffer.from(':t$es%t').toString('base64')}`);
});

0 comments on commit d65d0ca

Please sign in to comment.