I keep seeing examples about setting context on Apollo Client like this :
new ApolloClient({
uri: '...',
request(operation) {
const currentUser = readStore<CurrentUser>("currentUser");
currentUser &&
operation.setContext({
headers: { authorization: currentUser.token },
other: "things"
});
}
});
}
With things that go to the request headers, and "other" things. Yet after more than 2h of research, I couldn't find one example of that other context data being retrieved on the other end, on Apollo Server.
It seems like all the examples are about authorization tokens, but how to I retrieve the rest, say using apollo-server-express ?
Here is what I have so far :
const apollo = new ApolloServer({
typeDefs,
resolvers,
context({ req }): Context {
const currentUser =
(req.headers.authorization &&
(jwt.verify(
req.headers.authorization,
process.env.JWTSIGN
) as Context["currentUser"])) ||
null;
// Ok for req.headers.authorization, how to I get "other: things" ?
return {
ip: req.ip,
currentUser
};
}
});
The context function here only gets a req and a res object from Express. After some logs, it doesn't seem to contain the data I want.
Thank you.
The only reason it appears headers are shared in the example you shared is because ApolloClient uses the headers value inside its context to populate the HTTP headers it sends when making a request. Those headers are then parsed by express and made available on the req object. The context used by ApolloClient is strictly client-side. The context used by ApolloServer is strictly server-side. You can't use ApolloClient's context to pass arbitrary values to your server -- you should utilize headers or arguments inside your GraphQL query for that.
Related
Im using Vue.js 3 & the vue-cookies package Vue Cookies
this is how im setting the cookies in the app
in main.js
import VueCookies from "vue-cookies";
app.use(VueCookies, { expires: "35min" });
in login.vue
const $cookies = inject("$cookies");
const handleLogin = async () => {
try{
const res = await axios({
method: "POST",
url: "/sellers/login",
data: {
Email: email.value,
Password: password.value,
},
// withCredentials: true,
});
let token = res.data.token;
// $cookies.set("jwt", token); //---->method 1
const storeToken = useLocalStorage("token", {
token: res.data.token,
});
await new Promise((resolve) => {
setTimeout(resolve, 3000);
});
let storedToken = JSON.parse(window.localStorage.getItem("token"));
$cookies.set("jwt", storedToken.token); ///---> method 2
console.log("getting the cookie");
let y = $cookies.get("jwt");
console.log(y);
}
catch(error){
}
i've tried storing the cookie in local storage then retrieving and setting it from there (method 2) because i thought the problem was method 1
the results of console.log(y) is null
however,i have confirmed the token is in local storage
Both Method 1 & 2 work when the app is running via the Vite development server
After building for production and serving the assets in dist with nodejs, it does not work
what im i doing wrong or haven't done?
i can see the response from the node.js server and even save them in localstorage (like the token)
i can also retrieve the token from localstorage
setting the token from localstorage (method 2) or setting it direct from the response (method 1) is what is not happening
i appreciate your help
First of all, you shouldn't create authentication cookies or save any kind of authorization token this way. It makes a good vulnerability for XSS attacks. You should try to set the cookie from your backend with the httpOnly attribute which is not possible from the client side.
Regarding your problem, it's quite difficult to say what could be the problem on production. My best guess is that your production environment is using https and your cookie is being set insecurely by the package you are using as its the default. Therefor, it is only accessible when using http which you are probably using for development.
Try to set the config to use secure cookies when your import.meta.env.PROD equals true like this example below:
$cookies.config('35m', '', '', import.meta.env.PROD)
You should also make sure that the correct domain is set so it's accessible from the client.
I stored my token to authenticate with my GraphQL API by adding a JWT to the header inside an apollo-link-states #client property.
query ClientToken() {
clientToken #client
}
I now want to use that token to authenticate my Apollo remote queries. Without using the local cache, doing the following works:
const authLink = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
Authorization: `Bearer GETMEATOKEN`
}
})
return forward(operation)
})
I'm struggeling to find a way to query this locally stored token inside this operation to add it where currently GETMEATOKEN appears.
Anyone has a suggestion if/who to query for a locally stored property inside an ApolloLink?
Thanks for all suggestions
I'm trying to figure out the best way to structure a React / Redux app that will primarily use a swagger client for api access.
The problem is I'm not entirely sure where to store a reference to the swagger client. After logging in and obtaining a JWT auth token, I need to tell all subsequent requests to add the authorize header. With axios this is trivial because it persists it's headers until told otherwise. It doesn't appear the swagger client does this. So ideally, I would create a swagger client once upon login, add the header info and just reference it for all future requests (that way too it only fetches the schema json once in a single page application).
Since I'm doing this in the context of an action, would it be best to store the Swagger client in the Redux store (and how would I accomplish that)? Or would I create a static instance of it outside of Redux?
// app init
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
const store = createStoreWithMiddleware(reducers);
export const swaggerClient = { instance: authService.createFromState().then(() => {
ReactDOM.render(
<Provider store={store}></Provider>
...
);
});
do some login stuff, create swagger client:
// redux action
import { swaggerClient } from '../index';
// ... do login, get bearerToken
Swagger({
url: 'https://localhost/swagger/v1/swagger.json',
requestInterceptor(req) {
req.headers['Authorization'] = `Bearer ${bearerToken}`;
return req;
}
}).then((client) => {
// store reference for all future ajax calls
swaggerClient.instance = client;
});
and in case the page is refreshed, we need to rebuild the swagger client from the bearerToken in local storage
// authService
import { swaggerClient } from '../index';
function createFromState() {
// if authentication is known from localstorage, we can rebuild
// a swagger client
if(isAuthenticated()) {
const authentication = getAuthentication();
return Swagger({
url: 'https://localhost/swagger/v1/swagger.json',
requestInterceptor(req) {
req.headers['Authorization'] = `Bearer ${authentication.bearerToken}`;
return req;
}
}).then((client) => {
swaggerClient.instance = client;
return client;
});
}
}
I'm a little confused if this is the right direction, probably a newbie question. Having to wait for the swagger client to load while restoring from localstorage seems a kinda crazy way to do this (to prevent race conditions on future calls).
I have a function that returns a new Request object;
import * as _url from 'url';
// pathname starts with '/content/'
const isContentUrl = (path) => /^\/content\//.test(path);
export default function(url) {
let urlObj = _url.parse(url);
if (isContentUrl(urlObj.pathname)) {
urlObj.pathname = `/offline${urlObj.pathname}`;
}
return new Request(_url.format(urlObj), {
credentials: 'same-origin',
headers: {
'x-requested-with': 'sw'
}
});
}
Now I'm writing unit tests for this function and although I know there isn't actually much that changes here but say for example the headers could change for whatever reason, how can I assert parts of the request object like the headers, credentials or the URL?
Is there a nice way to be able to parse it for testing?
Ideally I'd like to do something like
it('should return a Request object with the correct headers', () => {
const url = '/content/2c509c50-e4ba-11e6-9645-c9357a75844a';
const request = offlineContent(url);
const result = request.headers;
const expected = {'x-requested-with': 'sw'};
expect(result).to.eql(expected);
});
in my test
Assuming that request here the request HTTP client, the returned object is an instance of the Request constructor defined here.
If you follow through the code, you will see that headers provided are available simply through an object member and thus headers, and other members being attached to self can be easily introspected through javascript.
In addition, the http module through which requests are dispatched is available as self.httpModule and can be mocked by an implementation that is compliant with node http module and requests dispatched through the library can be intercepted through spies.
MDN details the methods on the Request and Headers object. Unfortunately I haven't found a way to convert this to an object that I could assert with a deep equal. But I can use these to assert against.
request.url;
request.credentials;
request.headers.get('x-requested-with');
Unfortunately the Headers.getAll() method has been deprecated so in order to fail tests if someone added a new header I have asserted against;
Array.from(request.headers.keys()).length
I am working on express and I need to perform a POST request to an endpoint within the server. My code for this is :
request({
url : 'http://localhost:3000/api/oauth2/authorize',
qs:{
transaction_id:req.oauth2.transactionID,
user:req.user,
client : req.oauth2.client
},
headers:{
'Authorization':auth,
'Content-Type':'application/x-www-form-urlencoded'
},
method:'POST'
},function(err,res,bo){
console.log("Got response with body :"+bo);
});
localhost is the current server, this works properly but the session data is lost when i perform the POST request.
Is there any other way to perform a POST within the same server or to save the session data such that it is maintained after the POST?
Well, typically you register your routes something like:
var handlePostRequest = function(req,res,next) {
// process req.body etc.
};
app.post('/api/oauth2/authorize', handlePostRequest);
If you want to call that endpoint from within your application, you simply call handlePostRequest() providing the req, res, next objects as well.
Assuming handlePostRequest is in global scope, or required already; in your example that would be:
app.get('/some/other/endpoint', function(req,res,next){
// override the default req.body with your supplied data
req.body = {
transaction_id: req.oauth2.transactionID,
user: req.user,
client: req.oauth2.client
};
// you may also override req.headers etc. for authorization
// ...
// then call the "api" again with the new values
return handlePostRequest(req,res,next);
});
IF you however strictly want to make a POST request (for some reason), you need to supply the sessionID as well, which will be in your cookie. Then the session data will be available.