Skip to content

Modern B2C use-cases. This demo is built to show how to do things `serverless` with Okta. Plus Stripe integration!

Notifications You must be signed in to change notification settings

zeekhoo/okta-vue-serverless-stripe

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

37 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

About

This demo showcaes key concepts in protecting API resources using Okta's API Access Management.

A highly stylized sample SPA is provided(in the spa folder) to drive the demo. This make believe e-commerce website incorporates the following functionality:

  • Browse anonymously
  • Anonymous -> Known user with low firction "signup"
  • Site contents protected until a user "registers" and provides payment.

The SPA's interaction with the resource server (in the serverless folder) are the main demo points showcasing how Okta protects API resources with OAuth 2.0.

🎁 Bonus 🎁

All the identity functionality is powered by Okta. And as a bonus, the demo also provides a sample integration with Stripe Checkout, so we get to see a sample integration between the identity provider (Okta) and payments platform (Stripe).

Prerequisites

  • Okta developer account. Signup here.
  • Optional: Stripe account (to see the billing integration). Signup here.
  • Serverless: The sample APIs are built on Serverless and is in the serverless folder. See install instructions.
  • Terraform 0.14.x: To automatically provision Okta resources (lots of manual steps if we do it by hand!). Install for your operating system.

Note: Make sure to use Terraform 0.14.x

Use-cases

1. "Signup" a user with Email only

A visitor to the website (the demo SPA) browses around but cannot access content and is prompted to sign-up instead. The sign-up process only requires the visitor to provide an email. The "Sign Up" button makes request to the signup.js Lambda function, which creates the user in Okta, logs the user in and returns an Okta sessionToken. The SPA uses the sessionToken to start an Okta session by using the /authorize endpoint to retrieve an id_token and an access_token for the SPA.

Going forward, all API calls are protected by the access_token retrieved above.

2. Progressive Profiling

"Progress" the "prospect" user (we only know their email) to a "customer" by collecting preferences and most importantly, payment info.

By providing email, the website allows the visitor is see limited content. But in order to gain full access of the site, they are prompted to subscribe to the service by providing payment (via Stripe). A registration form collects information from the visitor to complete their profile (e.g. by entering full name and selecting preferences). And the "Submit" button makes a request to the subscribe.js Lambda function, which forwards the profile updates to Okta. It also initiates a Stripe Checkout Session and returns the "session id" that's required for the SPA to redirect to Stripe Checkout (More on this later).

AWS API Gateway is configured with the custom Lambda authorizer auth.js to prevent access to the API unless the access_token retrieved previously is present in the request.

3. Protect APIs with scoped access_tokens

The user accesseses site contents by making API calls. While each call requires an access_token, the resources' API also looks at claims in the token to determine the level of access. Okta is configured to return token claims that describe whether or not the user is a "prospect" or "customer", so the video.js Lambda function immediately knows of the user context without having to make additional lookups to the user-store. In other words, based on the token's claims, the API returns different results.


Setting up and running locally

1. Terraform

cd into the terraform folder
Then, rename the "sample" tfvars file

mv terraform.tfvars.sample terraform.tfvars

And edit in the values:

var value
org_name The "subdomain" part of the Okta developer account's url. e.g. dev-668899
base_url The Okta developer account's URL hostname: either oktapreview.com or okta.com
api_token Get an API token from the Okta developer account's admin UI

Now run terraform init

terraform init

Then "plan"

terraform plan

Then apply

terraform apply

Enter "yes" at the prompt.

Take a look using the Okta Admin UI after this is done and notice the resources that were provisioned:

  • A couple custom profile attributes
  • A couple groups
  • An OIDC app
  • An AuthorizationServer with some custom Scopes and Claims, and a couple specific Access Policy Rules

2. Populate environment variables.

At the completion of terraform apply, you'll see some outputs for ids of resources provisioned. We need those ids in our local environment files for the SPA and Serverless.

Using the Terraform outputs, generate a .env.development.local file for the SPA:
(The the command below in the /terraform folder)

terraform output | grep issuer | sed -e "s/issuer/VUE_APP_ISSUER/g" > spa.env.development.local \
&& terraform output | grep client_id | sed -e "s/client_id/VUE_APP_CLIENT_ID/g" >> spa.env.development.local \
&& terraform output | grep prospect_group_id | sed -e "s/prospect_group_id/VUE_APP_PROSPECT_GROUP_ID/g" >> spa.env.development.local \
&& terraform output | grep customer_group_id | sed -e "s/customer_group_id/VUE_APP_CUSTOMER_GROUP_ID/g" >> spa.env.development.local \
&& cp spa.env.development.local ../spa/.env.development.local

The above script generates the .env.development.local file in the spa folder. Examine its contents and make any changes or fixes if necessary.

Next, generate a .env.json file for Serverless:
(The the command below in the /terraform folder)

touch serverless.env.json \
&& echo "{" > serverless.env.json \
&& echo '  "AWS_PROFILE": "serverless-admin",' >> serverless.env.json \
&& echo '  "AWS_REGION": "us-west-2",' >> serverless.env.json \
&& echo '  "ENVIRONMENT": "dev",' >> serverless.env.json \
&& terraform output | grep issuer | sed -e 's/issuer =/  "ISS":/g' | sed -e 's/$/,/g' >> serverless.env.json \
&& echo '  "AUD": "api://bod.unidemo",' >> serverless.env.json \
&& cat terraform.tfvars | grep api_token | sed -e 's/api_token/  "API_KEY"/g' | sed -e 's/=/: /g' | sed -e 's/$/,/g' >> serverless.env.json \
&& terraform output | grep prospect_group_id | sed -e 's/prospect_group_id =/  "PROSPECT_GROUP_ID":/g' | sed -e 's/$/,/g' >> serverless.env.json \
&& terraform output | grep customer_group_id | sed -e 's/customer_group_id =/  "CUSTOMER_GROUP_ID":/g' >> serverless.env.json \
&& echo "}" >> serverless.env.json \
&& cp serverless.env.json ../serverless/.env.json

The above script generates the .env.json file in the /serverless folder. Examine its contents and edit if necessary (use the sample .env.json.sample as a guide)
Edit the following 2 variables (to match your AWS environment) and leave the rest alone (unless there are formatting errors):

var value
AWS_PROFILE setup (or use an existing) AWS profile using aws-cli. See instructions
AWS_REGION aws region where you want to deploy to

3. Spin up the Resource Server

cd into the serverless folder
and install the dependencies

npm install

We don't have to deploy. For testing and demo purposes, we'll use serverless-offline, which emulates AWS Lambda and API Gateway. This should already be installed during npm install.

serverless offline start

This'll bring up the API on localhost:3000

4. Spin up the SPA

cd into the spa folder
and install the dependencies

npm install

then compile and serve

npm run serve

The SPA and the resource server are now both up. Open up your browser to http://localhost:8080 to use the demo

Social Auth

The demo app provides a "Signin with Facebook" example but this needs to be configured in Okta first:

  • See the add Facebook instructions on how to configure Facebook as an external identity provider to Okta.
  • Obtain the "idp id" after configuration is complete
  • Add VUE_APP_FB_ID=<idp id> to the SPA's .env.development.local file.

Stripe Integration

An e-commerce site demo isn't complete without billing integration. And one of the easiest payment form integrations is Stripe Checkout.
Here's our basic implementation:

  1. At the end of the subscribe.js Lambda function, we POST a Stripe Checkout session.
    • When initiating the Checkout session, we provide the Stripe API with the mandatory success_url and cancel_url.
    • Also realize that prior to this, we've already created the user object in Okta. Thus, we also provide the Stripe Session API the client_reference_id, setting it equal to the Okta user id. This part is CRITICAL because we're going to use it later in the webhook.
  2. The SPA uses the above session id to redirect to the Stripe hosted checkout page.
  3. Upon checkout completion, Stripe redirects back to the SPA at success_url.
  4. Stripe also fires off the checkout.session.completed event to our stripe.js (the webhook) Lambda function.
    • The webhook updates the Okta user identified by client_reference_id and sets the custom profile attribute stripeCustomerId to the customer id found in the payload of the event.
  5. Meanwhile, the browser redirects back to a SPA component at the success_url url. Javascript on this page "polls" Okta by constantly requesting new set of id_token and access_tokens from Okta until it finds what it needs in the tokens:
    • We configured Okta to return the stripeCustomerId claim in the id and access tokens.
    • If the webhook has done it's job, the stripeCustomerId cliam should be populated in the tokens. And when it does, the SPA stops polling, updates the user-context and returns to the home page.

Setup

  • Add VUE_APP_STRIPE_PUBLISHABLE_KEY=<replace-with-your-publishable-key> to the SPA's .env.development.local. You can get the value from your Stripe developer dashboard.

  • Install the Stripe CLI and link it to your Stripe account.

  • Listen to webhook events and forward it to our serverless offline running on port 3000

    stripe listen --forward-to http://localhost:3000/dev/stripe/webhook
    
  • Add the following 3 key-values to the serverless.env.json file in the /serverless folder.

    var value
    STRIPE_SECRET_KEY <replace-with-your-secret-key>. Get the value from your Stripe developer dashboard
    STRIPE_PRICE_ID Setup a product in your Stripe developer dashboard and get its API id
    STRIPE_WEBHOOK_SECRET The CLI printed a webhook secret key to the console when you started the stripe listen command in the previous step
  • When presented with the Stripe Checkout page, use the test credit card number 4242424242424242. Enter any future expiration date and CVC.

About

Modern B2C use-cases. This demo is built to show how to do things `serverless` with Okta. Plus Stripe integration!

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published