Setting a test header in GraphQL Playground that I want to use causes "Server cannot be reached" - node.js

So I've created a bunch of mutations and queries and stitched them together that works and wanted to introduce authentication into the mix. I added an HTTP Header "x-token" to hold my sign-in token to be able to delete things like their job or the user itself.
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, "notSoSecret");
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
const server = new ApolloServer({
typeDefs: schema,
resolvers,
formatError: error => {
// remove the internal sequelize error message
// leave only the important validation error
const message = error.message
.replace('SequelizeValidationError: ', '')
.replace('Validation error: ', '');
return {
...error,
message,
};
},
context: async ({ req }) => {
const me = await getMe(req);
return {
models,
me,
secret: "notSoSecret",
}
},
path: "/graphql"
});
server.applyMiddleware({ app });
sequelize.sync().then(async () => {
createUsersWithJob();
});
app.get("/playground", graphiql({ endpoint: "/graphql" }));
const handler = serverless(app);
export { handler };
const createUsersWithJob = ... //creates seed data
So when I add the token and I look into my command line console, I actually see that I'm setting the header that I want, but it loops over and over again and doesn't stop. Also playground gets an error "Server cannot be reached"
{
"error": "Response not successful: Received status code 400"
}
and running a deleteUser mutation does not work, or any other mutation and query for that matter until I remove the HTTP Header that I set on playground.
There is a secondary issue where everything in this root file runs twice, but that's not as big for me at the moment as the header issue outlined.
If anyone has any insight into this, I'd love to know more. Thanks in advance.
edit: just a quick edit to say that it works fine when I hardcode a pre-existing user.

I had quite a struggle to get the React version of GraphQL Playground working within a very simple html setup, but I figured something out that might help you as well (fingers crossed).
I added a headers section to the config in the GraphQLPlayground.init call, like so:
const root = document.getElementById('root');
GraphQLPlayground.init(root, {
endpoint: "/graphql",
headers: {
"Authorization": "Bearer " + token
}
})
I have an element with an id root since this is embedded in HTML.
Not sure this will help you though, as I just noticed from your code sample you're calling graphiql which is a different GraphQL client than GraphQL Playground..
GraphIQL: https://github.com/skevy/graphiql-app
GraphQL Playground: https://github.com/prisma/graphql-playground

Related

"Sign in with Apple" redirects to a blank white page on PWA

I've implemented "Sign in with Apple" on my site. When I try it on my phone, it redirects me to a blank white page with the same URL as the redirect_uri I've configured.
I can't find any info on why this is happening. What's a possible fix?
UPDATE
It seems as if Apple JS SDK is creating a FORM HTML DOM element, sets the POST URL of the FORM to point to the redirect_uri, and finally programmatically clicks form.submit(). This for some reason causes the page to navigate to the redirect_uri and show the POST response as a new page.
I figured this out by tracking the Apple JS SDK in the debugger.
Here is my code
//---- Frontend ----
AppleID.auth.init({
clientId : '<client_id>',
scope : 'email',
redirectURI : 'mySite.com/apple_auth',
state : 'origin:web',
nonce : Date.now(),
//usePopup : true //not using this one. When false or undefined, Apple will make a POST request to the defined redirect_uri
})
// Listen for authorization success.
document.addEventListener('AppleIDSignInOnSuccess', (event) => {
// Handle successful response.
console.log(event.detail.data);
});
// Listen for authorization failures.
document.addEventListener('AppleIDSignInOnFailure', (event) => {
// Handle error.
console.log(event.detail.error);
});
//...
myButton.onClick = ()=>{
try {
var res = await AppleID.auth.signIn()
} catch(err) {
var x = 0
}
}
//---- Backend ----
var appleSignin = require("apple-signin-auth")
app.express.post('/apple_auth', async (req, res)=>{
var body = req.body
try {
const appleRes = await appleSignin.verifyIdToken(
body.id_token, // We need to pass the token that we wish to decode.
{
audience: '<client_id', // client id - The same one we used on the frontend, this is the secret key used for encoding and decoding the token.
ignoreExpiration: true, // Token will not expire unless you manually do so.
}
)
//do something with the Apple response
} catch (err) {
// Token is not verified
console.error(err)
}
})
From the documentation...
The HTTP body contains the result parameters with a content-type of application/x-www-form-urlencoded.
Make sure you've configured the urlencoded() body-parsing middleware in your Express app.
app.use(express.urlencoded());
Make sure you check for errors and actually send a response from your /apple_auth Express route
const { code, id_token, state, user, error } = req.body;
if (error) {
return res.status(500).send(error);
}
try {
const appleRes = await appleSignin.verifyIdToken(id_token, {
audience: "<client_id>",
ignoreExpiration: true,
});
// do something with the Apple response, then send a response
res.send(appleRes.sub);
} catch (err) {
console.error(err);
res.sendStatus(500); // send a 500 response status
}

Using Express and Axios, Axios will not recognize PUT Requests

I have dumbed down my use case to a simple test, in which I click a button and an UPDATE put request is sent using an axios API. Three other request methods-- post, get, and delete, all work properly and are recognized. My PUT method, however, gives a 404 Not Found error, as though I havent established the method with express in my server.
This is the code for the request, triggered by this event handler (for the button):
const handleUpdate = async (e, id) => {
e.stopPropagation();
// navigate(`/restaurants/${id}/update`);
//update test
try {
const updatedRestaurant = await RestaurantFinder.put(`/${id}`, {
name: "taco bell",
location: "dogtown",
price_range: "2"
});
console.log(updatedRestaurant);
navigate("/");
} catch(err) {
console.log(err);
}
};
This is the api's instantiation:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:3001/api/v1/restaurants"
}
);
This is the request in Express. Note that the first log "does this exist???" is never displayed. The address for the put request to "http://localhost:3001/api/v1/restaurants/id" is never found.
//UPDATE a restaurant
app.put("/api/v1/restaurants/:id"), async (req, res) => {
console.log("does this exist???");
try {
const results = await db.query(
"UPDATE restaurants SET name = $1, location = $2, price_range = $3 where id = $4 returning *",
[req.body.name, req.body.location, req.body.price_range, req.params.id]
);
res.status(200).json({
status: "succes",
data: {
restaurant: results.rows[0],
},
});
} catch (err) {
console.log(err);
}
console.log(req.params.id);
console.log(req.body);
};
I have pored over StackOverflow for answers, I think I've viewed over 50 posts at this point. This is something that should be so simple, and yet I can't find a single answer for it or see where it's happening to anyone else.
If anyone could please help walk me through what is going wrong, I would be so grateful!
Edit 1 #Stephen:
const app = express();
app.use(cors(
{
methods: ["POST", "GET", "DELETE", "PUT"]
}
));
app.use(express.json());
So i changed it to this with no luck. Originally i was just using cors(), and my understanding is that it defaults to allowing a put method.
Does your server allow the put method? Perhaps your server only allows the others in the access-control-allow-methods field.
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
For testing over http, make sure both of these are set by your server.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS

How to validate header in hapi

I want to add a middleware to check the header values. if the expected do not present in the header then I want throw an error on API response. Following code is throwing error and I can see it in the console but I want to send this error to the user.
const server = new Server({
host: config.get('host'),
port: config.get('port' )
});
await server.register(require('#hapi/inert')); // eslint-disable-line #typescript-eslint/no-var-requires
server.register({
plugin: AuthService
})
server.route(
// mapping route paths with context path
routes.map((datum) => {
return {
...datum,
path: `${config.get('basePath')}${datum.path}`
};
})
);
plugin
import { notFound } from '#hapi/boom';
export const AuthService = {
name: 'authService',
version: '1.0.0',
register: async function (server: any, options: any) {
throw notFound(`Unauthorized user not found`);
}
};
Well, I think you are thinking it the express way too much. You should look at the authentication part of Hapi.
You can create a custom strategy and return an error if the header is not present.
Also, you can check how to use a Joi.schema() for all your routes.

API Token returning 401 Unauthorised when its working in URL

I am making a Multi-Purpose API Service and as I got the token in the URL working perfect and authorising as expected with a 200. I've been having issues with the token not authorising with curl command or superagent, as its always return a 401 error.
auth.js
const { DB } = require('../src/Routes/index.js');
module.exports = function auth(req, res, next) {
if(!req.query.apiKey) {
return res.status(401).json({"error": "401 Unauthorized", message: "API Token is missing in the query. You will need to generate a Token and put in the apiKey query."})
} else {
let check = DB.filter(k => k).map(i => i.apiToken);
console.log(check);
let t = check.some(e => e == req.query.apiKey)
if(t !== true)
return res.status(401).json({"error": "401 Unauthorized", message: "The API Key you provided is invalid. Please refer to our Docs () and Generate a Token ()"});
return next();
}
}
This is the middleware for the token, I am using this in my routers so then the token will authenticate. However, if I remove the if statement for checking if an API Token is present. It seem to fix the issue kinda but always authenticates with any key (even ones not saved in the db) and is still not properly fixed.
and an example for requesting endpoint with token on a Discord Command:
const { MessageEmbed } = require("discord.js");
const { get } = require("superagent");
exports.run = async (bot, message, args) => {
const { body } = await get(`https://example.com/generators/3000years`)
.query({ image: message.author.displayAvatarURL({ dynamic: true, size: 2048 }) })
.set("Authorization", `Bearer MY_TOKEN`);
const embed = new MessageEmbed()
.setTitle(`**3000 Years**`)
.attachFiles({ attachment: body, name: "3000years.png" })
.setImage("attachment://3000years.png")
.setColor(`#ed8a5c`);
message.channel.send(embed);
}
You can see that if I authorise with Superagent, it will not work and return a 401 Unauthorised in the console.
I would like to ask why this is doing this and if I did something wrong. Any help is appreciated.

How fix 'Error while running `getDataFromTree` Network error: localStorage is not defined'?

Everythings works but I keep getting: Error while running getDataFromTree Network error: localStorage is not defined. I tried conditionals to only send req from client but it did'nt work. All the requests seem to be coming from server.
I'm using google Oauth and passport for authentication in NextJS
Should I create authLink from a different file? Any clue?
Usefull links:
https://www.apollographql.com/docs/react/recipes/authentication/
https://github.com/zeit/next.js/tree/canary/examples/with-apollo
I've tried to conditional logic to only send req from client but then, logic stops working. Looks like most of nextJS reqest are from serverside.
let apolloClient = null
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? Bearer ${token} : "",
}
}
});
function create(initialState) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
const isBrowser = typeof window !== 'undefined'
console.log(isBrowser);
return new ApolloClient({
connectToDevTools: isBrowser,
ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
link: authLink.concat(new createHttpLink({
uri: 'http://localhost:3000/graphql', // Server URL (must be absolute)
credentials: 'same-origin', // Additional fetch() options like credentials or headers
// Use fetch() polyfill on the server
fetch: !isBrowser && fetch
})),
cache: new InMemoryCache().restore(initialState || {})
})
}
export default function initApollo(initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
return create(initialState)
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState)
}
return apolloClient
}
I need apollo to send cookies with every request, so my react component can find if the user is logged in.
It's breaking my head, any help appreciated. Thank you in advance.

Resources