Next.js API Route Reference¶
Installation¶
Next.js API routes support express compatible middleware, so we will use the @propelauth/express library.
Initialize¶
In lib/propelauth.js:
import {initAuth} from "@propelauth/express";
const propelauth = initAuth({
debugMode: true, // (1)
authUrl: "REPLACE_ME", // (2)
apiKey: "REPLACE_ME", // (3)
manualTokenVerificationMetadata: {
verifierKey: "REPLACE_ME", // (4)
issuer: "REPLACE_ME", // (5)
}
})
export default propelauth
- 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.
- 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.
- You can manage your api keys under the Backend Integration section for your project.
- You can find this under the Backend Integration section for your project at https://app.propelauth.com.
- You can find this under the Backend Integration section for your project at https://app.propelauth.com.
You can then import what you need from propelauth in your routes:
import {
requireUser,
optionalUser,
requireOrgMember,
fetchUserMetadataByUserId,
fetchUserMetadataByEmail,
fetchUserMetadataByUsername,
fetchBatchUserMetadataByUserIds,
fetchBatchUserMetadataByEmails,
fetchBatchUserMetadataByUsernames,
fetchOrg,
fetchOrgByQuery,
fetchUsersByQuery,
fetchUsersInOrg,
createUser,
updateUserMetadata,
updateUserEmail,
UserRole,
} from "../../lib/propelauth"
Running middleware in a NextJS API route¶
The full details are available in the official docs.
However, the main thing you need to know is you can create this helper function:
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
export function runMiddleware(req, res, fn) {
return new Promise((resolve, reject) => {
fn(req, res, (result) => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
And then use that to call middleware within your routes:
async function handler(req, res) {
// Run the middleware
await runMiddleware(req, res, YOUR_MIDDLEWARE)
// Rest of the API logic
res.json({message: 'Hello Everyone!'})
}
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.
import {requireUser} from "../../lib/propelauth"
async function handler(req, res) {
// Verifies that a request was made with a valid access token
await runMiddleware(req, res, requireUser)
// req.user is now set from our requireUser middleware
res.status(200).json({message: "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.
import {optionalUser} from "../../lib/propelauth"
async function handler(req, res) {
await runMiddleware(req, res, optionalUser)
if (req.user) {
res.status(200).json({message: "Hello user with ID " + req.user.userId});
} else {
res.status(200).json({message: "Hello anonymous"})
}
}
requireOrgMember¶
An Express middleware that will verify the request was made by a valid user that belongs to a specific organization.
import {requireOrgMember} from "../../lib/propelauth"
const requireAdminInOrg = requireOrgMember({
// Tell our middleware that the orgId comes from `req.query`
orgIdExtractor: (req) => req.query.orgId,
// Users who call this API must be at least an Admin within this org
minimumRequiredRole: propelauth.UserRole.Admin,
})
async function handler(req, res) {
await runMiddleware(req, res, requireAdminInOrg)
// requireAdminInOrg automatically sets req.org
res.status(200).json(
{message: "You are an owner or admin of " + 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.
- (Optionally) Check that the user's role in that organization is >= minimumRequiredRole. If not, the request is rejected with a 403 status code.
- 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)
}
- 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) {
}
- 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.
- 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
async function handler(req, res) {
// Verifies that a request was made with a valid access token
await runMiddleware(req, res, requireUser)
// Fetch and return latest user metadata for this user from PropelAuth
const userMetadata = await fetchUserMetadataByUserId(req.user.userId)
res.status(200).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,
}
}
}
- Optional: Only returned if you have the field enabled as part of your user schema
- Optional: Only returned if you have the field enabled as part of your user schema
- Optional: Only returned if you have the field enabled as part of your user schema
- Optional: Only returned if you have the field enabled as part of your user schema
- True if the user's account is locked for security purposes.
- 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
const userMetadata = await fetchUserMetadataByUsername(req.query.username);
res.status(200).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
}
- Optional: Only returned if you have the field enabled as part of your user schema
- Optional: Only returned if you have the field enabled as part of your user schema
- Optional: Only returned if you have the field enabled as part of your user schema
- Optional: Only returned if you have the field enabled as part of your user schema
- True if the user's account is locked for security purposes.
- 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
}]
}
Fetch users in organization¶
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
}]
}
Create 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. |
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:
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¶
Successful response:
Fetch all organizations¶
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
If you use the middleware requireOrgMember({minimumRequiredRole: UserRole.Admin})
, then users with roles Owner
and Admin
are allowed,
but Member
is rejected.