-
-
Save dangolbeeker/660cb46636d30f2ada03e525de8ef5fc to your computer and use it in GitHub Desktop.
Lambda Shopify OAUTH
This file contains hidden or 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
import { config } from 'dotenv' | |
import Shopify from 'shopify-api-node' | |
import cryptographer from '@brixtol/cryptographer' | |
config() | |
export const crypto = cryptographer(process.env.SHOPIFY_AUTH_SECRET) | |
/** | |
* Request Headers | |
*/ | |
export const headers = { | |
'Access-Control-Allow-Origin': '*' | |
} | |
/** | |
* Shopify Node Client | |
*/ | |
export const shopify = new Shopify({ | |
shopName: 'your-shop', | |
apiKey: process.env.SHOPIFY_API_KEY, | |
password: process.env.SHOPIFY_SECRET, | |
autoLimit: { | |
calls: 2, | |
interval: 1000, | |
bucketSize: 30 | |
} | |
}) | |
/** | |
* OAuth | |
* | |
* @param {Netlify.NetlifyFunctionEvent} event | |
* @param {function} callback | |
*/ | |
export async function Auth ({ queryStringParameters }, callback) { | |
const { invalid, message } = validate(queryStringParameters) | |
if (invalid) { | |
return error(400, message) | |
} | |
const { customer_id, id, action, email } = queryStringParameters | |
const password = 'gen_' + crypto.encode(id) | |
try { | |
// if customer_id was passed in, customer is fetched using that | |
// else a search is executed looking for an account matching the email | |
const query = ((customer_id !== 'unknown') | |
? await shopify.customer.get(Number(customer_id)) | |
: await shopify.customer.search({ | |
query: 'email:' + email, | |
limit: 1 | |
}) | |
) | |
// if an array is returned an account search was made | |
// else the customer_id value was passed | |
const customer = Array.isArray(query) ? query[0] : query | |
// generate a custom multipass token, encrypting email/password | |
const multipass_identifier = crypto.encode({ email, password }) | |
// when action is register, attempt to create/register an account | |
if (action === 'register') { | |
// if customer exists, return | |
if (customer) return error(422, `Account using ${email} already exists`) | |
// extends the queryparameters to include multipass and password | |
return register( | |
{ | |
...queryStringParameters | |
, multipass_identifier | |
, password | |
} | |
) | |
} | |
// make sure account is authorized with | |
if (!customer.multipass_identifier) { | |
return error(422, `An account for ${email} exists but social login is not configured`) | |
} | |
if (multipass_identifier !== customer.multipass_identifier) { | |
return error(422, 'Failed to authenticate, incorrect or invalid indentifier') | |
} | |
return login(email, password) | |
} catch (e) { | |
console.log(e) | |
return error(422, `An account with email "${email}" does not exist!`) | |
} | |
/** | |
* Subscibe | |
* | |
* The request result to return the Login ready formData | |
* which will allow the POST request to be invoked client side. | |
* | |
*/ | |
async function Subscribe () { | |
// TODO | |
} | |
/** | |
* Login | |
* | |
* The request result to return the Login ready formData | |
* which will allow the POST request to be invoked client side. | |
* | |
* @param {string} email | |
* @param {string} password | |
*/ | |
function login (email, password) { | |
return callback(null, { | |
headers, | |
statusCode: 200, | |
body: JSON.stringify( | |
{ | |
form_type: 'customer_login', | |
utf8: '✓', | |
customer: { | |
email, | |
password | |
} | |
} | |
) | |
}) | |
} | |
/** | |
* Register | |
* | |
* Create customer within Shopify. | |
* | |
* @param {object} params | |
*/ | |
async function register ( | |
{ | |
, provider | |
, password | |
, multipass_identifier | |
, first_name | |
, last_name | |
, gender | |
, avatar | |
, accepts_marketing | |
, accepts_marketing_updated_at | |
, send_email_welcome | |
, marketing_opt_in_level | |
, country | |
, currency | |
} | |
) { | |
try { | |
await shopify.customer.create({ | |
first_name, | |
last_name, | |
email, | |
verified_email: email, | |
password, | |
password_confirmation: password, | |
multipass_identifier, | |
send_email_welcome, | |
marketing_opt_in_level, | |
accepts_marketing, | |
accepts_marketing_updated_at, | |
currency, | |
tags: `Registered via ${provider}`, | |
addresses: [ | |
{ | |
last_name, | |
first_name, | |
country | |
} | |
], | |
metafields: [ | |
{ | |
key: 'avatar', | |
value: avatar, | |
value_type: 'string', | |
namespace: 'customer' | |
}, | |
{ | |
key: 'gender', | |
value: gender, | |
value_type: 'string', | |
namespace: 'customer' | |
} | |
] | |
}) | |
return login(email, password) | |
} catch (e) { | |
console.log(e) | |
return error(500, 'The server encountered an signing in, try again later') | |
} | |
} | |
/* -------------------------------------------- */ | |
/* UTILITIES */ | |
/* -------------------------------------------- */ | |
/** | |
* Errors | |
* | |
* Returns error responses and/or messages when a request | |
* has failed to process of response | |
* | |
* @param {number} statusCode | |
* @param {string} message | |
* @returns | |
*/ | |
function error (statusCode, message) { | |
return callback(null, { | |
statusCode, | |
headers, | |
body: JSON.stringify({ message }) | |
}) | |
} | |
/** | |
* Validate | |
* | |
* @param {object} params | |
* @returns | |
*/ | |
function validate (params) { | |
let message | |
let invalid | |
/* ------------------- EMAIL ------------------ */ | |
if (!params.email) { | |
invalid = true | |
message = 'Missing email Field' | |
return { invalid, message } | |
} | |
if (!/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i.test(params.email)) { | |
invalid = true | |
message = `Invalid email pattern for ${params.email}` | |
return { invalid, message } | |
} | |
/* -------------------- ID -------------------- */ | |
if (!params.id) { | |
invalid = true | |
message = 'Missing "id" (auth identitifier) field' | |
return { invalid, message } | |
} | |
/* ------------------ ACTION ------------------ */ | |
if (!params.action) { | |
invalid = true | |
message = 'Missing "action" field' | |
return { invalid, message } | |
} | |
if (!/\blogin|register|subscribe\b/.test(params.action)) { | |
invalid = true | |
message = 'Invalid action was passed' | |
return { invalid, message } | |
} | |
return { | |
invalid, | |
message | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment