@enterprise_search/fetchfn
Advanced tools
Comparing version 0.8.66 to 0.8.67
export * from './src/fetchfn'; | ||
export * from './src/auth'; | ||
export * from './src/tokenCache'; |
@@ -19,2 +19,3 @@ "use strict"; | ||
__exportStar(require("./src/auth"), exports); | ||
__exportStar(require("./src/tokenCache"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -5,14 +5,6 @@ import { Timeservice } from "@itsmworkbench/utils"; | ||
import { FetchFn } from "./fetchfn"; | ||
import { TokenCache, TokenCacheConfig } from "./tokenCache"; | ||
export type AuthFn = (auth: Authentication) => Promise<NameAnd<string>>; | ||
export type TokenAndTime = { | ||
token: string; | ||
expires: number; | ||
refreshToken: string; | ||
}; | ||
export type TokenCache = NameAnd<Promise<TokenAndTime>>; | ||
export type AuthForEntraIdFn = (env: NameAnd<string>, fetch: FetchFn, oauth: EntraIdAuthentication) => Promise<TokenAndTime>; | ||
export declare const authForEntraId: AuthForEntraIdFn; | ||
export declare function getOrUpdateEntraIdRaw(fetch: FetchFn, timeService: Timeservice, oauth: EntraIdAuthentication, tokenCache: TokenCache, authForEntraIdFn?: AuthForEntraIdFn): Promise<TokenAndTime>; | ||
export declare function getOrUpdateEntraId(fetch: FetchFn, timeService: Timeservice, oauth: EntraIdAuthentication, tokenCache: TokenCache, authForEntraIdFn?: AuthForEntraIdFn): Promise<string>; | ||
export declare function getOrUpdateEntraId<Auth>(config: TokenCacheConfig<EntraIdAuthentication>, auth: EntraIdAuthentication): Promise<string>; | ||
export declare const defaultAuthFn: (env: NameAnd<string>, fetch: FetchFn, timeService: Timeservice, tokenCache?: TokenCache) => AuthFn; | ||
export declare const findVarsFrom: (env: NameAnd<string>) => (auth: Authentication | undefined) => string[]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.findVarsFrom = exports.defaultAuthFn = exports.authForEntraId = void 0; | ||
exports.getOrUpdateEntraIdRaw = getOrUpdateEntraIdRaw; | ||
exports.findVarsFrom = exports.defaultAuthFn = void 0; | ||
exports.getOrUpdateEntraId = getOrUpdateEntraId; | ||
const indexconfig_1 = require("@enterprise_search/indexconfig"); | ||
const oauth_fetcher_1 = require("./oauth.fetcher"); | ||
const tokenCache_1 = require("./tokenCache"); | ||
function authForApiToken(env, auth) { | ||
@@ -35,2 +36,24 @@ const apiKey = auth.credentials?.apiKey; | ||
} | ||
async function oAuthForBasic(env, fetch, auth) { | ||
const { username, password, tokenUrl } = auth.credentials; | ||
if (!username) | ||
throw Error('No username in ' + JSON.stringify(auth)); | ||
if (!password) | ||
throw Error('No password in ' + JSON.stringify(auth)); | ||
if (!tokenUrl) | ||
throw Error('No tokenUrl in ' + JSON.stringify(auth)); | ||
const passwordFromEnv = env[password]; | ||
if (!passwordFromEnv) | ||
throw Error('No password in environment for ' + password); | ||
const basicToken = `Basic ${Buffer.from(`${username}:${passwordFromEnv}`).toString('base64')}`; | ||
const params = new URLSearchParams({ grant_type: "client_credentials" }); | ||
const url = `${tokenUrl}?${params.toString()}`; | ||
const response = await fetch(url, { method: "Post", headers: { Authorization: basicToken } }); | ||
if (!response.ok) { | ||
console.log(await response.text()); | ||
throw new Error(`Error fetching access token: ${response.status} ${response.statusText} ${JSON.stringify(response.headers)}`); | ||
} | ||
const data = await response.json(); | ||
return { token: data.access_token, expires: data.expires_in }; | ||
} | ||
async function authForPrivate(env, auth) { | ||
@@ -45,82 +68,44 @@ const privateKey = auth.credentials?.token; | ||
} | ||
const authForEntraId = async (env, fetch, oauth) => { | ||
const { tenantId, clientId, clientSecret, resource, scope } = oauth.credentials; | ||
const version = oauth.credentials.version || 2; | ||
const url = version === 2 ? | ||
`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token` : | ||
`https://accounts.accesscontrol.windows.net/${tenantId}/tokens/OAuth/2`; //legacy but OK endpoint | ||
const body = new URLSearchParams(); | ||
const secret = env[clientSecret]; | ||
if (!secret) | ||
throw Error(`Need Environment variable for client secret ${clientSecret}`); | ||
body.append('grant_type', 'client_credentials'); | ||
body.append('client_id', clientId); | ||
if (resource) | ||
body.append('resource', resource); | ||
body.append('client_secret', secret); | ||
if (scope) | ||
body.append('scope', scope); | ||
const options = { | ||
method: 'Post', | ||
body: body.toString(), | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded' | ||
} | ||
async function getOrUpdateEntraId(config, auth) { | ||
return (await (0, tokenCache_1.getTokenAndTimeUsingCache)(config, auth)).token; | ||
} | ||
const defaultAuthFn = (env, fetch, timeService, tokenCache = tokenCache_1.globalTokenCache) => { | ||
const entraIdConfig = { | ||
fetch, | ||
timeService, | ||
env, | ||
tokenCache, | ||
tokenCacheLoadFn: oauth_fetcher_1.authForEntraId, | ||
keyFn: (auth) => auth.credentials.clientId + '/' + auth.credentials.scope, | ||
}; | ||
// console.log ( options ) | ||
const response = await fetch(url, options); | ||
if (!response.ok) { | ||
console.log(await response.text()); | ||
throw new Error(`Error fetching access token: ${response.status} ${response.statusText} ${JSON.stringify(response.headers)}`); | ||
} | ||
const data = await response.json(); | ||
return { token: data.access_token, expires: data.expires_in, refreshToken: data.refresh_token }; | ||
const authForBearerConfig = { | ||
fetch, | ||
timeService, | ||
env, | ||
tokenCache, | ||
tokenCacheLoadFn: oauth_fetcher_1.oauth2AuthenticationToken, | ||
keyFn: (auth) => auth.credentials.clientId + '/' + auth.credentials.scope, | ||
}; | ||
return async (auth) => { | ||
if ((0, indexconfig_1.isEntraIdAuthentication)(auth)) | ||
return { Authorization: `Bearer ${await (0, tokenCache_1.getTokenUsingCache)(entraIdConfig, auth)}` }; | ||
if ((0, indexconfig_1.isOauth2)(auth)) | ||
return { Authorization: `Bearer ${await (0, tokenCache_1.getTokenUsingCache)(authForBearerConfig, auth)}` }; | ||
if ((0, indexconfig_1.isSASAuthentication)(auth)) | ||
return {}; //no auth needed except for the sas token which is part of the url | ||
if ((0, indexconfig_1.isApiKeyAuthentication)(auth)) | ||
return authForApiToken(env, auth); | ||
if ((0, indexconfig_1.isBearerAuthentication)(auth)) | ||
return authForBearerToken(env, auth); | ||
if ((0, indexconfig_1.isBasicAuthentication)(auth)) | ||
return authForBasic(env, auth); | ||
if ((0, indexconfig_1.isOAuthForBasicAuthentication)(auth)) | ||
return oAuthForBasic(env, fetch, auth); | ||
if ((0, indexconfig_1.isPrivateTokenAuthentication)(auth)) | ||
return authForPrivate(env, auth); | ||
if ((0, indexconfig_1.isNoAuthentication)(auth)) | ||
return {}; | ||
throw Error('Unknown auth method ' + JSON.stringify(auth)); | ||
}; | ||
}; | ||
exports.authForEntraId = authForEntraId; | ||
async function getOrUpdateEntraIdRaw(fetch, timeService, oauth, tokenCache, authForEntraIdFn = exports.authForEntraId) { | ||
const key = oauth.credentials.clientId + '/' + oauth.credentials.scope; | ||
const tokenDataPromise = tokenCache[key]; | ||
if (tokenDataPromise) { | ||
const tokenData = await tokenDataPromise; | ||
if (tokenData && tokenData.expires > timeService()) | ||
return tokenData; | ||
} | ||
const doubleCheck = tokenCache[key]; //double sync check. the await in the above check can cause multiple fetches. Only causes wasted calls..but still... | ||
if (doubleCheck && (await doubleCheck).expires > timeService()) | ||
return (await doubleCheck); | ||
const newToken = authForEntraIdFn(process.env, fetch, oauth).then((token) => { | ||
if (!token.token) | ||
throw Error('No access token in ' + JSON.stringify(token)); | ||
if (!token.expires) | ||
throw Error('No expires in ' + JSON.stringify(token)); | ||
return ({ ...token, token: token.token, expires: timeService() + token.expires * 500 }); // we will get a new token well before the expiry is up | ||
}).catch(e => { | ||
console.error(e); | ||
tokenCache[key] = undefined; | ||
throw e; | ||
}); | ||
tokenCache[key] = newToken; | ||
return (await newToken); | ||
} | ||
async function getOrUpdateEntraId(fetch, timeService, oauth, tokenCache, authForEntraIdFn = exports.authForEntraId) { | ||
return (await getOrUpdateEntraIdRaw(fetch, timeService, oauth, tokenCache, authForEntraIdFn)).token; | ||
} | ||
const globalTokenCache = {}; | ||
const defaultAuthFn = (env, fetch, timeService, tokenCache = globalTokenCache) => async (auth) => { | ||
if ((0, indexconfig_1.isEntraIdAuthentication)(auth)) | ||
return { Authorization: `Bearer ${await getOrUpdateEntraId(fetch, timeService, auth, tokenCache)}` }; | ||
if ((0, indexconfig_1.isSASAuthentication)(auth)) | ||
return {}; //no auth needed except for the sas token which is part of the url | ||
if ((0, indexconfig_1.isApiKeyAuthentication)(auth)) | ||
return authForApiToken(env, auth); | ||
if ((0, indexconfig_1.isBearerAuthentication)(auth)) | ||
return authForBearerToken(env, auth); | ||
if ((0, indexconfig_1.isBasicAuthentication)(auth)) | ||
return authForBasic(env, auth); | ||
if ((0, indexconfig_1.isPrivateTokenAuthentication)(auth)) | ||
return authForPrivate(env, auth); | ||
if ((0, indexconfig_1.isNoAuthentication)(auth)) | ||
return {}; | ||
throw Error('Unknown auth method ' + JSON.stringify(auth)); | ||
}; | ||
exports.defaultAuthFn = defaultAuthFn; | ||
@@ -127,0 +112,0 @@ const findVarsFrom = (env) => (auth) => { |
{ | ||
"name": "@enterprise_search/fetchfn", | ||
"description": "Code to abstract fetch calls (node and browser are different)", | ||
"version": "0.8.66", | ||
"version": "0.8.67", | ||
"main": "dist/index", | ||
@@ -6,0 +6,0 @@ "types": "dist/index", |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
43149
18
566
1
8