@ookla/common-auth0-node
v9.1.1
Published
Release notes: [`CHANGELOG.md`](https://github.com/teamookla/common-auth0/blob/master/server/CHANGELOG.md)
Maintainers
Keywords
Readme
@ookla/common-auth0-node
Release notes: CHANGELOG.md
Contributing
Install dependencies
$ yarn installPublish
$ ../publish.sh server <major|minor|patch> [tag]Setup
Important: Ensure common-auth0-node is required in your entrypoint, so that there are no uses of config.get() prior. Because common-auth0-node modifies your app's config, it must be initialized early - the first config.get() call makes config immutable.
Database initialization
This module uses common-connect-db for managing users in the database. The
database connection needs to be initialized during app startup. See
common-connect-db docs.
Config
The following values should be set in the app's config in order to use this library.
See test/config/default.js as an example.
Note: mailer requires additional config to use.
Property | Description
:----------|:----------------
auth0.domain | Auth0 account domain
auth0.authorization.audience | Authorization API audience
auth0.management.audience | Management API audience
auth0.management.clientId | Management Client ID
auth0.management.clientSecret | Management Client secret
auth0.management.connection | Management Client connection
Optional: (defaults are set here) |
auth0.perPage | Number of items to fetch per request
auth0.maxRetriesauth0.retryDelayMs | When making requests to Auth0's user search API, the module will retry up to maxRetries times if the request fails with a 5xx error, waiting retryDelayMs between each attempt.
auth0.jwks.cacheauth0.jwks.rateLimitauth0.jwks.requestsPerMinute | Configuration for jwks-rsa, used to validate access tokens
auth0.accessTokenRequestProperty | Key used by express-jwt when storing decoded access token on req
enableLogging(options)
Enable logging of responses from auth0 API.
Params: options (Object)
[.error](function) - Called with error. Expected to have propertiesmessage,code,statusCode[.success](function) - Called with successful response
Example
commonAuth0.enableLogging({
error: (err) => {
log.error({ err }, 'Auth0 API call failed');
},
success: (res) => {
log.error({ incomingRes: res }, 'Auth0 API call completed');
}
});middleware
- authorizeForContactCustomerId
- authorizeForCustomerId
- authorizeForServerId
- authorizeForSubscriptionCustomerId
- authorizeForUserCustomerId
- requireApiUser
- requireEmbeddedUser
- requireLoggedInUser
- requirePermission
authorizeForContactCustomerId(contactIdResolver) ⇒ function
Middleware that requires the contact to belong to the same customer as the request customerId
Should run after requireLoggedInUser.
Params: contactIdResolver (string | function) - a property path or function of req object that resolves the requested contactId
Returns: function - Express middleware function
Example
router.use(commonAuth0.authorizeForContactCustomerId('params.contactId'));authorizeForCustomerId(customerIdResolver) ⇒ function
Middleware that requires the user's customerId to match the request customerId
Should run after requireLoggedInUser.
Params: customerIdResolver (string | function) - a property path or function of req object that resolves the requested customerId
Returns: function - Express middleware function
Example
router.use(commonAuth0.authorizeForCustomerId('params.customerId'));authorizeForServerId(serverIdResolver) ⇒ function
Middleware that requires the user's customerId be the owner of the serverId
Should run after requireLoggedInUser.
Params: serverIdResolver (string | function) - a property path or function of req object that resolves the requested serverId
Returns: function - Express middleware function
Example
router.use(commonAuth0.authorizeForServerId((req) => req.params.serverId));authorizeForSubscriptionCustomerId(subscriptionIdResolver) ⇒ function
Middleware that requires the user to belong to the same customer as the request subscriptionId
Should run after requireLoggedInUser.
Params: subscriptionIdResolver (string | function) - a property path or function of req object that resolves the requested subscriptionId
Returns: function - Express middleware function
Example
router.use(commonAuth0.authorizeForSubscriptionCustomerId('params.subscriptionId'));authorizeForUserCustomerId(userIdResolver) ⇒ function
Middleware that requires the user to belong to the same customer as the request customerId
Should run after requireLoggedInUser.
Params: userIdResolver (string | function) - a property path or function of req object that resolves the requested userId
Returns: function - Express middleware function
Example
router.use(commonAuth0.authorizeForUserCustomerId('params.userId'));requireApiUser(apiNames) ⇒ Array
Middleware for routes that require an API user authenticated through basic-auth.
Consists of:
validateBasicAuth: Decrypts basic-auth header & verifies validity of JWT secret. May yieldbasicAuthMissingorbasicAuthInvaliderror.loadApiUser: Fetches user and stores it atreq.user. YieldsinvalidApiCredentialsif no user could be found using given credentials.
Params: apiNames (Array) - API names to reference. Valid values: 'extracts', 'legacyExtracts'
Returns: Array - Array of middleware functions - see above
Example
app.use(commonAuth0.requireApiUser(['extracts', 'legacyExtracts']));requireEmbeddedUser() ⇒ Array
Middleware for routes that require a embedded user with a valid jwt token.
Consists of:
validateEmbeddedToken: Validates the jwt token from the client. Looks for token atreq.headers.authorizationorreq.query.token.loadEmbeddedUser: Fetches user and stores it atreq.user. Yieldsunauthorizedif no user can be found, orinvalidUserConfigif user has no customer id, invalidToken if the token is invalid.
Returns: Array - Array of middleware functions - see above
Example
router.use(commonAuth0.requireEmbeddednUser());requireLoggedInUser() ⇒ Array
Middleware for routes that require a logged-in user with a valid customer id.
Consists of:
validateJwt: Validates the access token from the client. Looks for token atreq.headers.authorizationorreq.query.token.loadUser: Fetches user and stores it atreq.user. Yieldsunauthorizedif no user can be found, orinvalidUserConfigif user has no customer id, or customer id is not valid.
Returns: Array - Array of middleware functions - see above
Example
router.use(commonAuth0.requireLoggedInUser());requirePermission(permission) ⇒ function
Middleware that requires the user to have at least one of the given permissions applied generally (not attached to a specific resource)
Should run after requireLoggedInUser.
Params: permission (Object | Array) - A permission or an array of permissions
to validate user against.
Returns: function - Express middleware function
Example
router.use(commonAuth0.requireLoggedInUser());
// Require user to have any of the given permissions
router.get('/users',
commonAuth0.requirePermission([ConnectViewerAdmin, ConnectAdmin, ConnectAdminSupport]),
users.viewAll);
// Require user to have a particular permission
router.get('/users/edit', commonAuth0.requirePermission(ConnectAdmin), users.edit);users
const users = require('@ookla/common-auth0-node').users;Generated using @ookla/common-connect-db version 7.0.0
- getAuth0User
- initializeDb
- create
- update
- remove
- getById
- getByEmail
- getByCustomerId
- getByAuth0Id
- getByApiKey
- getApiKeyId
- getCurrentSubscription
- hasPermission
- hasPermissionForSubscription
- hasPermissionForServer
- addApiKey
- getCurrentSubscription
- getSubscriptions
- user
users.getAuth0User
Get a auth0 user by auth0Id
users.initializeDb() ⇒ Promise
DEPRECATED: Use common-connect-db directly to initialize connections:
docs
Returns: Promise - Resolves when db connection is complete, rejects if it is unable to connect
users.create(data) ⇒ Promise.<user>
Create a new user
Params: data (Object) - user fields. The following
properties are required: email, password, firstName, lastName,
customerId
Returns: Promise.<user> - resolves with the created user
users.update(userId, data) ⇒ Promise.<user>
Update a user
Saves changes to database and updates auth0 if applicable
Params:
userId(number)data(Object) - user fields to update
Returns: Promise.<user> - resolves with updated user
users.remove(userId) ⇒ Promise.<boolean>
Delete a user from database and auth0
Params: userId (number)
Returns: Promise.<boolean> - resolves with true if user has been successfully
deleted or with false if there was nothing to delete (user was not found in db)
users.getById(userId) ⇒ Promise.<(user|null)>
Get db user by id
Params: userId (number)
Returns: Promise.<(user|null)> - Matching user or null
users.getByEmail(email) ⇒ Promise.<(user|null)>
Get db user by email
Params: email (string)
Returns: Promise.<(user|null)> - Matching user or null
users.getByCustomerId(customerId) ⇒ Promise.<Array>
Get all users associated with the given customer
Note: These users do not come with .permissions
Params: customerId (number)
Returns: Promise.<Array> - Array of users
users.getByAuth0Id(auth0Id) ⇒ Promise.<(user|null)>
Get db user by auth0 id
Params: auth0Id (string)
Returns: Promise.<(user|null)> - Matching user or null
users.getByApiKey(keyName, keyPair) ⇒ Promise.<(user|null)>
Get db user by API key
Params:
keyName(string) - Name of API key to reference (extractsorlegacyExtracts)keyPair(object) - API key object withidandsecretproperties
Returns: Promise.<(user|null)> - Matching user or null
users.getApiKeyId(userId, keyName) ⇒ Promise.<(String|null)>
Get API key id for a user
Params:
userId(number)keyName(string) - Name of API key to get (extractsorlegacyExtracts)
Returns: Promise.<(String|null)> - User's API key id
users.getCurrentSubscription(user, application) ⇒ Promise.<(Object|null|Error)>
Get the user's currently selected subscription for the given application. Finds the first valid subscription if user has no selected subscription stored, or if it is not valid.
Users may modify selectedSubscriptionId in user's application preferences.
Calls _updateSelectedSubscription to upsert the valid subscription id as the user's the stored preference.
If upsert fails, an error is thrown.
A subscription is "valid" if:
- it has the same customerId as the user, and customerId is valid
- it has a subscriptionTypeId compatible with the given application
- it is not expired, and its start date is not in the future
- its status is ENABLED
- the user has permission to access it (ex:
SpeedtestIntelligencefor STI,SpeedtestCustomorSpeedtestCustomReportingfor STC)
Params:
user(Object) - User object includingpermissionspropertyapplication(Object) - Either canonicalApplications.STI or canonicalApplications.STC
Returns: Promise.<(Object|null|Error)> - A valid subscription if any could be found
users.hasPermission(user, permissionId) ⇒ boolean
Determine whether user has the given permission at the top-level (not applied to a specific resource)
Params:
user(Object) - User, with.permissionspropertypermissionId(number)
Returns: boolean - True if match was found in user's permissions
users.hasPermissionForSubscription(user, criteria) ⇒ boolean
Determine if a user has the given permission for a subscriptions.
Params:
user(Object) - User, with.permissionspropertycriteria(Object).permissionId(number).subscriptionId(number)
Returns: boolean - Returns true if user has the permission for the specific
subscription, or has the permission at the top-level (not attached to a
particular resource)
users.hasPermissionForServer(user, criteria) ⇒ boolean
Determine if a user has the given permission for server.
Params:
user(Object) - User, with.permissionspropertycriteria(Object).permissionId(number).serverId(number)
Returns: boolean - Returns true if user has the permission for the specific
server, or has the permission at the top-level (not attached to a
particular resource)
users.addApiKey(userId, keyName) ⇒ Promise.<(Object|null)>
Create new API key for a user. Overwrites previously-created API key.
Params:
userId(number)keyName(string) - Name of API key to create (extractsorlegacyExtracts)
Returns: Promise.<(Object|null)> - Newly created API key with id & secret properties
users.getCurrentSubscription(user, application) ⇒ Promise.<(Object|null|Error)>
Get the user's currently selected subscription for the given application. Finds the first valid subscription if user has no selected subscription stored, or if it is not valid.
Users may modify selectedSubscriptionId in user's application preferences.
Calls _updateSelectedSubscription to upsert the valid subscription id as the user's the stored preference.
If upsert fails, an error is thrown.
A subscription is "valid" if:
- it has the same customerId as the user, and customerId is valid
- it has a subscriptionTypeId compatible with the given application
- it is not expired, and its start date is not in the future
- its status is ENABLED
- the user has permission to access it (ex:
SpeedtestIntelligencefor STI,SpeedtestCustomorSpeedtestCustomReportingfor STC)
Params:
user(Object) - User object includingpermissionspropertyapplication(Object) - Either canonicalApplications.STI or canonicalApplications.STC
Returns: Promise.<(Object|null|Error)> - A valid subscription if any could be found
users.getSubscriptions(user, application) ⇒ Promise.<Array>
Get the name and id of every valid subscription for the given user and application.
See Users.getCurrentSubscription for description of what "valid" means.
Params:
user(Object) - User object includingpermissionspropertyapplication(Object) - Either canonicalApplications.STI or canonicalApplications.STC
Returns: Promise.<Array> - List of subscriptions. Each has name and subscriptionId
user
User object format used by this module.
Note: createdAt is only present on the user returned by create.
It is passed to the common-connect-db users module as creationDate
and used to populate that field in our db. updatedAt was removed
as of CONNECT-858 because it does not map to any fields in our db.
Params:
userId(number) - User id from database. Read-onlyauth0Id(string) - Auth0 user id. Read-onlycustomerId(number)email(string)emailVerified(boolean)firstName(string)lastName(string)createdAt(string) - See note above.password(string) - Write-onlycustomData(Object)permissions(Array.<Object>) - SeeuserPermissions
mailer
Send emails using sendgrid. Uses pug for templating.
Requires the following app config:
Config key | Description
:-----------------------------|:------------------
mailer.sendgridApiKey | Sendgrid API key
mailer.emailLinkTokenSecret | Secret used to generate tokens
mailer.baseLinkUrl | Optional - see tokenEmailOptions
mailer.sendPasswordReset(email, [options]) ⇒ Promise.<Object>
Send an email with a link that lets the user change their password.
Config key: resetPassword - see tokenEmailOptions
Params:
email(string) - user email[options](tokenEmailOptions)
Returns: Promise.<Object> - resolves with Sendgrid API response
mailer.sendEmailVerification(email, [options]) ⇒ Promise.<Object>
Send an email with a link that lets the user verify their email address.
Config key: verifyEmail - see tokenEmailOptions
Params:
email(string) - user email[options](tokenEmailOptions)
Returns: Promise.<Object> - resolves with Sendgrid API response
mailer.sendWelcomeEmail(email, [options]) ⇒ Promise.<Object>
Send an email with a link that lets users verify their email addresses and will prompt users to set their password when they hit the /welcome route.
Config key: welcome - see tokenEmailOptions
Params:
email(string) - user email[options](tokenEmailOptions)
Returns: Promise.<Object> - resolves with Sendgrid API response
mailer.sendMail(options) ⇒ Promise.<Object>
Send a custom email using the sendgrid API.
Params: options (Object)
.fromAddress(string) - "from" email address.name(string) - "from" name.to(string) - recipient email address.cc(string) - optional cc email address.subject(string) - email subject.text(string) - path to pug template.html(string) - path to pug template[.templateContext](Object) - locals for rendering the templates[.attachments](array) - [{ filename: string, type: string, content: 64bit-encoded }] - https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html
Returns: Promise.<Object> - resolves with Sendgrid API response
tokenEmailOptions
sendPasswordReset and sendEmailVerification require the following options, either:
- In app config, under
mailer.<configKey>, or - Passed to the function as
optionsparam
Notes:
- Templates will be rendered with locals
expirationWindowandlinkUrl. - Either
linkUrlorlinkPathis required
Params:
fromAddress(string) - "from" email addressname(string) - "from" namesubject(string) - email subjecttextBodyFile(string) - path to text pug templatehtmlBodyFile(string) - path to html pug templateexpirationWindowHours(number) - duration of token email link, in hours[linkPath](string) - link path - requiresmailer.baseLinkUrlconfig[linkUrl](string) - link url[context](Object) - optional extra context for template
See: test config for examples.
emailToken
emailToken.generate(email, expireHours, overrideSecret) ⇒ string
Generate a JWT token using the secret specified by mailer.emailLinkTokenSecret
config.
Used by mailer methods.
Params:
email(string) - User emailexpireHours(number) - Duration of token, in hoursoverrideSecret(string) - Secret to pass in instead of config default
Returns: string - Encoded JWT token
emailToken.decode(token, overrideSecret) ⇒ string
Decode the given token.
Params:
token(string) - JWT token created by generateoverrideSecret(string) - Secret to pass in instead of config default
Returns: string - User email from token payload
Throws: Error invalidToken or expiredToken
