<commit_before>var crypto = require('crypto'),
	express = require('express'),
	_ = require('underscore');

function ShopifyAuth() {
	this.options = {
		/*Required by the module:
		api_key: 			'api-key-here',
		api_secret: 	'api-secret',
		scopes: 			'scopes',
		redirect_uri: 'url to redirect to after'

		*/

		/*Optional settings
		verbose: 			true, 
		install_mount_path: '/auth-test'

		*/

		//Default options go here
		embedded_app: 		false,
		verbose: 					false,
		mounted: 					false,
		install_route: 		'',
		response_shop:    'shop'
	};	
};

//
// Options
//
ShopifyAuth.prototype.setOptions = function(options){
	if (typeof options === 'object') {
		this.options = _.extend(this.options, options);
	} else {
		this.log('setOptions: New options type is invalid. Expecting OBJECT, got ' + typeof options);
	}
};
ShopifyAuth.prototype.getOption = function(option) {
	//Return the value for this key
	if (option in this.options) {
		return this.options[option];
	} else {
		this.log('No option found for requested key: ' + option);
		return null;
	}
};


ShopifyAuth.prototype.formatShop = function(shop){
	var subdomain_regex = /(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i  //javascript

	this.log('formatShop: Testing Shop: ' + shop);

	if (subdomain_regex.test(shop)){
		//Subdomain available, return it
		this.log('formatShop: Shop matched regex: ' + subdomain_regex.exec(shop));
		return subdomain_regex.exec(shop)[1];
	} else {
		return shop;
	}
};


//
//Mount Authentication Routes
//
ShopifyAuth.prototype.mount = function(options){
	var self = this;

	//Requires: express

	//See if we've already mounted
	if (ShopifyAuth.mounted) {
		self.log('Install routes already mounted, cannot mount again');
		//return new Error('Already Mounted');
		throw new Error('Already Mounted');
	}

	var routes = express.Router();

	//Set the default mount paths.
	options = _.extend({
		install_route: '/install',
		complete_route: '/complete',
		complete_callback: function(req, res) { res.redirect('/'); }
	}, options);

	//Base route for starting the authentication
	routes.get(options.install_route, function(req, res){ ////------------------------------- BEGIN INSTALL ROUTE HANDLER
		//Verify that we have all of our config settings
		// api_key, scope required

		if (!self.getOption('api_key')) 			throw new Error('API_KEY Not Specified');
		if (!self.getOption('scopes')) 				throw new Error('SCOPES Not Specified');
		if (!self.getOption('redirect_uri')) 	throw new Error('REDIRECT_URI Not Specified');

		//Locate the current SHOP
		//If there is no shop in the current query, then check the last url in our session
		var shop;

		if (req.query.shop) {
			shop = req.query.shop;
		} else {
			//See if we have a session var to use
			if (req.session && req.session._shopify_auth && req.session._shopify_auth.last_url) {
				var shop_regex = /\?(?:.*?)shop=(.*?)(?:&|$)/i
				if (shop_regex.test(req.session._shopify_auth.last_url)) {
					shop = shop_regex.exec(req.session._shopify_auth.last_url)[1];
				}
			}
		}

		//If we still dont have a shop here, then we cannot continue
		if (!shop) throw new Error('SHOP Not Specified');

		shop = self.formatShop(shop);

		//Get our installation URL
		var redirect_url = 'https://{shop}.myshopify.com/admin/oauth/authorize?client_id={api_key}&scope={scopes}&redirect_uri={redirect}';
		redirect_url = redirect_url.replace('{shop}', shop).
															 	replace('{api_key}', self.getOption('api_key')).
																replace('{scopes}', self.getOption('scopes')).
																replace('{redirect}', self.getOption('redirect_uri'));

		self.log('Install URL: ' + redirect_url);

		if (self.getOption('embedded')) {
			//Use an HTML+JS redirect
			res.type('html').send("<html><head></head><body><script type='text/javascript'>window.top.location.href = '" + redirect_url + "';</script></body></html>");
		} else {
			res.redirect(redirect_url);
		}

	}); ////----------------------------------------------------------------------------------- END INSTALL ROUTE HANDLER


	// 																											////----------------------------- BEGIN COMPLETE ROUTE HANDLER
	//The complete route includes an HMAC that must be valid
	routes.get(options.complete_route, self.verifyHMAC({allow_no_hmac: false}), function(req, res){

		//Shopify has authenticated and called us back with a temporary token. We will call them back to exchange it for
		// a permanent token

		self.post('/admin/oauth/access_token', {
			client_id: self.getOption('api_key'),
			client_secret: self.getOption('api_secret'),
			code: req.query.code
		}, function(err, body){

			//Code exchanged successfully.
			var access_token = body['access_token'];

			//Save access token in the session
			if (!req.session._shopify_auth) req.session._shopify_auth = {}
 			req.session._shopify_auth = _.extend(req.session._shopify_auth, {
				shop: 				req.query.shop,
				access_token: access_token
			});

			self.setOptions({shop: req.query.shop, access_token: access_token});

			//Call the complete callback to let the user finish this flow
			options.complete_callback(req, res);

		});

	}); ////---------------------------------------------------------------------------------- END COMPLETE ROUTE HANDLER

	//Indicate that we have already mounted the auth routes
	self.setOptions({mounted: true, install_route: options.install_route});

	self.log('Mount called - new router returned');

	return routes;
};

//
//Full Authentication Route
//

/*

This will ensure that we have an authenticated session tokens, as well as attach various fields
to the request object and response object<commit_msg>fixed install flow<commit_after>var crypto = require('crypto'),
	express = require('express'),
	_ = require('underscore');

function ShopifyAuth() {
	this.options = {
		/*Required by the module:
		api_key: 			'api-key-here',
		api_secret: 	'api-secret',
		scopes: 			'scopes',
		redirect_uri: 'url to redirect to after'

		*/

		/*Optional settings
		verbose: 			true, 
		install_mount_path: '/auth-test'

		*/

		//Default options go here
		embedded_app: 		false,
		verbose: 					false,
		mounted: 					false,
		install_route: 		'',
		response_shop:    'shop'
	};	
};

//
// Options
//
ShopifyAuth.prototype.setOptions = function(options){
	if (typeof options === 'object') {
		this.options = _.extend(this.options, options);
	} else {
		this.log('setOptions: New options type is invalid. Expecting OBJECT, got ' + typeof options);
	}
};
ShopifyAuth.prototype.getOption = function(option) {
	//Return the value for this key
	if (option in this.options) {
		return this.options[option];
	} else {
		this.log('No option found for requested key: ' + option);
		return null;
	}
};


ShopifyAuth.prototype.formatShop = function(shop){
	var subdomain_regex = /(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i  //javascript

	this.log('formatShop: Testing Shop: ' + shop);

	if (subdomain_regex.test(shop)){
		//Subdomain available, return it
		this.log('formatShop: Shop matched regex: ' + subdomain_regex.exec(shop));
		return subdomain_regex.exec(shop)[1];
	} else {
		return shop;
	}
};


//
//Mount Authentication Routes
//
ShopifyAuth.prototype.mount = function(options){
	var self = this;

	//Requires: express

	//See if we've already mounted
	if (ShopifyAuth.mounted) {
		self.log('Install routes already mounted, cannot mount again');
		//return new Error('Already Mounted');
		throw new Error('Already Mounted');
	}

	var routes = express.Router();

	//Set the default mount paths.
	options = _.extend({
		install_route: '/install',
		complete_route: '/complete',
		complete_callback: function(req, res) { res.redirect('/'); }
	}, options);

	//Base route for starting the authentication
	routes.get(options.install_route, function(req, res){ ////------------------------------- BEGIN INSTALL ROUTE HANDLER
		//Verify that we have all of our config settings
		// api_key, scope required

		if (!self.getOption('api_key')) 			throw new Error('API_KEY Not Specified');
		if (!self.getOption('scopes')) 				throw new Error('SCOPES Not Specified');
		if (!self.getOption('redirect_uri')) 	throw new Error('REDIRECT_URI Not Specified');

		//Locate the current SHOP
		//If there is no shop in the current query, then check the last url in our session
		var shop;

		if (req.query.shop) {
			shop = req.query.shop;
		} else {
			//See if we have a session var to use
			if (req.session && req.session._shopify_auth && req.session._shopify_auth.last_url) {
				var shop_regex = /\?(?:.*?)shop=(.*?)(?:&|$)/i
				if (shop_regex.test(req.session._shopify_auth.last_url)) {
					shop = shop_regex.exec(req.session._shopify_auth.last_url)[1];
				}
			}
		}

		//If we still dont have a shop here, then we cannot continue
		if (!shop) throw new Error('SHOP Not Specified');

		shop = self.formatShop(shop);

		//Get our installation URL
		var redirect_url = 'https://{shop}.myshopify.com/admin/oauth/authorize?client_id={api_key}&scope={scopes}&redirect_uri={redirect}';
		redirect_url = redirect_url.replace('{shop}', shop).
															 	replace('{api_key}', self.getOption('api_key')).
																replace('{scopes}', self.getOption('scopes')).
																replace('{redirect}', self.getOption('redirect_uri'));

		self.log('Install URL: ' + redirect_url);

		if (self.getOption('embedded_app')) {
			//Use an HTML+JS redirect
			res.type('html').send("<html><head></head><body><script type='text/javascript'>window.top.location.href = '" + redirect_url + "';</script></body></html>");
		} else {
			res.redirect(redirect_url);
		}

	}); ////----------------------------------------------------------------------------------- END INSTALL ROUTE HANDLER


	// 																											////----------------------------- BEGIN COMPLETE ROUTE HANDLER
	//The complete route includes an HMAC that must be valid
	routes.get(options.complete_route, self.verifyHMAC({allow_no_hmac: false}), function(req, res){

		//Shopify has authenticated and called us back with a temporary token. We will call them back to exchange it for
		// a permanent token

		self.post('/admin/oauth/access_token', {
			client_id: self.getOption('api_key'),
			client_secret: self.getOption('api_secret'),
			code: req.query.code
		}, function(err, body){

			if (err) {
				self.logError('Access Token Exchange Failed');
				self.logError(JSON.stringify(err));
			} else {

				//Code exchanged successfully.
				var access_token = body['access_token'];

				//Save access token in the session
				if (!req.session._shopify_auth) req.session._shopify_auth = {}
	 			req.session._shopify_auth = _.extend(req.session._shopify_auth, {
					shop: 				req.query.shop,
					access_token: access_token
				});

				self.setOptions({shop: req.query.shop, access_token: access_token});

				//Call the complete callback to let the user finish this flow
				options.complete_callback(req, res);
 			}
		});

	}); ////---------------------------------------------------------------------------------- END COMPLETE ROUTE HANDLER

	//Indicate that we have already mounted the auth routes
	self.setOptions({mounted: true, install_route: options.install_route});

	self.log('Mount called - new router returned');

	return routes;
};

//
//Full Authentication Route
//

/*

This will ensure that we have an authenticated session tokens, as well as attach various fields
to the request object and response object
{'fraction_non_alphanumeric': 0.1801035178524432, 'fraction_numerical': 0.0007018159487674358, 'mean_word_length': 2.9901995099754988, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1814350', 'n_tokens_mistral': 3952, 'n_tokens_neox': 3499, 'n_words': 925}