-
-
Notifications
You must be signed in to change notification settings - Fork 323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to add and update environment variables, modify headers, and new lifecycle events #2121
Comments
Hey! Trying to make sure I understand the issue here
Why can't you automatically use a valid token? Why can't you generate a new one before every request? |
Hope I can give a comprehensive answer:
Because I can't set headers from the pre-request script. For some reason I'm given access to the custom headers which have already been set in the array on So I have to create an empty ENV, and pre-emptively set a the header referencing the empty ENV and then populate it from the script. I don't consider this automated.
Yes, I concede, I can do this. But seems expensive and unnecessary.
Here's what I want to do:
I would like to make this a plugin I can share with others including my teammates, so that we have a robust and easy to use GraphQL client. Insomnia just broke their plugin system with the latest updates, and Postman is rubbish for GraphQL. The pre-requests script at worst needs to be repeated per request, and at best per collection (I split my collections by Query/Mutations and by app) Top issue:
const CryptoJS = await altair.importModule('crypto-js');
const b64Decode = await altair.importModule('atob');
const collectEnvironmentVariables = () => {
const clientId = altair.helpers.getEnvironment('AwsCognitoClientId')
const clientSecret = altair.helpers.getEnvironment('AwsCognitoClientSecret')
const region = altair.helpers.getEnvironment('AwsCognitoRegion')
const username = altair.helpers.getEnvironment('AwsCognitoUsername')
const password = altair.helpers.getEnvironment('AwsCognitoPassword')
if ( !clientId || !clientSecret || !region || !username || !password) {
altair.log("AWS Cognito set up is not complete. Missing ENV vars")
throw new Error("AWS Cognito set up is not complete. Missing ENV vars")
}
altair.log("AWS Inputs: " + JSON.stringify({
clientId,
clientSecret: Boolean(clientSecret),
region,
username,
password: Boolean(password)
}))
return {
clientId,
clientSecret,
region,
username,
password
}
}
const jwtPartLabels = new Map([
[0, 'algo'],
[1, 'content']
])
const decodeAWSCognitoJWTToken = jwtString => {
return jwtString.split('.').reduce((parts, next, i) => {
if (jwtPartLabels.has(i)) {
parts.set(jwtPartLabels.get(i), JSON.parse(atob(next)))
}
return parts
}, new Map())
}
const unixTimestampAtNow = () => {
return Math.round(Date.now() / 1000)
}
const isAWSCognitoJWTokenValid = jwtContent => {
const { exp } = jwtContent
return unixTimestampAtNow() < exp
}
const getRequestSecretHash = (requestSettings) => {
return CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(
[requestSettings.username, requestSettings.clientId].join(''),
requestSettings.clientSecret
)
)
}
const getAWSCognitoAuthURI = (region) => {
return `https://cognito-idp.${region}.amazonaws.com`
}
const generateRequestBody = requestSettings => {
return {
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: requestSettings.clientId,
AuthParameters: {
USERNAME: requestSettings.username,
PASSWORD: requestSettings.password,
SECRET_HASH: getRequestSecretHash(requestSettings)
}
}
}
const getNewAWSCognitoIdToken = async requestSettings => {
const { AuthenticationResult: { IdToken } } = await altair.helpers.request(
'POST',
getAWSCognitoAuthURI(requestSettings.region),
{
headers: {
"content-type": "application/x-amz-json-1.1",
"x-amz-target": "AWSCognitoIdentityProviderService.InitiateAuth",
},
body: JSON.stringify(generateRequestBody(requestSettings))
}
);
altair.log("Id token retrieved: " + IdToken)
return IdToken
}
const getExistingAWSCognitoSession = () => {
return localStorage.getItem("AWSCognitoIdToken") || null
}
const getSessionAWSCognitoToken = () => {
let token = getExistingAWSCognitoSession()
if ( !token || ( token && !isAWSCognitoJWTokenValid(token) ) ) {
// no existing session or it's expired, get a new one
token = getNewAWSCognitoIdToken(collectEnvironmentVariables())
}
return token
}
const addAuthenticationToRequest = async () => {
const token = await getSessionAWSCognitoToken()
altair.helpers.setEnvironment('AwsCognitoIdToken', token)
}
await addAuthenticationToRequest() |
@ortonomy Trying to split this up into multiple separate tasks.
The headers provided to the pre request scripts are read only, and any changes need to use the environment variable like you described. I think it's a valid point to be able to modify the headers directly.
I'm not sure I understand if there's something to be fixed here, or if you're just stating a fact about pre-request scripts generally. Re the plugin..
I am not sold on the idea of allowing the plugin interact with the environment variables just yet. In general, the environment variables tend to be the more sensitive part of the data.
This seems to be the main part that is missing from the current plugin architecture that will enable you do what you want using a plugin. So basically, we need a new event triggered with a "blocking" callback (i.e. it is not just a regular event listener, but it should wait for the event listener to return before proceeding)
We should also be able to set headers from plugins Do these summarize the action items or did I miss anything? |
Hey @imolorhe - thanks for being so responsive and trying to address this. Sorry haven't responded. will make effort to edit this comment and respond today or tomorrow. |
Really really appreciate you feedback and analysis here. I agree with them all, especially:
Allow me to clarify your question I said:
You said:
It is stating a fact, but cannot act alone as a sentence withou context (which was split apart in my request)
To emphasise: if I could write a plugin, I could offer a smooth way for teammates (existing or new) to get started with our AWS appsync grpahql without messy setting up of the pre-request script.
I only suggested this because:
is that clear? if you allow us to set headers in the plugin with a blocking request, yea, then the ENV setting it not needed. as an aside one UX improvement I saw with altair which was somewhat confusing is that despite enabling the "collection" level pre-request script, it doesn't actually get enabled per request, unless you enable the pre-request script in the request. Which may be empty... Suggest removing this limuitation. If you have a pre-request script at collection level and it's enabled, it should be executed regardless of if the request script itself has been enabled. Thanks for working on a great app! |
So for the environment variables, you only want to be able to set them and not read them? |
I think reading and setting them would give ultimately flexibility to plugin creators - you could chain plugins and pass data? If you are making this update, please make the available plugin events available on docs pages. I had to dive into the code to find what was available. |
I try to keep the docs (available docs) updated with relevant changes. I didn't add the events at all though 😅 so nothing to update (since I was concerned they will get stale quickly) Anyway would appreciate any help on the documentation front. |
Sure @imolorhe - are docs for site in this repo? Add a PR from a fork? Let me know the list of events and I'll update the plugin pages. I also couldn't find a full spec on the current versions plugin specification in the manifest. I also want to update the description about a) using |
I need transform my |
@yuniers I am not sure how you can do that easily. You'll need to pretty much copy and paste the entire code into the pre request script. |
I'm trying with next code. await dynamicallyLoadScript('https://cdnjs.cloudflare.com/ajax/libs/jose/4.14.0/index.umd.min.js', 'module');
async function dynamicallyLoadScript(url, type = 'text/javascript') {
try {
let script = document.createElement('script');
let content = await fetch(url);
script.textContent = await content.text();
script.type = type;
document.head.appendChild(script);
} catch (e) {
console.log(e);
throw e;
}
} But a CSP error is triggered. I can't do
|
Yes you can't execute inline scripts on the page |
Could be added another default module? |
Yes that's the only other alternative |
Is your feature request related to a problem? Please describe.
I have an AWS AppSync graphql endpoint. It's secured by Cognito (the auth system for AWS.). It requires an ID token to be able to authenticate. I can't automatically use a valid token, or generate a new one before every request like I can with REST in say, Postmans.
Describe the solution you'd like
I'm trying to write a plugin for Altair that will allow me to manage login and credentials to an AWS Cognito Pool using confidential-client credentials (username,password,client secret, region, poolId) using the
aws-sdk
libI wanted to write my plugin such that it could satisfy:
we need:
Describe alternatives you've considered
A pre-request script - however, I can't use node packages here and the available modules are too limited (for example decoding a JWT) to do the proper logic needed
Additional context
N/A
Tasks
The text was updated successfully, but these errors were encountered: