Skip to content

Instantly share code, notes, and snippets.

@dangolbeeker
Forked from panoply/auth.js
Created October 11, 2021 18:40
Show Gist options
  • Save dangolbeeker/660cb46636d30f2ada03e525de8ef5fc to your computer and use it in GitHub Desktop.
Save dangolbeeker/660cb46636d30f2ada03e525de8ef5fc to your computer and use it in GitHub Desktop.
Lambda Shopify OAUTH
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 (
{
email
, 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