Created
January 30, 2016 11:36
-
-
Save dbnoble/1120dafcf44cc8f60f46 to your computer and use it in GitHub Desktop.
Authentication without 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 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 | |
Shops.update({shop: authConfig.shop}, { | |
$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 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 | |
}); | |
// Reload keysets into memory at startup from the Keysets collection so that they persist between reloads | |
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
// This is not very secure, you should find a way to make sure that the client requesting the Keyset UUID has permission | |
// This is partially why I use accounts, but if you can come up with a way to verify the request, you should add that check | |
// to the method and throw an error if it does not pass your check. | |
Meteor.methods({ | |
getKeyset: function(shop) { | |
// Returns the keyset that corresponds to the this shop | |
var shopKeyset = Shops.findOne({shop: shop}).locator; | |
return shopKeyset; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment