Search code examples
node.jsshopifykoashopify-appkoa-session

How to access a token in session (Shopify Access Token)


I mostly work on front-end so I'm not super familiar with NodeJS. I'm working on a Shopify Custom App and purpose of this app is when order placed it will receive webhook request and with that request it will send some data to other API (billing application)

I built shopify app with shopify app cli and my server.js file is like this;

import "@babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "@shopify/koa-shopify-auth";
import graphQLProxy, { ApiVersion } from "@shopify/koa-shopify-graphql-proxy";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
import session from "koa-session";
const { receiveWebhook } = require("@shopify/koa-shopify-webhooks");
import * as handlers from "./handlers/index";
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
    dev,
});
const handle = app.getRequestHandler();
const { SHOPIFY_API_SECRET, SHOPIFY_API_KEY, SCOPES } = process.env;
app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();

    server.use(
        session(
            {
                sameSite: "none",
                secure: true,
            },
            server
        )
    );
    server.keys = [SHOPIFY_API_SECRET];
    server.use(
        createShopifyAuth({
            apiKey: SHOPIFY_API_KEY,
            secret: SHOPIFY_API_SECRET,
            scopes: [SCOPES],

            async afterAuth(ctx) {
                //Auth token and shop available in session
                //Redirect to shop upon auth
                const { shop, accessToken } = ctx.session;
                // This accessToken is what I need on other scope

                ctx.cookies.set("shopOrigin", shop, {
                    httpOnly: false,
                    secure: true,
                    sameSite: "none",
                });

                // Register Webhook
                handlers.registerWebhooks(
                    shop,
                    accessToken,
                    "ORDERS_PAID",
                    "/webhooks/orders/paid",
                    ApiVersion.October20
                );

                console.log(accessToken);

                ctx.redirect("/");
            },
        })
    );

    const webhook = receiveWebhook({ secret: SHOPIFY_API_SECRET });
    router.post("/webhooks/orders/paid", webhook, (ctx) => {
        let user_id = ctx.state.webhook.payload.customer.id;

        console.log("received webhook, user_id: ", user_id);

        //console.log("ctx", ctx);

        // I need that accessToken here to get some more info from Admin API with GraphQL
        let accessToken = "???"
        
        handlers
            .graphqlRequest(
                accessToken,
                "https://my-store.myshopify.com/admin/api/2020-10/graphql.json",
                `{
                    customer(id: "gid://shopify/Customer/${user_id}") {
                        email
                        metafields(first: 5) {
                            edges {
                                node {
                                    key
                                    value
                                }
                            }
                        }
                    }
                }`
            )
            .then((res) => {
                console.log("res => ", res);
            })
            .catch((err) => {
                console.log("err => ", err);
            });
    });

    server.use(
        graphQLProxy({
            version: ApiVersion.October20,
        })
    );
    router.get("(.*)", verifyRequest(), async (ctx) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
        ctx.res.statusCode = 200;
    });
    server.use(router.allowedMethods());
    server.use(router.routes());
    server.listen(port, () => {
        console.log(`> Ready on http://localhost:${port}`);
    });
});

createShopifyAuth() method gets an accessToken with my app secret and app api key and I can use it in afterAuth() method but I also need that token in router.post() method to get more info from Shopify Admin API.

According to the Shopify docs(or what I understand), that key is available in session but how can I access that session data? or what can I use that token in router.push()?


Solution

  • The session is created only for the user who logged in the app from the admin panel. This is true for the online method for creating an access token.

    If you are requesting the access token from a webhook request (a.k.a a request that doesn't require you to relogin in the app) you will not be able to access the session and you will not be able to get the access token. In addition the session expires at some point.

    In order to use the Access Token in a webhook request you need to create an offline access token which is valid indefinitely. createShopifyAuth has an option for creating an offline access token, you just need to add accessMode: 'offline' to your request (more on this here)

    Example:

    createShopifyAuth({
          apiKey: SHOPIFY_API_KEY,
          secret: SHOPIFY_API_SECRET_KEY,
          accessMode: 'offline',
          scopes: ['read_products', 'read_orders'],
    

    After you create the Offline Access Token you need to save that in a database (or some other way) and request it from the webhook route in order make your graphql request.

    That's pretty much the just of it.