Skip to content

Instantly share code, notes, and snippets.

@dbnoble
Last active January 30, 2016 11:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dbnoble/8867b392f89462732495 to your computer and use it in GitHub Desktop.
Save dbnoble/8867b392f89462732495 to your computer and use it in GitHub Desktop.
Authentication with accounts for froatsnook:shopify
// define the Shops collection on the client and the server
Shops = new Mongo.Collection("shops");
// This can be anywhere you need to initialize the API on the client.
// ie. Template.rendered callback, inside it's own function, etc
MyAPIFunctionality = function() {
var api_key = envAPIKey;
var shop = Session.get("shop"); // I store the shop in the Session in my routes, you can do it a different way if you wish
// This will find the UUID of the Shop's keyset as long as the current user has access to this shop
Meteor.call('getKeyset', shop, function(error, shopKeyset){
if(!error) {
api = new Shopify.API({
shop: shop,
keyset: shopKeyset
});
Shopify.getEmbeddedAppAPI(function(err, ShopifyApp) {
if (err) {
console.log("Getting Embedded App SDK Failed");
return;
}
ShopifyApp.init({
apiKey: api_key,
shopOrigin: "https://" + shop + ".myshopify.com",
});
// Note: requests through API are proxied via Meteor Method.
api.countOrders(function(err, count) {
if (err) {
ShopifyApp.flashError("Counting orders failed: " + err);
} else {
ShopifyApp.flashNotice("There are " + count + " order(s)");
}
});
});
} else {
console.log("No credentials found");
}
});
// This code can be anywhere you want on the client, ie. in a route, in an event handler, etc
// You can have multiple authenticators on different pages/routes with different options if you need to
// Get the shop however you need to, in this case the query parameter.
const shop = query.shop.substring(0, query.shop.indexOf('.'));
var authenticator = new Shopify.PublicAppOAuthAuthenticator({
shop: shop,
api_key: 'API_KEY_HERE'
keyset: "auth", // auth is the keyset I generated in /server/startup.js that contains the API key & secret
scopes: "read_products",
embedded_app_sdk: true,
post_auth_uri: 'https://' + shop + '.myshopify.com/admin/apps/' + envAPIKey //this will send them to the embedded app after auth
});
window.top.location.href = authenticator.auth_uri;
// Subscribe to the current user object
Meteor.subscribe("userData");
// Subscribe to shopData to make Shops collection available to the client
Meteor.subscribe("shopData");
Shopify.harden(); // do not send accessToken to client, we only need it on the server
Shopify.onAuth(function(access_token, authConfig, userId) {
var shopUUID = uuid.new(); // Generate a unique ID for this shop so we can find it's corresponding keyset later
var existingShop = Shops.findOne({shop: authConfig.shop});
if(existingShop) {
shopUUID = existingShop.locator; //If the shop already exists, use it's existing unique ID instead
}
// Update or upsert the shop record
// Since I have user accounts, I also tie the shop to the userId for authorization purposes
Shops.update({shop: authConfig.shop, userId: userId}, {
$set: {
locator: shopUUID // Save the unique ID value to the locator field so we can find it's corresponding keyset later
},
}, {upsert: true});
// Add the Keyset so it can be accessed by the server, use the unique ID instead of the shop name (for security)
Shopify.addKeyset(shopUUID, {
access_token: access_token
});
// Add a copy of the Keyset to the database so we can reload it into memory anytime the server restarts
Keysets.upsert({name: shopUUID}, {$set: { token: access_token }});
});
envAPIKey = process.env.SHOPIFYKEY;
envSecretKey = process.env.SHOPIFYSECRET;
Keysets = new Mongo.Collection("keysets"); // Server only collection to persist Keysets
// Publish the current user object (if you store any unsafe fields in the user document, you should also specify which fields to include/exclude)
Meteor.publish("userData", function () {
return Meteor.users.find({_id: this.userId});
});
// Publish the Shops collection to clients
Meteor.publish("shopData", function () {
return Shops.find({userId: this.userId}, {fields: {shop: 1, locator: 1}});
});
Meteor.startup(function () {
// add the auth keyset with secret. "adding" a keyset simply loads it into the servers memory.
// Since it is not persisted anywhere, we add it immediately at startup.
Shopify.addKeyset("auth", {
api_key: envAPIKey,
secret: envSecretKey
});
// I am persisting client keysets in the database in a collection called Keysets so that I can reload them into memory at startup
existingKeysets = Keysets.find({});
existingKeysets.forEach(function(keyset) {
var thisName = keyset.name;
var thisToken = keyset.token;
Shopify.addKeyset(thisName, {
access_token: thisToken
});
});
});
Meteor.methods({
getKeyset: function(shop) {
if (! this.userId ) {
// No user? No keyset.
throw new Meteor.Error("not-authorized");
} else {
// Returns the keyset's name as long as the shop belongs to the user
var shopKeyset = Shops.findOne({shop: shop, userId: this.userId}).locator;
return shopKeyset;
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment