Skip to content

Commit

Permalink
Phase2 updates (#110)
Browse files Browse the repository at this point in the history
- add authMiddleware for dev portal apis
  • Loading branch information
xinghengwang authored Nov 28, 2024
1 parent 241dcf9 commit e3f5442
Show file tree
Hide file tree
Showing 20 changed files with 1,515 additions and 394 deletions.
7 changes: 6 additions & 1 deletion my-dev-portal-api/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ MOESIF_MONETIZATION_VERSION="V2"
# Identity Provider Configuration #
###################################

# Set to "Auth0" or "Okta" depending on your selected IdP
AUTH_PROVIDER="Auth0|Okta"
# Note, we are assume you are using asymmetric key aka RS256 algorithm.

# Auth0 envars
# Only needed in frontend .env file (unless configuring M2M for AWS API Gateway)
AUTH0_DOMAIN=

# Okta envars
OKTA_ORG_URL=
OKTA_DOMAIN=
OKTA_API_TOKEN=
OKTA_APPLICATION_ID=
Expand Down
296 changes: 157 additions & 139 deletions my-dev-portal-api/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const express = require("express");
const path = require("path");
require("dotenv").config({ path: ['.env', '.env.template'] })
require("dotenv").config({ path: [".env", ".env.template"] });
const bodyParser = require("body-parser");
const moesif = require("moesif-nodejs");
const cors = require("cors");
Expand All @@ -18,8 +18,10 @@ const {
getSubscriptionForUserEmail,
} = require("./services/moesifApis");

const { authMiddleware } = require("./services/authPlugin");

const StripeSDK = require("stripe");
const { getApimProvisioningPlugin } = require("./config/pluginLoader")
const { getApimProvisioningPlugin } = require("./config/pluginLoader");

const app = express();
app.use(express.static(path.join(__dirname)));
Expand Down Expand Up @@ -57,36 +59,40 @@ const moesifMiddleware = moesif({

app.use(moesifMiddleware, cors());

app.post("/create-stripe-checkout-session", async (req, res) => {
const stripe = StripeSDK(process.env.STRIPE_API_KEY);
const priceId = req.query?.price_id;
const email = req.query?.email;
// https://docs.stripe.com/checkout/quickstart?client=react
// for embedded checkout.
app.post(
"/create-stripe-checkout-session",
authMiddleware,
async (req, res) => {
const stripe = StripeSDK(process.env.STRIPE_API_KEY);
const priceId = req.query?.price_id;
const email = req.user?.email || req.query?.email;
// https://docs.stripe.com/checkout/quickstart?client=react
// for embedded checkout.

try {
const session = await stripe.checkout.sessions.create({
ui_mode: "embedded",
line_items: [
{
// Provide the exact Price ID (for example, pr_1234) of the product you want to sell
price: priceId,
},
],
customer_email: email || undefined,
mode: "subscription",
return_url: `http://${process.env.FRONT_END_DOMAIN}/return?session_id={CHECKOUT_SESSION_ID}&price_id=${priceId}`,
});
try {
const session = await stripe.checkout.sessions.create({
ui_mode: "embedded",
line_items: [
{
// Provide the exact Price ID (for example, pr_1234) of the product you want to sell
price: priceId,
},
],
customer_email: email || undefined,
mode: "subscription",
return_url: `http://${process.env.FRONT_END_DOMAIN}/return?session_id={CHECKOUT_SESSION_ID}&price_id=${priceId}`,
});

console.log("got session back from stripe session");
console.log(JSON.stringify(session));
console.log("got session back from stripe session");
console.log(JSON.stringify(session));

res.send({ clientSecret: session.client_secret });
} catch (err) {
console.error("Failed to create stripe checkout session", err);
res.status(400).json({ message: "Error creating check out session" });
res.send({ clientSecret: session.client_secret });
} catch (err) {
console.error("Failed to create stripe checkout session", err);
res.status(400).json({ message: "Error creating check out session" });
}
}
});
);

app.get("/plans", jsonParser, async (req, res) => {
// if you created your "stripe" or "zoura" plans through moesif.
Expand All @@ -101,7 +107,7 @@ app.get("/plans", jsonParser, async (req, res) => {
});
});

app.get("/subscriptions", jsonParser, async (req, res) => {
app.get("/subscriptions", authMiddleware, jsonParser, async (req, res) => {
// !IMPORTANT, depends on your authentication scheme
// you may want to authenticate your user first

Expand All @@ -112,7 +118,9 @@ app.get("/subscriptions", jsonParser, async (req, res) => {
// using companyId, userId or email as in this example
// - It all can vary depends on your profile.

const email = req.query.email;
console.log('query email ' + req.query.email);
console.log('verified email from claims ' + req.user.email);
const email = req.user?.email || req.query.email;

try {
const subscriptions = await getSubscriptionForUserEmail({ email });
Expand All @@ -122,7 +130,7 @@ app.get("/subscriptions", jsonParser, async (req, res) => {
res.status(200).json(subscriptions);
} catch (err) {
console.error("Error getting subscription from moesif for " + email, err);
res.status(404).json({ message: "Error" });
res.status(404).json({ message: err.toString() });
}
});

Expand Down Expand Up @@ -200,69 +208,77 @@ app.post("/okta/register", jsonParser, async (req, res) => {
// - handles syncing the ids to Moesif.
// - and creates customers to API Management platform if need.
// - Please see DATA-MODEL.md see the assumptions and background on data mapping.
app.post("/register/stripe/:checkout_session_id", function (req, res) {
const checkout_session_id = req.params.checkout_session_id;

verifyStripeSession(checkout_session_id)
.then(async (result) => {
const stripeCheckOutSessionInfo = result;
console.log("in register");
if (result.customer && result.subscription) {
console.log("customer and subscription present");
const email = result.customer_details.email;
const stripe_customer_id = result.customer;
const stripe_subscription_id = result.subscription;
try {
if (
process.env.MOESIF_MONETIZATION_VERSION &&
process.env.MOESIF_MONETIZATION_VERSION.toUpperCase() === "V1"
) {
console.log("updating company and user with V1");
// in v1, companyId and subscription id is one to one mapping.
syncToMoesif({
companyId: stripe_subscription_id,
subscriptionId: stripe_subscription_id,
userId: stripe_customer_id,
email: email,
});
}
// V1 as fallback
else {
console.log("updating company and user with V2");

// assume you have one user per subscription
// but if you have multiple users per each subscription
// please check out https://www.moesif.com/docs/getting-started/overview/
// for the different entities how they are related to each other.
syncToMoesif({
companyId: stripe_customer_id,
subscriptionId: stripe_subscription_id,
userId: stripe_customer_id,
email: email,
});
app.post(
"/register/stripe/:checkout_session_id",
authMiddleware,
function (req, res) {
const checkout_session_id = req.params.checkout_session_id;

verifyStripeSession(checkout_session_id)
.then(async (result) => {
const stripeCheckOutSessionInfo = result;
console.log("in register");
if (result.customer && result.subscription) {
console.log("customer and subscription present");
const email = result.customer_details.email;
const stripe_customer_id = result.customer;
const stripe_subscription_id = result.subscription;
try {
if (
process.env.MOESIF_MONETIZATION_VERSION &&
process.env.MOESIF_MONETIZATION_VERSION.toUpperCase() === "V1"
) {
console.log("updating company and user with V1");
// in v1, companyId and subscription id is one to one mapping.
syncToMoesif({
companyId: stripe_subscription_id,
subscriptionId: stripe_subscription_id,
userId: stripe_customer_id,
email: email,
});
}
// V2 as fallback
else {
console.log("updating company and user with V2");

// assume you have one user per subscription
// but if you have multiple users per each subscription
// please check out https://www.moesif.com/docs/getting-started/overview/
// for the different entities how they are related to each other.
syncToMoesif({
companyId: stripe_customer_id,
subscriptionId: stripe_subscription_id,
userId: stripe_customer_id,
email: email,
});
}
} catch (error) {
console.error("Error updating user/company/sub:", error);
}
} catch (error) {
console.error("Error updating user/company/sub:", error);
}

// Provision new user for access to API
const user = await provisioningService.provisionUser(stripe_customer_id, email, stripe_subscription_id);
console.log(JSON.stringify(user));
}
// we still pass on result.
console.log(JSON.stringify(stripeCheckOutSessionInfo));
res.status(201).json(stripeCheckOutSessionInfo);
})
.catch((err) => {
console.error("Error registering user", err);
res.status(500).json({
message: "Failed to register user. Contact support for assistance",
// Provision new user for access to API
const user = await provisioningService.provisionUser(
stripe_customer_id,
email,
stripe_subscription_id
);
console.log(JSON.stringify(user));
}
// we still pass on result.
console.log(JSON.stringify(stripeCheckOutSessionInfo));
res.status(201).json(stripeCheckOutSessionInfo);
})
.catch((err) => {
console.error("Error registering user", err);
res.status(500).json({
message: "Failed to provision user. " + err.toString(),
});
});
});
});
}
);

app.get("/stripe/customer", function (req, res) {
const email = req.query.email;
app.get("/stripe/customer", authMiddleware, function (req, res) {
const email = req.user?.email || req.query.email;
console.log("get stripe customer " + typeof getStripeCustomer);
getStripeCustomer(email)
.then((result) => {
Expand All @@ -280,15 +296,21 @@ app.get("/stripe/customer", function (req, res) {
});
});

app.post("/create-key", jsonParser, async function (req, res) {
app.post("/create-key", authMiddleware, jsonParser, async function (req, res) {
try {
// FIXME needs validation on email
const email = req.body.email;
// if authentication used, email can come from idToken claims,
// otherwise we use email from body.
const email = req.user?.email || req.body.email;
const stripeCustomer = await getStripeCustomer(email);
const customerId = (stripeCustomer.data && stripeCustomer.data[0]) ? stripeCustomer.data[0].id : undefined;
const customerId =
stripeCustomer.data && stripeCustomer.data[0]
? stripeCustomer.data[0].id
: undefined;

if (!customerId) {
throw new Error(`Customer Id unknown. Ensure you're subscribed to a plan. If you just subscribed, try again.`);
throw new Error(
`Customer Id unknown. Ensure you're subscribed to a plan. If you just subscribed, try again.`
);
}

// Provision new key for access to API
Expand All @@ -301,52 +323,48 @@ app.post("/create-key", jsonParser, async function (req, res) {
}
});

app.get("/embed-dash-time-series(/:userId)", function (req, res) {
try {
// FIXME needs validation on userId
const userId = req.params.userId;

getInfoForEmbeddedWorkspaces({
workspaceId: templateWorkspaceIdTimeSeries,
userId,
})
.then((info) => {
res.json(info);
})
.catch((err) => {
console.log(err);
res.status(500).send({
error: "something went wrong",
});
});
} catch (error) {
console.error("Error generating embedded template:", error);
res.status(500).json({ message: "Failed to retrieve embedded template" });
}
});
app.get(
"/embed-charts(/:authUserId)",
authMiddleware,
async function (req, res) {
// if authMiddleware is enabled, the data for user should come from the auth data.
// otherwise use query param.
const authUserId = req.user?.sub || req.params?.authUserId;
const email = req.user?.email || req.query?.email;

// depends your data model (see assumptions in DATA_MODEL.md),
// and if in your API gateway if you identifyUser using stripeCustomerId
// or the userId from authorization provider.
// Perhaps, you have your own userId for your own system.
// the most important aspect is the user_id used in your identifyUser hook
try {
const stripeCustomer = await getStripeCustomer(email);
const stripeCustomerId =
stripeCustomer.data && stripeCustomer.data[0]
? stripeCustomer.data[0].id
: undefined;

if (!stripeCustomerId) {
console.error("stripe customer not found when fetching for " + email);
}

app.get("/embed-dash-live-event(/:userId)", function (req, res) {
try {
const userId = req.params.userId;
getInfoForEmbeddedWorkspaces({
workspaceId: templateWorkspaceIdLiveEvent,
userId,
})
.then((info) => {
res.json(info);
})
.catch((err) => {
console.log(err);
res.status(500).send({
error: "something went wrong",
});
});
} catch (error) {
console.error("Error generating embedded template:", error);
res.status(500).json({ message: "Failed to retrieve embedded template" });
const embedInfoArray = await Promise.all(
[templateWorkspaceIdTimeSeries, templateWorkspaceIdLiveEvent].map(
(workspaceId) =>
getInfoForEmbeddedWorkspaces({
workspaceId: templateWorkspaceIdTimeSeries,
userId: stripeCustomerId || authUserId,
})
)
);
res.status(200).json(embedInfoArray);
} catch (err) {
console.error("Error generating embedded templates:", error);
res.status(500).json({ message: "Failed to retrieve embedded template" });
}
}
});
);

app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
console.log(`My Dev Portal Backend is listening at http://localhost:${port}`);
});
Loading

0 comments on commit e3f5442

Please sign in to comment.