Last active
January 30, 2016 11:43
-
-
Save dbnoble/8867b392f89462732495 to your computer and use it in GitHub Desktop.
Authentication with accounts for froatsnook:shopify
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// define the Shops collection on the client and the server | |
Shops = new Mongo.Collection("shops"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Subscribe to the current user object | |
Meteor.subscribe("userData"); | |
// Subscribe to shopData to make Shops collection available to the client | |
Meteor.subscribe("shopData"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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