- Backend
- Express Library Reference
Backend
Express Library Reference
Installation
$ npm install --save @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,
requireOrgMemberWithMinimumRole,
requireOrgMemberWithExactRole,
requireOrgMemberWithPermission,
requireOrgMemberWithAllPermissions,
fetchUserMetadataByUserId,
fetchUserMetadataByEmail,
fetchUserMetadataByUsername,
fetchBatchUserMetadataByUserIds,
fetchBatchUserMetadataByEmails,
fetchBatchUserMetadataByUsernames,
fetchOrg,
fetchOrgByQuery,
fetchUsersByQuery,
fetchUsersInOrg,
createUser,
updateUserMetadata,
updateUserEmail,
createMagicLink,
migrateUserFromExternalSource,
createOrg,
addUserToOrg,
deleteUser,
disableUser,
enableUser,
allowOrgToSetupSamlConnection,
disallowOrgToSetupSamlConnection,
} = propelAuth.initAuth({
// 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.
debugMode: true,
// You can find your Auth URL and API key under the Backend Integration
// section for your project at https://app.propelauth.com.
authUrl: "https://auth.yourdomain.com",
apiKey: "YOUR_API_KEY",
});
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:
- Check that a valid access token was provided. If not, the request is rejected
with a
401
status code. - 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:
Property | Description |
---|---|
userId | The id of the user |
orgIdToOrgMemberInfo | A dictionary of org ids to metadata about the org. Includes all orgs that the user is in |
legacyUserId | The original ID of the user, if the user was migrated from an external source |
The values of orgIdToOrgMemberInfo are OrgMemberInfo’s, with the following fields/functions:
Field | Description |
---|---|
orgId | The id of the org |
orgName | The name of the org |
urlSafeOrgName | A URL-safe version of the name of the org |
assignedRole(): string | The user’s role within the organization. See Roles and Permissions for more details. |
permissions(): string[] | The user’s permissions within the organization. See Roles and Permissions |
isRole(role: string): boolean | Returns True if the user’s role within the organization matches the role passed in |
isAtLeastRole(role: string): boolean | Returns True if the user’s role within the organization is at least the role passed in. If the hierarchy of roles is Owner => Admin => Member, specifying “Admin” will return True for Admins and Owners, but False for Members. |
hasPermission(permission: string): boolean | Returns True if the user has a specific permission. The users’ permissions are derived from their role within this organization. |
hasAllPermissions(permissions: string[]): boolean | Returns True if the user has all the specified permissions. The users’ permissions are derived from their role within this organization. |
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(), (req, res) => {
res.text("You are in " + req.org.orgName);
});
Specifically, it will:
- Check that a valid access token was provided. If not, the request is rejected
with a
401
status code. - Set user on the Request with the user’s information. See requireUser for what the user object looks like.
- Find an id for an organization within the request. By default, it will check Request.params.orgId (a path parameter named orgId).
- Check that the user is a member of that organization. If not, the request is
rejected with a
403
status code. - Set org on the Request with the organization’s information for this user.
Roles and Permissions
A user has a Role within an organization. By default, the available roles are Owner, Admin, or Member, but these can be configured. These roles are also hierarchical, so Owner > Admin > Member.
Roles allow you to control what different users can do within your product. If
you want to check a user’s role, you can use requireOrgMemberWithExactRole
or
requireOrgMemberWithMinimumRole
.
// Assuming a Role structure of Owner => Admin => Member
app.get(
"/org/:orgId/admin",
requireOrgMemberWithExactRole({ role: "Admin" }),
(req, res) => {
res.text("You are an Admin of " + req.org.orgName);
}
);
app.get(
"/org/:orgId/admin_or_owner",
requireOrgMemberWithMinimumRole({ minimumRequiredRole: "Admin" }),
(req, res) => {
res.text("You are an Admin of " + req.org.orgName);
}
);
Permissions are arbitrary strings associated with a role. For example, can_view_billing, ProductA::CanCreate, and ReadOnly are all valid permissions. The PropelAuth dashboard allows you to set up these permissions.
You can use requireOrgMemberWithPermission or requireOrgMemberWithAllPermissions to check for a given permission.
app.get(
"/org/:orgId/billing",
requireOrgMemberWithPermission({ permission: "can_view_billing" }),
(req, res) => {
// TODO: view billing
}
);
All of these functions, just like requireOrgMember, can also take in orgIdExtractor, and will set both req.user and req.org on a valid request.
Fetch user metadata by id
function fetchUserMetadataByUserId(userId: string, includeOrgs?: boolean): Promise<UserMetadata | null>
Successful response (UserMetadata):
{
userId: "e9d3520f-836e-403c-82c2-09843517e1ce",
email: "user@example.com",
emailConfirmed: true,
hasPassword: true,
username: "example",
firstName: "first",
lastName: "last",
pictureUrl: "https://...",
// True if the user's account is locked for security purposes.
locked: false,
enabled: true,
mfaEnabled: false,
createdAt: 1645131680,
lastActiveAt: 1650654711,
// Only returned if includeOrgs = true
orgIdToOrgInfo: {
"2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4" : {
orgId: "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
orgName: "ExampleOrganization",
userAssignedRole: "Owner",
}
},
// Only returned if user was migrated from an external system
legacyUserId: "507f191e810c19729de860ea"
}
Fetch user metadata by email
function fetchUserMetadataByEmail(email: string, includeOrgs?: boolean): Promise<UserMetadata | null>
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>
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 }>
Any IDs that don’t have a match are not returned. Duplicate users are only returned once.
Successful response:
{
"e9d3520f-836e-403c-82c2-09843517e1ce": {
userId: "e9d3520f-836e-403c-82c2-09843517e1ce",
email: "user@example.com",
emailConfirmed: true,
hasPassword: true,
username: "example",
firstName: "first",
lastName: "last",
pictureUrl: "https://...",
// True if the user's account is locked for security purposes.
locked: false,
enabled: true,
mfaEnabled: false,
createdAt: 1645131680,
lastActiveAt: 1650654711,
// Only returned if includeOrgs = true
orgIdToOrgInfo: {
"2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4" : {
orgId: "2ef0e1fc-234f-4dc0-a50c-35adb1bbb7e4",
orgName: "ExampleOrganization",
userAssignedRole: "Owner",
}
},
// Only returned if user was migrated from an external system
legacyUserId: "507f191e810c19729de860ea"
},
// other user_ids
}
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
}]
}
Fetch users in organization
function fetchUsersInOrg(usersInOrgQuery: UsersInOrgQuery): Promise<UsersPagedResponse>
// Example
const usersResponse = await auth.fetchUsersInOrg({
orgId: req.params.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:
{
"totalUsers": 43,
"currentPage": 0,
"pageSize": 10,
"hasMoreResults": true,
"users": [{
// See (1) for example users
}, {
// There will be 10 users in this response, all in the specified organization
}]
}
Create User
function createUser(createUserRequest: CreateUserRequest): Promise<User>
Argument | Description |
---|---|
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. |
askUserToUpdatePasswordOnLogin | Whether we should require the user to set or update their password on initial login. Default false |
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"
}
Migrate User from External Source
Similar to Create User, but for cases where you already have a user stored in an external system. You can, for example, pass in a password hash for an existing user, and they will be able to login with their same password. It is also possible to provide an existing identifier, and we will store and return it along with our future identifiers, allowing you to link them.
function migrateUserFromExternalSource(migrateUserFromExternalSourceRequest: MigrateUserFromExternalSourceRequest): Promise<User>
Argument | Description |
---|---|
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. |
askUserToUpdatePasswordOnLogin | Whether we should require the user to set or update their password on initial login. Default false |
existingUserId | (Optional) The user’s ID in the existing system. This ID will be stored on the user as a legacyUserId and it is present everywhere userId’s are (e.g. fetching user metadata or validating a user’s token). |
existingPasswordHash | (Optional) The user’s hashed password. We support both bcrypt and argon2 password hashes. |
existingMfaBase32EncodedSecret | (Optional) The user’s MFA secret, base32 encoded. If specified the user will have MFA enabled by default. |
enabled | (Optional) Whether or not the user can login |
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"
}
Create Magic Link
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 |
---|---|
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/..."
}
Create Access Token
Our frontend libraries allow you to get access tokens that can be sent to the backend. The backend is then responsible for verifying these tokens.
For testing purposes, you may want to create access tokens separately from the frontend and set a custom expiration time. With this API, you can create access tokens for users programmatically and set a custom expiration.
function createAccessToken(createAccessTokenRequest: CreateAccessTokenRequest): Promise<AccessToken>
Argument | Description |
---|---|
userId | The user’s ID |
durationInMinutes | How long the token is valid for, in minutes. |
Successful response:
{
"access_token": "..."
}
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 |
Update Password
// Returns true if it was successful, false if the user was not found
function updateUserPassword(userId: string, updateUserPasswordRequest: UpdateUserPasswordRequest): Promise<boolean>
Argument | Description |
---|---|
password | The users new password |
askUserToUpdatePasswordOnLogin | Whether we should require the user to set or update their password the next time they visit your product. Default false |
+Returns true if the user’s password was updated.
Delete user
function deleteUser(userId: string): Promise<boolean>
Returns true if the user was deleted.
Disable user
function disableUser(userId: string): Promise<boolean>
Returns true if the user was disabled. Afterwards, the user is logged out and unable to log back in.
Enable user
function enableUser(userId: string): Promise<boolean>
Returns true if the user was enabled, allowing them to log in again.
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
}]
}
Create Organization
function createOrg(createOrgRequest: CreateOrgRequest): Promise<Org>
Argument | Description |
---|---|
name | The organization’s name |
Successful response:
{
"orgId": "d488996d-8ccc-4101-b5f2-131f5f09ddb6"
}
Add user to organization
function addUserToOrg(addUserToOrgRequest: AddUserToOrgRequest): Promise<boolean>
Argument | Description |
---|---|
userId | The user’s ID |
orgId | The org’s ID |
role | The role of the user in that organization, e.g. Admin |
Returns true if the user was successfully added to the organization.
Allow organization to set up SAML connection
function allowOrgToSetupSamlConnection(orgId: string): Promise<boolean>
Returns true if successful, meaning the organization can now set up SAML connections.
Disallow organization to set up SAML connection
function disallowOrgToSetupSamlConnection(orgId: string): Promise<boolean>
Returns true if successful, meaning the organization can no longer set up SAML connections.