Files
pnpm/network/auth-header/test/getAuthHeadersFromConfig.test.ts
Burra Karthikeya b1ad9c7d83 feat(auth): prepend 'Bearer' to auth token generated by tokenHelper (#11097)
* fix(auth-header): decode _password from base64 for default registry auth

* fix(auth): prepend 'Bearer ' to auth token generated by tokenHelper

* test: skip flaky parallel dlx test on Node 25

* fix(auth): improve tokenHelper Bearer prefix with validation and generic scheme detection

- Throw an error when the token helper returns an empty token instead of
  producing an invalid "Bearer " header
- Use a generic auth scheme regex instead of hardcoding only Bearer/Basic,
  so other schemes (Token, Negotiate, etc.) are preserved as-is
- Add tests for raw token prefixing, existing scheme preservation, and
  empty token error

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-03-26 15:33:02 +01:00

154 lines
5.3 KiB
TypeScript

import os from 'node:os'
import path from 'node:path'
import { Buffer } from 'safe-buffer'
import { getAuthHeadersFromConfig } from '../src/getAuthHeadersFromConfig.js'
const osTokenHelper = {
linux: path.join(import.meta.dirname, 'utils/test-exec.js'),
win32: path.join(import.meta.dirname, 'utils/test-exec.bat'),
}
const osRawTokenHelper = {
linux: path.join(import.meta.dirname, 'utils/test-exec-raw-token.js'),
win32: path.join(import.meta.dirname, 'utils/test-exec-raw-token.bat'),
}
const osEmptyTokenHelper = {
linux: path.join(import.meta.dirname, 'utils/test-exec-empty-token.js'),
win32: path.join(import.meta.dirname, 'utils/test-exec-empty-token.bat'),
}
const osErrorTokenHelper = {
linux: path.join(import.meta.dirname, 'utils/test-exec-error.js'),
win32: path.join(import.meta.dirname, 'utils/test-exec-error.bat'),
}
// Only exception is win32, all others behave like linux
const osFamily = os.platform() === 'win32' ? 'win32' : 'linux'
describe('getAuthHeadersFromConfig()', () => {
it('should get settings', () => {
const allSettings = {
'//registry.npmjs.org/:_authToken': 'abc123',
'//registry.foobar.eu/:_password': encodeBase64('foobar'),
'//registry.foobar.eu/:username': 'foobar',
'//registry.hu/:_auth': 'foobar',
'//localhost:3000/:_auth': 'foobar',
}
const userSettings = {}
expect(getAuthHeadersFromConfig({ allSettings, userSettings })).toStrictEqual({
'//registry.npmjs.org/': 'Bearer abc123',
'//registry.foobar.eu/': 'Basic Zm9vYmFyOmZvb2Jhcg==',
'//registry.hu/': 'Basic foobar',
'//localhost:3000/': 'Basic foobar',
})
})
describe('should get settings for the default registry', () => {
it('_auth', () => {
const allSettings = {
registry: 'https://reg.com/',
_auth: 'foobar',
}
expect(getAuthHeadersFromConfig({ allSettings, userSettings: {} })).toStrictEqual({
'//reg.com/': 'Basic foobar',
})
})
it('username/_password', () => {
const allSettings = {
registry: 'https://reg.com/',
username: 'foo',
_password: encodeBase64('bar'),
}
expect(getAuthHeadersFromConfig({ allSettings, userSettings: {} })).toStrictEqual({
'//reg.com/': `Basic ${encodeBase64('foo:bar')}`,
})
})
it('tokenHelper', () => {
const allSettings = {
registry: 'https://reg.com/',
}
const userSettings = {
tokenHelper: osTokenHelper[osFamily],
}
expect(getAuthHeadersFromConfig({ allSettings, userSettings })).toStrictEqual({
'//reg.com/': 'Bearer token-from-spawn',
})
})
it('only read token helper from user config', () => {
const allSettings = {
registry: 'https://reg.com/',
tokenHelper: osTokenHelper[osFamily],
}
expect(getAuthHeadersFromConfig({ allSettings, userSettings: {} })).toStrictEqual({})
})
})
it('should get tokenHelper', () => {
const userSettings = {
'//registry.foobar.eu/:tokenHelper': osTokenHelper[osFamily],
}
expect(getAuthHeadersFromConfig({ allSettings: {}, userSettings })).toStrictEqual({
'//registry.foobar.eu/': 'Bearer token-from-spawn',
})
})
it('should throw an error if the token helper is not an absolute path', () => {
expect(() => getAuthHeadersFromConfig({
allSettings: {},
userSettings: {
'//reg.com:tokenHelper': './utils/text-exec.js',
},
})).toThrow('must be an absolute path, without arguments')
})
it('should throw an error if the token helper is not an absolute path with args', () => {
expect(() => getAuthHeadersFromConfig({
allSettings: {},
userSettings: {
'//reg.com:tokenHelper': `${osTokenHelper[osFamily]} arg1`,
},
})).toThrow('must be an absolute path, without arguments')
})
it('should throw an error if the token helper fails', () => {
expect(() => getAuthHeadersFromConfig({
allSettings: {},
userSettings: {
'//reg.com:tokenHelper': osErrorTokenHelper[osFamily],
},
})).toThrow('Exit code')
})
it('only read token helper from user config', () => {
const allSettings = {
'//reg.com:tokenHelper': osTokenHelper[osFamily],
}
expect(getAuthHeadersFromConfig({ allSettings, userSettings: {} })).toStrictEqual({})
})
it('should prepend Bearer to raw token from tokenHelper', () => {
const userSettings = {
'//registry.foobar.eu/:tokenHelper': osRawTokenHelper[osFamily],
}
expect(getAuthHeadersFromConfig({ allSettings: {}, userSettings })).toStrictEqual({
'//registry.foobar.eu/': 'Bearer raw-token-no-scheme',
})
})
it('should not modify token that already has an auth scheme', () => {
const userSettings = {
'//registry.foobar.eu/:tokenHelper': osTokenHelper[osFamily],
}
expect(getAuthHeadersFromConfig({ allSettings: {}, userSettings })).toStrictEqual({
'//registry.foobar.eu/': 'Bearer token-from-spawn',
})
})
it('should throw an error if the token helper returns an empty token', () => {
expect(() => getAuthHeadersFromConfig({
allSettings: {},
userSettings: {
'//reg.com:tokenHelper': osEmptyTokenHelper[osFamily],
},
})).toThrow('returned an empty token')
})
})
function encodeBase64 (s: string) {
return Buffer.from(s, 'utf8').toString('base64')
}