Embedded app: Authenticated REST routes with Koa, and identifying shop users

Solved
rorz
Shopify Partner
14 0 4

Hello there,

 

I've been following the React + Node tutorial to work on an embedded app concept. One of the core features of an app like this (which doesn't seem to be covered in that much detail in the — otherwise great — tutorial) is communicating back to our own database / API service.

 

In principle, I want to allow my app to communicate back to my own database in order to store merchant / product settings. So, I've set up a custom GET route (as per this thread) in order to start handling data flows out of my app.

 

I'm familiar with Express, but Koa is new to me. Having discovered the shop property of ctx.session within Koa, I am using this as the basis of verifying who the owner is and updating the database accordingly, for example:

 

--server.js--

  router.get("/app-api/do-something", verifyRequest(), async (ctx) => {
    const { shop }  = ctx.session; // returns '<my-store>.myshopify.com`
    // ... Update the database, using the value of "shop" as my user ID
    // ... Handle the rest of the request
  });

 

Is this the best way of authenticating REST requests 'within' my app? Or is there:

  • A 'better' way of defining who the current 'shop' / merchant is for the purposes of external API / DB operations?
  • Another variable (such as a merchant ID or similar) that would be better-suited for tracking who the current user / store is?
  • A way for malicious actors to bypass my above code and spoof the "shop" value (i.e. there is a better way of doing this)?

 

Another reason why I am asking this is because I want to be able to identify shops for the purposes of linking their usage of my app, to my own accounts.

 

For example, if I want to send an email to the merchant about an update on their app usage, can I access their details (with their permission, of course) via the same Koa session / verifyRequest calls — or do I need to 'manually' provide a way for them to link an account on my service, with their shop ID within my embedded app?

 

^ I realise this is likely a separate topic, but I'm hoping there is a way this is consolidated into the request verification somehow.

 

Appreciate any help on this.

 

rorz

reecewilliams
Tourist
6 0 1

Hi @rorz 

 

From my understanding this is the correct way to identify a store, and using this is a perfectly legitimate way of storing data against.

 

Koa should be doing all of the HMAC checks to verify the source is actually from the store, and not just a spoofed request so there shouldn't be any more verification needed.

 

DISCLAIMER: I am also relatively new to this as well, but this is what I understand from previous investigation

 

 

 

 

 

SBD_
Shopify Staff
Shopify Staff
1043 141 183

This is an accepted solution.

Hey @rorz,

 

A 'better' way of defining who the current 'shop' / merchant is for the purposes of external API / DB operations?

Nope, once you verify the request, you can be certain who the shop is.

 

Another variable (such as a merchant ID or similar) that would be better-suited for tracking who the current user / store is?

Using shop is common practice.

 

A way for malicious actors to bypass my above code and spoof the "shop" value (i.e. there is a better way of doing this)?

Not if you only set the shop once you've verified the request.

 

can I access their details (with their permission, of course) via the same Koa session / verifyRequest calls

An offline token might be handy here, for example if this logic is running via a cron job / there's no session.

rorz
Shopify Partner
14 0 4

Thanks @SBD_ for this info!

 

This makes things easier for verifying / storing against a given shop.

 

Appreciate you pointing me towards offline tokens, too: these certainly look interesting. My app is currently interactive (user session-based), so I don't think I should be using those just yet. However, I see in the docs you've linked me to that they're "ideal for" webhooks, which is functionality I'm going to add soon now.

 

Do you happen to know if I can utilise both online / offline tokens for a Koa app somehow? Currently my verification / auth flow is using online tokens (default in koa-shopify-auth)... I can see in this discussion on GitHub that some users are getting webhook failures due to using online tokens. But surely it's bad practice for me to request offline tokens for an interactive app?

 

Should I somehow detect if a user is connecting to the app for the first time, and get an offline token for the purposes of background work, webhooks etc, and then switch to online tokens for sessions thereafter? It's not immediately clear to me how to use these in tandem (to have secure user sessions, and long-lived background tasks)...

 

(Thanks also to @reecewilliams 😊for the similar answer above)

0 Likes
SBD_
Shopify Staff
Shopify Staff
1043 141 183

@rorz funny you should ask - I only just looked into this: https://community.shopify.com/c/Shopify-APIs-SDKs/Online-and-offline-tokens/m-p/725426/highlight/tru...

 

Let me know how you go.

0 Likes
rorz
Shopify Partner
14 0 4

Thanks @SBD_ — how bizzare! :) I still haven't gotten around to testing online + offline tokens (turns out we made do without tokens for our webhooks) yet, but when I do I will jump into that thread.

 

Cheers

0 Likes
emeraldev
Shopify Partner
5 1 4

Hi,

 

I am trying to implemented something similar in terms of identifying shops with an offline token. How can I store and identify a shop when using an external Express.js custom API. I have asked this in another question, so it might seem like a repetition 

 

require('isomorphic-fetch');
const Koa = require('koa');
const Router = require('koa-router');
const next = require('next');
const { default: createShopifyAuth } = require('@shopify/koa-shopify-auth');
const dotenv = require('dotenv');
const { verifyRequest } = require('@shopify/koa-shopify-auth');
const session = require('koa-session');
dotenv.config();
const { default: graphQLProxy } = require('@shopify/koa-shopify-graphql-proxy');
const { ApiVersion } = require('@shopify/koa-shopify-graphql-proxy');

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

const { SHOPIFY_API_SECRET_KEY, SHOPIFY_API_KEY } = process.env;

app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();

    server.use(session(server));
    server.keys = [SHOPIFY_API_SECRET_KEY];

    server.use(
        createShopifyAuth({
            apiKey: SHOPIFY_API_KEY,
            secret: SHOPIFY_API_SECRET_KEY,
            scopes: ['read_products', 'write_products'],
            accessMode: "Offline",
            async afterAuth(ctx) {
                const { shop, accessToken } = ctx.session;
                ctx.cookies.set('shopOrigin', shop, { httpOnly: false })
                ctx.cookies.set('accessToken', accessToken);
                ctx.redirect('/');
            },
        }),
    );

    server.use(graphQLProxy({ version: ApiVersion.April19 }))
    server.use(verifyRequest());
    server.use(async (ctx) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
        ctx.res.statusCode = 200;
        return
    });

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

});

I have built an external Express.js Rest API that handles most of the logic and interacts with the database for the app. My issues is are:

  • In order for me to save the shop and the token to the database through the API. How can I make a POST request to the external API to save the shop.
  • Also at what stage do I call the the external Express API to retrieve and verify the shop that was saved. And how can I reuse the the access token to make queries to shopify with GraphQL.
  • My other question is, what is the purpose if verifyRequest in the Shopify-koa-auth and how does it work?

I am  fairly new to the the environment and and I'm bit confused on the moving parts after a shop is authenticated for the first time. I am not sure if i am thinking about this the right way. Please help.  Any help is greatly appreciated. Thank you

emeraldev
Shopify Partner
5 1 4

Hi there,

 

I am trying to implemented something similar in terms of identifying shops with an offline token. How can I store and identify a shop when using an external custom API. I have followed the React + Node.js tutorial.  I have asked this in another question and it might seem repetitive, I'm sorry for that.

 

require('isomorphic-fetch');
const Koa = require('koa');
const Router = require('koa-router');
const next = require('next');
const { default: createShopifyAuth } = require('@shopify/koa-shopify-auth');
const dotenv = require('dotenv');
const { verifyRequest } = require('@shopify/koa-shopify-auth');
const session = require('koa-session');
dotenv.config();
const { default: graphQLProxy } = require('@shopify/koa-shopify-graphql-proxy');
const { ApiVersion } = require('@shopify/koa-shopify-graphql-proxy');

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

const { SHOPIFY_API_SECRET_KEY, SHOPIFY_API_KEY } = process.env;

app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();

    server.use(session(server));
    server.keys = [SHOPIFY_API_SECRET_KEY];

    server.use(
        createShopifyAuth({
            apiKey: SHOPIFY_API_KEY,
            secret: SHOPIFY_API_SECRET_KEY,
            scopes: ['read_products', 'write_products'],
            accessMode: "Offline",
            async afterAuth(ctx) {
                const { shop, accessToken } = ctx.session;
                ctx.cookies.set('shopOrigin', shop, { httpOnly: false })
                ctx.cookies.set('accessToken', accessToken);
                ctx.redirect('/');
            },
        }),
    );

    server.use(graphQLProxy({ version: ApiVersion.April19 }))
    server.use(verifyRequest());
    server.use(async (ctx) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
        ctx.res.statusCode = 200;
        return
    });

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

});

I have built an external Express.js Rest API that handles most of the logic and interacts with the database for the app. My issues is are:

  • In order for me to save the shop and the token to the database through the API. How can I make a POST request to the external API to save the shop.
  • Also at what stage do I call the the external Express API to retrieve and verify the shop. And how can I reuse the the access token to make queries to shopify with GraphQL.
  • My other question is, what is the purpose if verifyRequest in the Shopify-koa-auth and how does it work?

The shopify environment is new to me. I can seem to follow what happens after a shop is authenticated for the first time.  I am not sure if i am thinking about this the right way. Please help.  Any help is greatly appreciated. Thank you

John777
Tourist
3 0 0

@emeraldev Hi I have also same question did you get any solution please help me
I am working with php in this All process and app will work fine I am generating the access token once and use it everywhere but, using node I didn't get away how we checked that We have access token so no need to generate again and use the old one where we required. I am able to store this token in my database
but haven't idea about how to check it.
node & koa both things are new for me I just want to learn

0 Likes
emeraldev
Shopify Partner
5 1 4

Hi @John777 

 

Yes I did. I have put the solution on my initial post. You can follow the link: link to solution. I hope it helps

0 Likes