npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@morphatic/feathers-auth0-authorize-hook

v1.0.3

Published

A "before" hook for FeathersJS to authorize requests accompanied by an Auth0-issued JWT.

Downloads

6

Readme

FeathersJS Auth0 Authorization Hook

Build Status Coverage Status npm version GitHub license

This plugin was designed to be used in scenarios where FeathersJS is being used solely as a backend API server, and Auth0 is being used for authentication from a frontend client written with, e.g. Vue, React, or Angular. For a fuller discussion of this scenario and why I chose to write this plugin, check out this blog post. If you are using Feathers for BOTH the backend AND the frontend, you're probably much better off using the @feathersjs/authentication that's already part of the framework.

Installation and Configuration

To install this plugin, from the root of your package:

npm install --save @morphatic/feathers-auth0-authorize-hook

In your feathers app config (usually found in config/default.json) you'll need to add the following properties. You'll want to update them with your actual Auth0 domain, i.e. swap out the example below with the domain for your app which you can find in your Auth0 application settings.

{
  "jwksUri": "https://example.auth0.com/.well-known/jwks.json",
  "jwtOptions": {
    "algorithms": [
      "RS256"
    ],
    "audience": [
      "https://example.auth0.com/api/v2/",
      "https://example.auth0.com/userinfo"
    ],
    "ignoreExpiration": false,
    "issuer": "https://example.auth0.com/"
  }
}

Also, you will need to create or update two services: users and keys. You'll also need to add a simple middleware.

The users service

It's likely that you already have a users service in your app. If you don't you'll need to create one, e.g. by using the feathers-plus generator. Assuming you already have a users service, you'll need to ensure that your model has two string properties: user_id and currentToken. user_id will hold the user_id generated by Auth0 when the user's account was created. currentToken will be used to store already-verified tokens, to prevent the app from having to re-verify tokens on every request. For reference, a JSON Schema representation of a minimal users service is included in this repo.

The keys service

You'll also need to generate a keys service in your app. This is used to store JSON web keys (JWKs) retrieved from Auth0's JWKS endpoint. This service does not need to be persistent (I use an in-memory service) as it is mainly used to cache already-retrieved JWKs and improve performance. For reference, please see the JSON Schema version of the keys service contained in this repo.

Although not strictly necessary, since the keys service does not contain any non-public information, I typicaly add a hook to prevent the keys service from being called by external clients like this:

// src/services/keys/keys.hooks.js
const { disallow } = require('feathers-hooks-common')
module.exports = {
  before: {
    all: [
      disallow('external')
    ]
  }
}

The middleware

This hook relies upon extracting a JWT from the Authorization HTTP header (and as such ONLY works for REST transports--not socket.io/primus). By default, however, HTTP headers are not available to hooks, so you need to create middleware to make them so. One way to do this is to generate new middleware with a CLI generator and add the following code to it:

// src/middleware/authorize.js
module.exports = function (options = {}) {
  return function authorize(req, res, next) {
    req.feathers = { ...req.feathers, headers: req.headers }
    next()
  }
}

Alternatively, you can modify src/app.js to modify the REST configuration section as follows:

// src/app.js
app.configure(express.rest(
  (req, res, next) => {
    req.feathers = { ...req.feathers, headers: req.headers }
    next()
  }
))

(Personally, I prefer the former method as I like to leave the generated app.js file basically untouched.)

Using the authorize hook

You'll need to make changes on both the server and the client to use this hook.

On the server

Once installed you'll need to add this hook into your feathers app. You can use it either for the entire app or for individual services. I find it easiest to set it up in conjunction with feathers-hooks-common. This hook ONLY works as a before hook. Here's an example of using it for your entire app (NOTE: I've abbreviated this to just the before.all hook.):

// src/app.hooks.js
// import feathers-hooks-common `isProvider` and `unless`
const { isProvider, unless } = require('feathers-hooks-common')
// import the authorize hook
const { authorize } = require('@morphatic/feathers-auth0-authorize-hook')() // <-- note the parentheses

module.exports = {
  before: {
    all: [
      unless(isProvider('server'), authorize)
    ]
  }
}

Setting it up this way will not require internal service calls to be authorized. If you don't do this, it could make your feathers app unusable since internal functions would have no way to be authorized.

On the client

From your frontend app, you'll need to set up any services that access your feathers API as follows. I've used this in a Vue app, but it should work equally well for any other frontend framework like React or Angular. In this example, I'm imagining that I'm developing the proverbial To Do List app, and that my To Do list items are stored in my feathers backend.

// src/services/feathers.js
import feathers from '@feathersjs/client'
import axios from 'axios' // NOTE: this only works for REST clients

const app = feathers()
const rc = feathers.rest('https://api.example.com') // the URL to your feathers API server
app.configure(rc.axios(axios))

export const api = token => {
  // throw an error if no token was passed
  if (!token) throw 'Token cannot be empty!'

  // add the token to the Authorization header
  const params = {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }

  // now just pass these params with every call to the API
  return {
    getTodos: async query => {
      if (query) params.query = query
      return app.service('todos').find(params)
    }
  }
}

And then in the view or component that consumes the api:

// src/views/ToDoList.js
import { api } from '../services/feathers'

// assuming you've already authenticated with Auth0 and stored your
// token localy, e.g. in window.sessionStorage
const token = sessionStorage.getItem('auth0_access_token')
const { getTodos } = api(token)

// get all "to do" items due in the future
const query = {
  dueDate: {
    $gt: new Date().getTime()
  }
}
getTodos(query)
  .then(items => {
    // do something with the retrieved items
  })
  .catch(err => {
    // handle any errors, including failure to authorize
  })

Custom Configuration

Remember the "extra" parentheses we had to use when importing the hook into app.hooks.js above?

// from src/app.hooks.js in our feathers app
const { authorize } = require('@morphatic/feathers-auth0-authorize-hook')() // <-- NOTE: the "extra" parentheses

They're necessary because the function that returns the hook uses the factory pattern and allows for customization. While you can swap out any of the functions in this plugin for your own implementation, the main things you're likely to want to customize are the services used to hold the user_id and keys.

Using a different users service

By default, the hook queries the users service to find a user whose user_id matches the Auth0 user_id stored in the sub claim in the token. However, you might want to store this information on a different model in a different service. To do that, you'd provide an options object as a parameter to the require()() function and set the userService property to the name of the service you want to use. For example, pretend that instead of users, you want to look for the user_id property on the members service instead:

// example src/app.hooks.js with custom `users` service
// first, create an `options` object with the preferred name for your service
const options = {
  userService: 'members'
}
const { authorize } = require('@morphatic/feathers-auth0-authorize-hook')(options) // <-- second, pass it to the import statement

That's it!

Using a different keys service

Similarly, you can use a different service name for where you store and look for the JWKs. Here's an example showing customization of BOTH the users and keys services:

// example src/app.hooks.js with custom `users` service
// first, create an `options` object with the preferred names for your services
const options = {
  userService: 'members',
  keysService: 'jwks'
}
const { authorize } = require('@morphatic/feathers-auth0-authorize-hook')(options) // <-- second, pass it to the import statement

Comments, Questions, Issues, etc

I welcome feedback, bug reports, enhancement requests, or reports on your experiences with the plugin. Undoubtedly, there's a better way to do this than what I've come up with, and I'd love to hear about it. That being said, I hope some people will find this useful!!!