Skip to content

Express - Integrate your backend

Minimal example

The following example creates a route which can only be accessed from logged-in users.

const propelAuth = require("@propelauth/express");
const {requireUser} = propelAuth.initAuth({
    authUrl: "REPLACE_ME", //(1)
    apiKey: "YOUR_API_KEY", //(2)
})

// requireUser is a middleware which validates the access token
app.get("/hello", requireUser, (req, res) => {
    res.text("Hello user with ID " + req.user.userId);
})
  1. The base URL where your authentication pages are hosted. You can find this under the Backend Integration section for your project at https://app.propelauth.com.
  2. You can manage your api keys under the Backend Integration section for your project.

How it works

You've seen that the frontend gets an access token. When it makes an HTTP request, it provides this access token in an Authorization header.

PropelAuth provides you with metadata that you use to validate the access token and figure out who it belongs to. The complexity of fetching the metadata and validating the tokens is hidden in the express library.

Install

In your Express app, install the @propelauth/express library.

$ npm install --save @propelauth/express
$ yarn add @propelauth/express

Set up

Create a new file with the following code

// propelauth.js
const propelAuth = require("@propelauth/express");
module.exports = propelAuth.initAuth({
    debugMode: true, // (1)
    authUrl: "https://auth.yourdomain.com", // (2)
    apiKey: "YOUR_API_KEY", // (3)
})
  1. If true, error messages returned to the user will be detailed. It's useful for debugging, but a good idea to turn off in production.
  2. The base URL where your authentication pages are hosted. You can find this under the Backend Integration section for your project at https://app.propelauth.com.
  3. You can manage your api keys under the Backend Integration section for your project.

initAuth performs a one-time initialization of the library. It will verify your apiKey is correct and fetch the metadata needed to verify access tokens in requireUser, optionalUser, or requireOrgMember.

Protect API routes

requireUser

An Express middleware that will verify the request was made by a valid user. Specifically, it will:

  1. Check that a valid access token was provided. If not, the request is rejected with a 401 status code.
  2. Set user on the Request with the user's information.
app.get("/hello", requireUser, (req, res) => {
    res.text("Hello user with ID " + req.user.userId);
})

The user object looks like:

{
  // the id of the user whose token was provided
  userId: "2667a646-cdef-49da-8580-47e0d73cffa7",
  // For each organization the user is a member of, this is a dict of the organization's id
  //   to some information about the organization.
  orgIdToOrgMemberInfo: {
    "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4": {
      "orgId": "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
      "orgName": "ExampleOrganization",
      // This is the role of the user within that organization. See UserRole for more information
      "userRole": UserRole.Owner,
    }
  }
}

optionalUser

Similar to requireUser, except if an access token is missing or invalid, the request is allowed to continue, but user on the Request will be undefined.

app.get("/hello", optionalUser, (req, res) => {
    if (req.user) {
        res.text("Hello user with ID " + req.user.userId);
    } else {
        res.text("Hello anonymous")
    }
})

requireOrgMember

An Express middleware that will verify the request was made by a valid user that belongs to a specific organization.

// By default, requireOrgMember checks for orgId as a path parameter
app.get("/org/:orgId/hello",
    requireOrgMember({minimumRequiredRole: UserRole.Admin}),
    (req, res) => {
        res.text("You are an admin or owner of " + req.org.orgName)
    }
)
// Expect users to query with /hello?orgId=...
const orgIdInQueryExtractor = (req) => req.query.orgId;
const requireAdminForOrgInQuery = requireOrgMember({
    orgIdExtractor: orgIdInQueryExtractor,
    minimumRequiredRole: UserRole.Admin,
})

app.get("/hello", requireAdminForOrgInQuery, (req, res) => {
    res.text("You are an admin or owner of " + req.org.orgName)
})

Specifically, it will:

  1. Check that a valid access token was provided. If not, the request is rejected with a 401 status code.
  2. Set user on the Request with the user's information. See requireUser for what the user object looks like.
  3. Find an id for an organization within the request. By default, it will check Request.params.orgId (a path parameter named orgId).
  4. Check that the user is a member of that organization. If not, the request is rejected with a 403 status code.
  5. (Optionally) Check that the user's role in that organization is >= minimumRequiredRole. If not, the request is rejected with a 403 status code.
  6. Set org on the Request with the organization's information for this user.

The org object looks like:

{
  "orgId": "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
  "orgName": "ExampleOrganization",
  "userRole": UserRole.Owner, // (1)
}
  1. This is the role of the user within that organization. See UserRole for more information

The full signature for requireOrgMember is:

interface RequireOrgMemberArgs {
    minimumRequiredRole?: UserRole // (2)
    orgIdExtractor?: (req: Request) => string // (1)
}

function requireOrgMember(args?: RequireOrgMemberArgs) {
}
  1. A function that takes in the Request and outputs the orgId to require the user to be a member of. If none is specified, it will check for a path param orgId via req.params.orgId.
  2. If specified, requireOrgMember will check both that the user is a member of the organization, and that their role is >= minimumRequiredRole. If not, the request is rejected with a 403 Forbidden error.

API calls

In addition to protecting API routes, you can make requests to PropelAuth to fetch more information about your users or organizations. You can also create new users, update user metadata, etc.

// Example
const usersResponse = await fetchUsersByQuery({emailOrUsername: "@example.com", orderBy: "LAST_ACTIVE_AT_DESC"})
for (const user of userResponse.users) {
    console.log("Found user " + user)
}

See the reference for everything you can do.

Next Steps

Done with your backend? Next you can deploy to production.