hasura / nodejs-graphql-subscriptions-boilerplate

Boilerplate to setup GraphQL subscriptions in your nodejs code
https://hasura.io
MIT License
80 stars 9 forks source link

How to set x-hasura-admin-secret/x-hasura-access-key #3

Open scheung38 opened 5 years ago

scheung38 commented 5 years ago

pointed to my Heroku instance

$ node index.js

Err { extensions: { path: '$', code: 'start-failed' }, message: 'cannot start as connection_init failed with : x-hasura-admin-secret/x-hasura-access-key required, but not found' }

But x-hasura-admin-secret is already set inside Heroku?

How do we set x-hasura-admin-secret from within nodejs? Tried dotenv package and included in .env file x-hasura-admin-secret=MY_SECRET

But still same as above?

EDIT: this only works if I remove security from Hasura GraphQL engine? Namely HASURA_GRAPHQL_ADMIN_SECRET is removed, but of course zero security

NicTanghe commented 5 years ago

Some explanation annywhere on how to get x-hasura-acces-key's whould be extremely usefull. Or should you realy provide people with the admin password for your DP probably not.

In the tutorials i noticed that only using Authorization is enaugh but in my console i have to use both authorization and the admin key or it doesn't work. (including from the client ???

JohnMusleh commented 4 years ago

any solution to this yet??

myorkgitis commented 4 years ago

Having the same problem as well. I'm guessing the client is not supposed to be using the HASURA_ADMIN_SECRET and only the Bearer token?

myorkgitis commented 4 years ago

I figured it out, checkout this sample: https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/todo-auth0-jwt

The important step is adding your PEM key to the Hasura Heroku server so it can authenticate correctly. If you do not have HASURA_GRAPHQL_JWT_SECRET: {"type":"RS256", "key": "<the-certificate-data-in-one-line>"} on your Heroku instance, your Bearer token will not authenticate.

clementvp commented 4 years ago

@myorkgitis Can you confirm that a graphql client is not supposed to use the HASURA_ADMIN_SECRET ? even with a HASURA_GRAPHQL_JWT_SECRET setted in the hasura conf ?

I can query but subscriptions not work with a HASURA_ADMIN_SECRET header.

myorkgitis commented 4 years ago

@clementvp Here is my client setup on the front end.

const createApolloClient = authToken => {
  return new ApolloClient({
    link: new WebSocketLink({
      uri: HASURA_GRAPHQL_URL,
      options: {
        reconnect: true,
        connectionParams: {
          headers: {
            Authorization: `Bearer ${authToken}`
          }
        }
      }
    }),
    cache: new InMemoryCache()
  });
};

I'm pretty sure the HASURA_ADMIN_SECRET is not needed for the front end client to access anything since it is really just a password. It would not make sense for this to be distributed in any way to the front end.

With the HASURA_GRAPHQL_JWT_SECRET key value provided by Auth0, the Hasura back-end can authenticate the JWT passed to the front-end client after logging in through Auth0. In the params of the callback url after logging in through Auth0, there is the Bearer token which is then extracted by the react-auth0-spa.js and passed into the App to be used in the client init I pasted above.

k96white commented 4 years ago

@myorkgitis does this works, i am trying this out using Apolloclient and getting this error while using the query

[GraphQL error]: Message: x-hasura-admin-secret/x-hasura-access-key required, but not found, Location: undefined, Path: undefined

clementvp commented 4 years ago
const createApolloClient = authToken => {
  return new ApolloClient({
    link: new WebSocketLink({
      uri: "wss://url/v1/graphql",
      options: {
        reconnect: true,
        connectionParams: {
          headers: {
             Authorization: `Bearer ${authToken}`
          }
        }
      }
    }),
    cache: new InMemoryCache()
  });
};

@k96white This is working for me with subscriptions and ws subscription but carrefull with http link instead of ws link, syntax is not the same:

const createApolloClient = (authToken) => {
  return new ApolloClient({
    link: new HttpLink({
      uri: "https://hasura.cairncross.ovh/v1/graphql",
      headers: {
         Authorization: `Bearer ${authToken}`
      },
    }),
    cache: new InMemoryCache(),
  });
};
saul-data commented 4 years ago

I am having the same issue when using an Authorization header. This is strange behaviour because the client should not need the admin key. That would expose a security flaw too.

I also find that when running with an admin key, its pretty slow. We are talking adding on 100ms to 200ms per request. I disabled the admin key and it runs much faster.

https:///v1/graphql Header: Authorization: Bearer eyJhbGciOiJIUz...

{
    "errors": [
        {
            "extensions": {
                "path": "$",
                "code": "access-denied"
            },
            "message": "x-hasura-admin-secret/x-hasura-access-key required, but not found"
        }
    ]
}
danicunhac commented 4 years ago
const httpsLink = new HttpLink({
  uri: HTTPS_URL,
  headers: {
    'x-hasura-admin-secret': HASURA_SECRET
  }
});

const wssLink = new WebSocketLink({
  uri: WSS_URL,
  options: {
    reconnect: true,
    connectionParams: {
      headers: {
        'x-hasura-admin-secret': HASURA_SECRET
      }
    }
  }
});

const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wssLink,
  httpsLink
);

const createApolloClient = () => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link
  });
};

const client = createApolloClient();

I have tried this and worked!

brunobraga95 commented 4 years ago

any updates here? I am having the same issue

rshashik commented 3 years ago

Iam getting invalid x-hasura-admin-secret/x-hasura-access-key in graphql-request client. i have set the HASURA_SECRET in .env file and set the header as 'x-hasura-admin-secret': process.env.HASURA_SECRET.

danicunhac commented 3 years ago

Iam getting invalid x-hasura-admin-secret/x-hasura-access-key in graphql-request client. i have set the HASURA_SECRET in .env file and set the header as 'x-hasura-admin-secret': process.env.HASURA_SECRET.

That's because if you're using React, you have to name it REACT_APP_HASURA_SECRET. I also did this: image

AntouanK commented 3 years ago

had the same issue.

solution from me came from here --> https://github.com/apollographql/subscriptions-transport-ws/issues/511#issuecomment-463849793

basically, on the example given in the README, instead of

const getWsClient = function(wsurl) {
  const client = new SubscriptionClient(
    wsurl, {reconnect: true}, ws
  );
  return client;
};

you do

const getWsClient = function (wsurl) {
  const client = new SubscriptionClient(
    wsurl,
    {
      reconnect: true,
      connectionParams: {
        headers: {
          "content-type": "application/json",
          "x-hasura-admin-secret": "myadminsecretkey",
        },
      },
    },
    ws
  );
  return client;
};
danicunhac commented 3 years ago

We have two solutions in here already, can someone close this? Thank you!

AntouanK commented 3 years ago

would be good to add it in the main example in the readme, to avoid anyone else asking about this.

rwieruch commented 3 years ago

You should not put the admin secret into the client application... Anyone can just take this information and make requests to your server on your behalf. Instead, follow this tutorial series to connect Auth0 with Hasura.

danicunhac commented 3 years ago

You should not put the admin secret into the client application... Anyone can just take this information and make requests to your server on your behalf. Instead, follow this tutorial series to connect Auth0 with Hasura.

That's why you should use a .env file

skworden commented 3 years ago

Let's not get everyone reading this hacked. Env files do not prevent your token from being accessed by users on the client. User's shouldn't be allowed to make api calls from the client using an admin key. The token is in the request headers which are accessible by anyone using your client. Every user should use their own token. The admin key should be avoided, even internal systems should be issued their own keys and roles.

rwieruch commented 3 years ago

Let's not get everyone reading this hacked. Env files do not prevent your token from being accessed by users on the client. User's shouldn't be allowed to make api calls from the client using an admin key. The token is in the request headers which are accessible by anyone using your client. Every user should use their own token. The admin key should be avoided, even internal systems should be issued their own keys and roles.

Thank you.

heblol commented 3 years ago
const httpsLink = new HttpLink({
  uri: HTTPS_URL,
  headers: {
    'x-hasura-admin-secret': HASURA_SECRET
  }
});

const wssLink = new WebSocketLink({
  uri: WSS_URL,
  options: {
    reconnect: true,
    connectionParams: {
      headers: {
        'x-hasura-admin-secret': HASURA_SECRET
      }
    }
  }
});

const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wssLink,
  httpsLink
);

const createApolloClient = () => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link
  });
};

const client = createApolloClient();

I have tried this and worked!

This is extremely unsafe.

DONT PUT YOUR ADMIN KEY ANYWHERE IN THE CLIENT

I want to emphasise this, .env file do NOT protect anything. Please think about this, how else can the client make the call to the server if it cannot read the .env file?

The .env file its only protected if you want to send it to a git server, since (if you put it in your .gitignore) will not be send to the git server. So, if your git is public, your keys are not exposed. This does NOT make it save to be used on the client side! Its totally exposed. Please downvote the solutions so its clear those are NOT SAVE solutions.

heblol commented 3 years ago

I found it! Finally.

The tutorial i viewed put the HASURA_GRAPHQL_JWT_SECRET in the Heroku cloud. But you should not go to Heroku, but instead to the Hasura.io project details and add the environment at that location.

login > edit button at the project > Env Vars > Create here your environment variable called HASURA_GRAPHQL_JWT_SECRET with value generated from this page https://hasura.io/jwt-config/

more info https://hasura.io/docs/latest/graphql/core/guides/integrations/auth0-jwt.html

Hope this helps

younfuckinghoo commented 3 years ago

We have two solutions in here already, can someone close this? Thank you!

i cant find the solution in here.

younfuckinghoo commented 3 years ago

it seems most of replier do not get it.the problem is when i use "subscription" in hasura,i got a message : cannot start as connection_init failed with : invalid x-hasura-admin-secret/x-hasura-access-key. but there is no problem while i use "query" and "mutation".

hemsingh780 commented 3 years ago

this is working for me have a look at it ---->

const client = new ApolloClient({ cache, link:new HttpLink( {uri:'https://dsafa.hasura.app/v1/graphql', headers:{ 'x-hasura-admin-secret':'BoZg6QhvkRVJ7mT3yo5eygQcrRFhmfs4vFdM7G98vRdsadWg13OS9ghJcAa7y0mTVVf' } } ), })

langford-bc commented 3 years ago

Hope it's ok reaching out here. I've followed the thread, but am just starting out and am not making my request to Hasura with a client. I'm just doing a basic API post request to the Hasura endpoint with Postman. I'm using the x-hasura-admin-secret and value from Request Headers details in the API panel.

However I get the error: "message": "x-hasura-admin-secret/x-hasura-access-key required, but not found"

But there's a status code in Postman of 200. Am I completely off-base with how to properly connect to this endpoint?

heblol commented 3 years ago

Hope it's ok reaching out here. I've followed the thread, but am just starting out and am not making my request to Hasura with a client. I'm just doing a basic API post request to the Hasura endpoint with Postman. I'm using the x-hasura-admin-secret and value from Request Headers details in the API panel.

However I get the error: "message": "x-hasura-admin-secret/x-hasura-access-key required, but not found"

But there's a status code in Postman of 200. Am I completely off-base with how to properly connect to this endpoint?

Hi there, in Postman you should add in the header the following information:

x-hasura-admin-secret: HASURA_ADMIN_SECRET. Putting this manually in postman is fine. Never put this in the client. Your admin secret should never be visible for anyone else then the owner of the project. You can get the x-hasura-admin-secret on hasura. Its shown when you want to create a query. Not logged in atm, give a reply if you cannot find it.

Postman:

Screenshot 2021-11-01 at 17 38 41
langford-bc commented 3 years ago

@heblol Thanks for the pointers. I put these keys and values into the Postman client, but received the following error.

hasura-postman-fetch-error

This noted that x-hasura-admin-secrete/x-hasura-access-key is required. I'm unsure what x-hasura-access-key refers to?

Many thanks again.

tintin10q commented 2 years ago

it seems most of replier do not get it.the problem is when i use "subscription" in hasura,i got a message : cannot start as connection_init failed with : invalid x-hasura-admin-secret/x-hasura-access-key. but there is no problem while i use "query" and "mutation".

This is also what I have been thinking while reading this thread.

This is also my issue. Everything works fine with query and mutation but not with subscriptons. I have been using middleware to inject the users jwt token into the request if its available but for some reason this does not work for subscriptions.

Setting the header when you create the appolloclient does not work because when the user opens the app the jwt token will not be there or it could change after the creation of the client.

The approach trough middleware works well, but I get the x-hasura-admin-secret/x-hasura-access-key required but not found message with subscriptions.

This is how my code looks:

import {ApolloClient} from 'apollo-client';
import {createHttpLink} from 'apollo-link-http';
import {WebSocketLink} from 'apollo-link-ws';
import {InMemoryCache} from 'apollo-cache-inmemory';
import {ApolloLink, split} from 'apollo-link';
import {setContext} from '@apollo/link-context';
import {getMainDefinition} from 'apollo-utilities';
import {get_jwt} from "./state/get_jwt";

const uri = 'app.hasura.app/v1/graphql';
const admin_secret = "secret" ; // Set to falsy to not include

// HTTP connection to the API
const httpLink = createHttpLink({
    uri: `https://${uri}`,
});

// Create the subscription websocket link
const wsLink = new WebSocketLink({
    uri: `wss://${uri}`,
    options: {
        reconnect: true,
    },
});

const GQLink = split(
    // split based on operation type
    ({query}) => {
        const definition = getMainDefinition(query);
        return definition.kind === 'OperationDefinition' &&
            definition.operation === 'subscription';
    },
    wsLink,
    httpLink
);
// Cache implementation
const cache = new InMemoryCache();

// Add token from state if its available
const authMiddleware = setContext((_request, {headers}) => {
    if (admin_secret)
    {
        console.warn("Do not forget to remove admin key from apollo client middleware!!");
        return ({
            headers: {
                ...headers, Authorization: `Bearer ${get_jwt()}`, 'X-Hasura-Admin-Secret': admin_secret
            }
        });
    } 
    else
    {
        return ({
            headers: {
                ...headers, Authorization: `Bearer ${get_jwt()}`
            }
        });
    }
});

// Create the apollo client
const apolloClient = new ApolloClient({
    link: ApolloLink.from([authMiddleware, GQLink]),
    cache,
    connectToDevTools: true // TODO turn this off
});

export {apolloClient};

Only if I add the header directly to the options of the wss link does it work.

connectionParams: {
            headers: {
                'x-hasura-admin-secret': admin_secret
            }
        }

I assume that if I add the get_jwt like in the middleware to the connectionParams.headers instead that it will also work. But the jwt token might change, so that is why I have to run the middleware every time.

For some reason, the middleware does not work with subscriptions but does work with https/queries/subscriptions. I have no idea why but it is kind of annoying.

shaopengwu commented 2 years ago

I found it! Finally.

The tutorial i viewed put the HASURA_GRAPHQL_JWT_SECRET in the Heroku cloud. But you should not go to Heroku, but instead to the Hasura.io project details and add the environment at that location.

login > edit button at the project > Env Vars > Create here your environment variable called HASURA_GRAPHQL_JWT_SECRET with value generated from this page https://hasura.io/jwt-config/

more info https://hasura.io/docs/latest/graphql/core/guides/integrations/auth0-jwt.html

Hope this helps

This solution works for me. Thanks

joaotux commented 2 years ago

I solved this problem as follows:

1 - Access: https://cloud.hasura.io/ 2 - Go in "Env Vars" 3 - Click on "New Env Var" 4 - Add the HASURA_GRAPHQL_JWT_SECRET the same way it is in hiroku

bastianhilton commented 2 years ago

I faced the same issue and found a solution for those in Nuxtjs connecting to Hasura using nuxt and apollo extension:

import { InMemoryCache } from "apollo-cache-inmemory"; export default function(context){ return { httpLinkOptions: { uri: 'https://hasuraapilink.hasura.app/v1/graphql', credentials: 'same-origin', headers: { 'x-hasura-admin-secret': 'x-hasura-admin-secret-from-your-console-in-the-api-section' } }, cache: new InMemoryCache(), wsEndpoint: 'ws://hasuraapilink.hasura.app/v1/graphql', } }

Sefibrah commented 2 years ago

Maybe this can help? Try whitelisting your domain so that Hasura knows you're not some random guy trying to hack it.

Screenshot 2022-04-07 at 20 22 08
joheee commented 2 years ago

try to use this

import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";

export const GetClient =()=>{ return new ApolloClient({ link: new HttpLink({ uri: 'https://binterest.hasura.app/v1/graphql', headers: { "x-hasura-admin-secret": "mJdIAJ1vY9nCTlYSRVpFdn3SlpidB1hkqkCSjy6MJKO627qBsDg2jN41YYYki2QB" } }), cache: new InMemoryCache() }) }

refirst11 commented 2 years ago

Add NEXT_PUBLIC to the environment variable, I solved it with that...

hussainb commented 1 year ago

connectionParams can also be a function which can return the headers as shown below:

Unlike the static object, When connectionParams is a function, it gets evaluated before each connection.

const wsLink = new GraphQLWsLink(
  createClient({
    url: `wss://${GRAPHQL_URL}`,
    connectionParams: () => {
      return {
        headers: {
          "x-hasura-admin-secret": "hasura-token" : ""
        }
      };
    }
  })
);

reference: https://stackoverflow.com/questions/60048402/apollo-subscriptions-jwt-authentication

kimgold2010 commented 9 months ago

I had the same problem.

We have 2 ways to fix it:

  1. Use HASURA_GRAPHQL_JWT_SECRET
  2. Split header 'x-hasura-admin-serect/x-hasura-access-key' into 2 fields: headers = { 'x-hasura-admin-serect': 'admin_serect', 'x-hasura-access-key': 'admin_serect' };

Hope this can help you.