Skip to content

Express Library Reference

Installation

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

Initialize

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.

const propelAuth = require("@propelauth/express");
const {
    requireUser,
    optionalUser,
    requireOrgMember,
    fetchUserMetadataByUserId,
    fetchUserMetadataByEmail,
    fetchUserMetadataByUsername,
    fetchBatchUserMetadataByUserIds,
    fetchBatchUserMetadataByEmails,
    fetchBatchUserMetadataByUsernames,
    fetchOrg,
    fetchOrgByQuery,
    fetchUsersByQuery,
    fetchUsersInOrg,
    createUser,
    updateUserMetadata,
    updateUserEmail,
    UserRole,
} = 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.

We recommend putting this in its own file and exporting it:

// propelauth.js
const propelAuth = require("@propelauth/express");
module.exports = propelAuth.initAuth({
    debugMode: true,
    authUrl: "https://auth.yourdomain.com",
    apiKey: "YOUR_API_KEY",
})

And then importing just what you need:

const {requireUser} = require("./propelauth");

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.

Fetch user metadata by id

function fetchUserMetadataByUserId(userId: string, includeOrgs?: boolean): Promise<UserMetadata | null>

// Example
app.get("/whoami", requireUser, async (req, res) => {
    const userMetadata = await fetchUserMetadataByUserId(req.user.userId);
    res.json({"userId": userMetadata.userId, "email": userMetadata.email, "lastActiveAt": userMetadata.lastActiveAt});
})

Successful response (UserMetadata):

{
    userId:            "e9d3520f-836e-403c-82c2-09843517e1ce", 
    email:             "user@example.com",
    emailConfirmed:    true,

    username:          "example", // (1)
    firstName:         "first", // (2)
    lastName:          "last", // (3)
    pictureUrl:        "https://...", // (4)

    locked:            false, // (5)
    enabled:           true, 
    mfaEnabled:        false, 

    createdAt:         1645131680,
    lastActiveAt:      1650654711,

    // Only returned if include_orgs = true
    orgIdToOrgInfo: { // (6)
        "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4" : {
            orgId: "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4", 
            orgName: "ExampleOrganization", 
            userRole: UserRole.Owner,
        }
    }
}
  1. Optional: Only returned if you have the field enabled as part of your user schema
  2. Optional: Only returned if you have the field enabled as part of your user schema
  3. Optional: Only returned if you have the field enabled as part of your user schema
  4. Optional: Only returned if you have the field enabled as part of your user schema
  5. True if the user's account is locked for security purposes.
  6. Includes all organizations that the user is a member of

Fetch user metadata by email

function fetchUserMetadataByEmail(email: string, includeOrgs?: boolean): Promise<UserMetadata | null>

// Example
const email = await db.fetchEmailFromAnalytics()
const userMetadata = await fetchUserMetadataByEmail(req.email)
if (userMetadata) {
    console.log("Found user " + userMetadata)
} else {
    console.log("No user found")
}

Successful response is the same as Fetch user metadata by id

Fetch user metadata by username

function fetchUserMetadataByUsername(username: string, includeOrgs?: boolean): Promise<UserMetadata | null>

// Example
app.get("/username/lookup", async (req, res) => {
    const userMetadata = await fetchUserMetadataByUsername(req.query.username);
    res.json({"userId": userMetadata.userId, "pictureUrl": userMetadata.pictureUrl});
})

Successful response is the same as Fetch user metadata by id

Batch fetch users by ids

function fetchBatchUserMetadataByUserIds(userIds: string[], includeOrgs?: boolean): Promise<{ [userId: string]: UserMetadata }>

Successful response:

Any IDs that don't have a match are not returned. Duplicate users are only returned once.

{
    "e9d3520f-836e-403c-82c2-09843517e1ce": {
        userId:            "e9d3520f-836e-403c-82c2-09843517e1ce",
        email:             "user@example.com",
        emailConfirmed:    true,

        username:          "example", // (1)
        firstName:         "first", // (2)
        lastName:          "last", // (3)
        pictureUrl:        "https://...", // (4)

        locked:            false, // (5)
        enabled:           true,
        mfaEnabled:        false,

        createdAt:         1645131680,
        lastActiveAt:      1650654711,

        // Only returned if include_orgs = true
        orgIdToOrgInfo: { // (6)
            "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4" : {
                orgId: "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
                orgName: "ExampleOrganization",
                userRole: UserRole.Owner,
            }
        }
    },
    // other user_ids
}
  1. Optional: Only returned if you have the field enabled as part of your user schema
  2. Optional: Only returned if you have the field enabled as part of your user schema
  3. Optional: Only returned if you have the field enabled as part of your user schema
  4. Optional: Only returned if you have the field enabled as part of your user schema
  5. True if the user's account is locked for security purposes.
  6. Includes all organizations that the user is a member of

Batch fetch users by emails

function fetchBatchUserMetadataByEmails(emails: string[], includeOrgs?: boolean): Promise<{ [email: string]: UserMetadata }> 

Successful response is the same as Batch fetch users by ids, but the keys are matching email addresses

Batch fetch users by usernames

function fetchBatchUserMetadataByUsernames(usernames: string[], includeOrgs?: boolean): Promise<{ [username: string]: UserMetadata }>

Successful response is the same as Batch fetch users by ids, but the keys are matching usernames

Search for users

function fetchUsersByQuery(usersQuery: UsersQuery): Promise<UsersPagedResponse>

// Example
const usersResponse = await auth.fetchUsersByQuery({emailOrUsername: "@example.com", orderBy: "LAST_ACTIVE_AT_DESC"})
for (const user of userResponse.users) {
    console.log("Found user " + user)
}
Argument Description
pageSize The number of results to return at a time. Must be between 1 and 100, default 10.
pageNumber Used to page over results
orderBy How to order the results. Must be one of: CREATED_AT_ASC, CREATED_AT_DESC, LAST_ACTIVE_AT_ASC, LAST_ACTIVE_AT_DESC, EMAIL, USERNAME
emailOrUsername Searches for partial matches within email addresses or usernames. port would match a user with email address support@propelauth.com.
includeOrgs Whether or not to include the user's organization information in the response. Default false

Successful response:

{
    "totalUsers": 103, 
    "currentPage": 0, 
    "pageSize": 10, 
    "hasMoreResults": true,
    "users": [{
        // See (1) for example users
    }, {
        // There will be 10 users in this response
    }]
}
  1. Example user in response

Fetch users in organization

function fetchUsersInOrg(usersInOrgQuery: UsersInOrgQuery): Promise<UsersPagedResponse>

// Example
app.get("/org/:orgId/members", requireOrgMember({minimumRequiredRole: UserRole.Admin}), async (req, res) => {
    const usersResponse = await auth.fetchUsersInOrg({
        orgId: req.org.orgId,
        pageNumber: req.params.pageNumber,
    })
})
Argument Description
orgId The organization to fetch users for
pageSize The number of results to return at a time. Must be between 1 and 100, default 10.
pageNumber Used to page over results
includeOrgs Whether or not to include the user's organization information in the response. Default false

Successful response:

{
    "total_users": 43, 
    "current_page": 0, 
    "page_size": 10, 
    "has_more_results": true,
    "users": [{
        // See (1) for example users
    }, {
        // There will be 10 users in this response, all in the specified organization
    }]
}
  1. Example user in response

Create User

function createUser(createUserRequest: CreateUserRequest): Promise<User>
Argument Description
email The user's email address
emailConfirmed Whether the email address should start off as already confirmed. If false, the user is required to confirm the email address before they sign in.
sendEmailToConfirmEmailAddress Whether we should send an email immediately to confirm the user's email address. If false, the user will get a confirmation email when they first sign in.
password Optional password. If unset, the user can set it themselves via their account page
username Optional username. Can only be used if username is enabled in your user schema
firstName Optional first name. Can only be used if name is enabled in your user schema
lastName Optional last name. Can only be used if name is enabled in your user schema

Successful response:

{
    "userId": "b5f667fb-e51a-49c6-a396-711e62948689"
}

Creates a magic link that a user can use to log in. Use this API if you'd prefer to send the magic link to the customer yourself, otherwise, we have createUser which will email them directly.

function createMagicLink(createUserRequest: CreateMagicLinkRequest): Promise<MagicLink>
Argument Description
email The user's email address
redirectToUrl (Optional) Where to redirect the user after they login. If unspecified, will use the login redirect path specified in your dashboard.
expiresInHours (Optional) How many hours should the link be valid for?
createNewUserIfOneDoesntExist (Optional) If true, createMagicLink will create a new user if one matching the provided email address doesn't exist. If false, the request will fail if no user with that email exists. Default is true.

Successful response:

{
    "url": "https://auth.yourdomain.com/..."
}

Update User Email

// Returns true if it was successful, false if the user was not found
function updateUserEmailWrapper(userId: string, updateUserEmailRequest: UpdateUserEmailRequest): Promise<boolean>
Argument Description
newEmail New email address for this user
requireEmailConfirmation Whether the new email address requires confirmation. If true, an email will be sent to the new email address to confirm. If false, the users email address will be updated and confirmed immediately.

Update User Metadata

// Returns true if it was successful, false if the user was not found
function updateUserMetadata(userId: string, updateUserMetadataRequest: UpdateUserMetadataRequest): Promise<boolean>
Argument Description
username Optional username. Can only be used if username is enabled in your user schema
firstName Optional first name. Can only be used if name is enabled in your user schema
lastName Optional last name. Can only be used if name is enabled in your user schema

Fetch an organization

function fetchOrg(orgId: string): Promise<Org | null>

Successful response:

{
    "orgId": "d488996d-8ccc-4101-b5f2-131f5f09ddb6"
    "name": "OneOfYourCustomers"
}

Fetch all organizations

function fetchOrgsByQuery(orgQuery: OrgQuery): Promise<OrgQueryResponse>
Argument Description
pageSize The number of results to return at a time. Must be between 1 and 100, default 10.
pageNumber Used to page over results
orderBy How to order the results. Must be one of: CREATED_AT_ASC, CREATED_AT_DESC, NAME

Successful response:

{
    "totalOrgs": 21, 
    "currentPage": 0, 
    "pageSize": 10, 
    "hasMoreResults": true,
    "orgs": [{
        "orgId": "d488996d-8ccc-4101-b5f2-131f5f09ddb6",
        "name": "OneOfYourCustomers"
    }, {
        // There will be 10 orgs in this response
    }]
}

UserRole

An enum of roles within an organization. Values, in order of highest to lowest, include

UserRole.Owner
UserRole.Admin
UserRole.Member

If you use the middleware requireOrgMember({minimumRequiredRole: UserRole.Admin}), then users with roles Owner and Admin are allowed, but Member is rejected.