bigcommerce / b2b-buyer-portal

B2B Buyer Portal - BigCommerce B2B Edition
MIT License
25 stars 19 forks source link

GraphQL invalid credentials #10

Open sunilit42 opened 4 months ago

sunilit42 commented 4 months ago

I m trying to setup locally b2b portal.

I m getting below error msg :-
{"errors":[{"message":"GraphQL invalid credentials. String is not a JWT"}]}

For temp fix

graphqlBC: function post<T>(data: T): Promise<any> { console.log(store.getState()) let { bcGraphqlToken } = store.getState().company.tokens bcGraphqlToken ='created token and added here'; const config = { Authorization:Bearer ${bcGraphqlToken}, } return graphqlRequest(RequestType.BCGraphql, data, config) },

After adding that graphql calling but getting below error 'Origin' header invalid: The 'Origin' request header value did not match the allowed origin set in the JWT auth token. Please generate a token that is valid for this origin

I created that GraphQL using http://localhost:3000/ as domain but still getting error

Please help us to setup locally.

Thanks

bc-victor commented 4 months ago

@sunilit42 Hello, a couple asks:

Regards

jp06 commented 4 months ago

I am also getting the issue in a local Stencil theme setup. Not sure if I messed up something during setup, but it seems that it's passing an undefined GraphQL token in the request.

In the meanwhile, the workaround I did is to use the storefront API token provided in the Handlebars page context. You can do it somewhere above in the layout template files (base.html, and maybe empty.html also if used in checkout page):

<script>
    window.STOREFRONT_GRAPHQL_API_TOKEN = '{{{ settings.storefront_api.token }}}'
</script>

And then in the b3Fetch.ts file in Buyer Portal code, I updated the graphqlBC method:

const B3Request = {
  /* ... */
  graphqlBC: function post<T>(data: T): Promise<any> {
    const { bcGraphqlToken } = store.getState().company.tokens
    const config = {
      // Authorization: `Bearer  ${bcGraphqlToken}`,
      Authorization: `Bearer  ${bcGraphqlToken ?? window.STOREFRONT_GRAPHQL_API_TOKEN}`,
    }
    return graphqlRequest(RequestType.BCGraphql, data, config)
  },
  /* ... */
}

It appears to work at least for now in my local. Please advise on the proper way to resolve this, thank you!


Update: I also tried tunneling my local Stencil dev server to see if it just doesn't like a localhost origin. It still fails on getting the token from the B2B proxy endpoint. Also putting some request details just in case it helps resolving the issue:

Payload: (I am tunnelling my local with Cloudflare Tunnel)

{
    "storeHash": "<redacted>",
    "method": "post",
    "url": "/v3/storefront/api-token",
    "params": {},
    "data": {
        "channel_id": 1,
        "expires_at": 1866896353,
        "allowed_cors_origins": [
            "https://<redacted>.trycloudflare.com"
        ]
    }
}

Response:

{
    "code": 40010,
    "message": "Bad Requests Error",
    "data": {
        "non_field_errors": "Invalid data",
        "errMsg": "Invalid data; "
    }
}

Update 2: Just realized that the setup is with Script Manager, but what I was doing was setting up with a local theme. I just glossed over it thinking I can just copy the header/footer scripts to the theme during local development. I guess this is currently intended to work with the store URL as origin which also makes sense. I'll stick with my workaround for now as I can't do updates on the Script Manager of the sandbox I am currently working with.

onur-o7 commented 4 months ago

same issue here but i am using headless catalyst setup. building buyers portal with modified b3Fetch.ts is not a solid solution maybe a new way to be able to put allowed orgin to b2b proxy via b2badmin api could be a solution. or going back to using "sf-bcGraphql" session key. i was able to set my own graphql jwt token created for localhost and it was working.

bc-marco commented 4 months ago

Hello @sunilit42 @jp06 @onur-o7 Could you provide the following information of the request that is causing that error, in order to assist you properly:

endpoint request
payload sent
jp06 commented 4 months ago

Hi @bc-marco, here:

Endpoint:

https://api-b2b.bigcommerce.com/api/v2/proxy

Payload:

{
  "storeHash": "<redacted>",
  "method": "post",
  "url": "/v3/storefront/api-token",
  "params": {},
  "data": {
    "channel_id": 1,
    "expires_at": 1866896353,
    "allowed_cors_origins": [
      "https://<slug given by Cloudflare>.trycloudflare.com"
    ]
  }
}

I am tunnelling my local Stencil with Cloudflare Tunnel:

# 3016 being the non-browsersync port
cloudflared tunnel --url http://localhost:3016

Might be worth noting that hardcoding the actual storefront URLs in allowed_cors_origins gives a token. I tried on both of these variants:

bc-marco commented 4 months ago

Thanks for provide the information @jp06

In your case that is not going to work as were accomplishing security requisites, we are not allowed to generate api-tokens to origins that are different from those that are registered on the site

You could update your url sites by doing an update request

curl --request PUT \
  --url 'https://api.bigcommerce.com/stores/[store_hash]/v3/channels/[channel_id]/site' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Token: xxxxxxxxxxxxxxxxx' \
  --data '{"url":"string"}'

Then you can verify which are the registered urls on your site by doing a request to

curl --request GET \
  --url 'https://api.bigcommerce.com/stores/[store_hash]/v3/channels/[channel_id]/site' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Token: xxxxxxxxxxxxxxxxx'

For more information you can check here: https://developer.bigcommerce.com/docs/rest-management/channels/site#get-a-channel-site

sunilit42 commented 4 months ago

Hello @bc-marco

As i see https://api-b2b.bigcommerce.com/api/v2/proxy need b2b client Id , how i can get this one? is that same as b2b api token?

jp06 commented 4 months ago

Hello @bc-marco

As i see https://api-b2b.bigcommerce.com/api/v2/proxy need b2b client Id , how i can get this one? is that same as b2b api token?

You can check the generated B2B Edition footer script in Script Manager for this.

jp06 commented 4 months ago

Hi @bc-marco, understandable, that makes sense. From the docs, it seems to only allow one site URL anyway, so I probably won't touch that. Thanks for the clarification!

sunilit42 commented 4 months ago

@bc-marco @jp06 so what is final solution to call from locally for temp i using @jp06 way add window.STOREFRONT_GRAPHQL_API_TOKEN} and use it

bc-marco commented 4 months ago

Hello @bc-marco

As i see https://api-b2b.bigcommerce.com/api/v2/proxy need b2b client Id , how i can get this one? is that same as b2b api token?

Could you show me what is the current payload of your request? That will help us to provide you better support

jp06 commented 4 months ago

@bc-marco I think he already solved it. But now just wanted to ask what the preferred final solution is, he's currently using the workaround I did for now (he said in the Slack channel).

@sunilit42 So, to review the ways so far that I know for Buyer Portal development with Stencil storefront:

  1. Tunnel local Stencil and add the header/footer scripts to theme code (in local only, don't commit) and modify Buyer Portal code to make use of {{{ settings.storefront_api.token }}}.
  2. The setup mentioned in the documentation. Adding the header/footer scripts in Script Manager and accessing the Buyer Portal directly in the store. The documentation doesn't mention it so I'm not sure, but you may also need to remove the third-party that came along with B2B Edition setup (@bc-marco please confirm if this is the case).

It really depends on preference, if no one else tests/use the Buyer Portal in the sandbox store when you're developing, method 2 might be preferred. I use method 1 as the Buyer Portal eventually has to be tested by someone else. I find it more convenient to just have the local development scripts in my local, and I won't have to "toggle" the header and footer scripts in Script Manager to go back and forth to using local and deployed Buyer Portal (well, I know I can use a script to automate switching with the Scripts API but can't be bothered yet). You have your own circumstances so it's up to you in the end, unless maybe others can provide a better way.

bc-victor commented 4 months ago

@jp06 I've submitted a new feature request for this issue - specifically, we'll support the token in the handlebars page context and we'll provide better documentation for Stencil CLI users

jp06 commented 4 months ago

Hi @bc-victor, that would be great thank you!

I was just thinking about implementing something similar myself, adding it to the window.B3.setting object like this, which just serves as an optional override for local development:

window.B3 = {
    setting: {
        storefront_api_token: "{{{ settings.storefront_api.token }}}",
    },

As for consuming it, I'm not sure yet how to best go about it but I'll probably do something like this in loginInfo.ts for the loginInfo function:

export const loginInfo = async () => {
  const channelId = B3SStorage.get('B3channelId')
  const loginTokenInfo = getloginTokenInfo(channelId)
  const {
    data: { token },
  } = await getBCGraphqlToken(loginTokenInfo)

  store.dispatch(
    setbcGraphqlToken(token ?? globalB3?.setting?.storefront_api_token)
  )
}

For headless, one can probably put a token created with this instead of {{{ settings.storefront_api.token }}}.

jp06 commented 3 months ago

Great! Trying again in local with code synced to upstream, it seems like there is no need to tunnel now?