Skip to content
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

AppBridgeError INVALID_CONFIG: host must be provided #628

Closed
templatetuners opened this issue May 15, 2021 · 17 comments
Closed

AppBridgeError INVALID_CONFIG: host must be provided #628

templatetuners opened this issue May 15, 2021 · 17 comments
Labels

Comments

@templatetuners
Copy link

templatetuners commented May 15, 2021

When I open the app I get this error:

Unhandled Runtime Error
AppBridgeError: APP::ERROR::INVALID_CONFIG: host must be provided

Sometimes I can't even install it because of this.

I have both .env and process.env files completed.
Sometimes I see for a second a message popup to enable cookies but I can't press Enable, the redirect is too fast.

@templatetuners
Copy link
Author

@FiliSantillan
Copy link

The same thing happened to me, I added it by adding the host to the configuration.

A new query parameter named host has been introduced with the release of App Bridge 2.0. The base64-encoded host parameter represents the domain that is currently hosting your embedded app.

We start with the env file, it is important that you have the SHOPIFY_APP_URL defined, since it is the value that I use as host.

SHOPIFY_API_KEY=123
SHOPIFY_API_SECRET=shpss_123
SHOPIFY_API_SCOPES=read_products,write_products,read_script_tags,write_script_tags
SHOPIFY_APP_URL=https://123.ngrok.io

Now, in your next.config.js file you must take that value and send it, as well as the apiKey.

// next.config.js

require("dotenv").config();
const webpack = require("webpack");

const apiKey = JSON.stringify(process.env.SHOPIFY_API_KEY);
const host = JSON.stringify(process.env.SHOPIFY_APP_URL);

module.exports = {
    webpack: (config) => {
        const env = { API_KEY: apiKey, HOST_URL: host };
        config.plugins.push(new webpack.DefinePlugin(env));
        return config;
    },
};

Finally you have to pass that host to the configuration. This must be modified with base64 and that's it. You can see your app from the store you are testing in.

import React from "react";
import App from "next/app";
import Head from "next/head";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/es.json";

class MyApp extends App {
    render() {
        const { Component, pageProps, shopOrigin } = this.props;

        const config = {
            apiKey: API_KEY,
            shopOrigin,
            host: Buffer.from(HOST_URL).toString("base64"),
            forceRedirect: true,
        };

        return (
            <React.Fragment>
                <Head>
                    <title>Sample App</title>
                    <meta charSet="utf-8" />
                </Head>
                <Provider config={config}>
                    <AppProvider i18n={translations}>
                        <Component {...pageProps} />
                    </AppProvider>
                </Provider>
            </React.Fragment>
        );
    }
}

MyApp.getInitialProps = async ({ ctx }) => {
    return {
        shopOrigin: ctx.query.shop,
    };
};

export default MyApp;

@kinngh
Copy link

kinngh commented May 18, 2021

@FiliSantillan wait why are we pulling from env when it's available in the query?

Update your pages/_app.js to include host in getInitialProps and then call it in your config from this.props along side shopOrigin

const { Component, pageProps, shopOrigin, host } = this.props;
const config = { apiKey: API_KEY, shopOrigin, host, forceRedirect: true };
...
...
...

MyApp.getInitialProps = async ({ ctx }) => {
  return { shopOrigin: ctx.query.shop, host: ctx.query.host };
};

And if you're using something like recurring subscriptions, you'll need to get host from the URL and pass it in your return URL

const getHost = (ctx) => {
 const baseUrl = new URL(`https://${ctx.request.header.host}${ctx.request.url}`);
  return baseUrl.searchParams.get("host");  
 };
server.use(
 createShopifyAuth({ async afterAuth(ctx) {
  const { shop, scope, accessToken } = ctx.state.shopify;
  const host = getHost(ctx);
  ACTIVE_SHOPIFY_SHOPS[shop] = scope;
  const returnUrl = `https://${Shopify.Context.HOST_NAME}?host=${host}&shop=${shop}`;
  const subscriptionUrl = await getSubscriptionUrl(accessToken, shop, returnUrl );
  ctx.redirect(subscriptionUrl);
  },
 })
);

@Theantipioneer
Copy link

Theantipioneer commented May 23, 2021

Did this help anyone? I have the same problem, still.
server.js : from the node tutorial.

require("isomorphic-fetch");
const dotenv = require("dotenv");
const Koa = require("koa");
const next = require("next");
const { default: createShopifyAuth } = require("@shopify/koa-shopify-auth");
const { verifyRequest } = require("@shopify/koa-shopify-auth");
const { default: Shopify, ApiVersion } = require("@shopify/shopify-api");
const Router = require("koa-router");

dotenv.config();

Shopify.Context.initialize({
    API_KEY: process.env.SHOPIFY_API_KEY,
    API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
    SCOPES: process.env.SHOPIFY_API_SCOPES.split(","),
    HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/https:\/\//, ""),
    API_VERSION: ApiVersion.April21,
    IS_EMBEDDED_APP: true,
    SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});

const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();

const ACTIVE_SHOPIFY_SHOPS = {};

app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();
    server.keys = [Shopify.Context.API_SECRET_KEY];

    server.use(
        createShopifyAuth({
            afterAuth(ctx) {
                const { shop, scope } = ctx.state.shopify;
               
                ACTIVE_SHOPIFY_SHOPS[shop] = scope;

                ctx.redirect(`/`);
                ctx.redirect(`/?shop=${shop}`);
            },
        })
    );


    const handleRequest = async (ctx) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
        ctx.res.statusCode = 200;
    };

    router.get("/", async (ctx) => {
        const shop = ctx.query.shop;

        if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
            ctx.redirect(`/auth?shop=${shop}`);
        } else {
            await handleRequest(ctx);
        }
    });

    router.get("(/_next/static/.*)", handleRequest);
    router.get("/_next/webpack-hmr", handleRequest);
    router.get("(.*)", verifyRequest(), handleRequest);

    server.use(router.allowedMethods());
    server.use(router.routes());

    server.listen(port, () => {
        console.log(`> Ready on http://localhost:${port}`);
    });
});

.env

SHOPIFY_API_KEY=0000
SHOPIFY_API_SECRET=0000
SHOPIFY_API_SCOPES=read_products
SHOPIFY_APP_URL=https://daee3b7889af.ngrok.io

next.config.js

require("dotenv").config();
const webpack = require("webpack");

const apiKey = JSON.stringify(process.env.SHOPIFY_API_KEY);
const host = JSON.stringify(process.env.SHOPIFY_APP_URL);

module.exports = {
    webpack: (config) => {
        const env = { API_KEY: apiKey, HOST_URL: host };
        config.plugins.push(new webpack.DefinePlugin(env));
        return config;
    },
};

app.js

import App from "next/app";
import Head from "next/head";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";
import ClientRouter from "../components/ClientRouter";

class MyApp extends App {
    render() {
        const { Component, pageProps, shopOrigin, host } = this.props;
        const config = {
            apiKey: API_KEY,
            shopOrigin,
            host,
            forceRedirect: true,
        };
        return (
            <React.Fragment>
                <Head>
                    <title>Sample App</title>
                    <meta charSet="utf-8" />
                </Head>
                <Provider config={config}>
                    <ClientRouter />
                    <AppProvider i18n={translations}>
                        <Component {...pageProps} />
                    </AppProvider>
                </Provider>
            </React.Fragment>
        );
    }
}
MyApp.getInitialProps = async ({ ctx }) => {
    return {
        shopOrigin: ctx.query.shop, host: ctx.query.host 
    };
};

export default MyApp;

@sergey-v9
Copy link

looks like this was fixed by #629

@lubojanski
Copy link

but the tutorial is not updated yet. Still had to add what @kinngh posted ( thanks!).

@kinngh
Copy link

kinngh commented Jun 19, 2021

@lubojanski thanks for reminding that now you don't have to use a separate function to call in on host and instead it's directly available. I've updated the code on my template repository available here or you can directly look at the individual files at pages/_app.js and server.js. Though I hope they kinda update the repo soon.

@joshbedo
Copy link

joshbedo commented Aug 10, 2021

Anyone figure out a fix for this I'm trying to deploy the app to AWS Cloudfront and this issue keeps popping up. Man deploying shopify apps to AWS is a F***** nightmare this really needs worked out better. In 13 years of development I've never seen any company use Heroku everyone is on AWS/Azure.

@rodrigograca31
Copy link

Im still facing this issue.
Seems my code is pretty correct but after moving to vercel it starts giving me this error......

when running locally and using local-tunnel/ngrok all works but after deploying to vercel and changing my URLs it stops working.......

@rodrigograca31
Copy link

@joshbedo I'd appriciate if you could share any insights you found? seems you were deploying to AWS.... did it work? did you change anything?

@MatiSap
Copy link

MatiSap commented Mar 29, 2022

Any updates on this?

@CostierLucas
Copy link

+1

@ghost
Copy link

ghost commented May 29, 2022

Please note there is a bug in some versions of @shopify/app-bridge-react that will cause the "host must be provided error".

This bug randomly occurred after an "npm update" with no code changes.

app-bridge-react 2.0.24 seems to be the only stable version.

Also see https://community.shopify.com/c/shopify-apis-and-sdks/shopify-app-bridge-react-getting-error-host-not-provided-after/m-p/1579622

@towfiqi
Copy link

towfiqi commented Jul 24, 2022

This also happens when the session expires after 24 hours. Had to manually redirect the app to the auth url to reauthenticate the user to fix this issue.

@github-actions
Copy link

github-actions bot commented Oct 7, 2022

This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.

@github-actions github-actions bot added the Stale label Oct 7, 2022
@github-actions
Copy link

We are closing this issue because it has been inactive for a few months.
This probably means that it is not reproducible or it has been fixed in a newer version.
If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.

If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines

Thank you!

@zaaack93
Copy link

For Remix developers,

To resolve this issue, include the following code in the app.tsx file. This addition should fix the issue.

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const url = new URL(request.url);
  const params = new URLSearchParams(url.search);
  const host = params.get('host');
  
  await authenticate.admin(request);
  return json({ apiKey: process.env.SHOPIFY_API_KEY || "", host: host });
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests