future-micropub-endpoint icon indicating copy to clipboard operation
future-micropub-endpoint copied to clipboard

Package proposal: IndieAuth token verification

Open paulrobertlloyd opened this issue 6 years ago • 9 comments

In the spirit of creating small un-opinionated packages that do one thing well (rather than attempt to build a single monolith package), I wonder if one of the first candidates for broader collaboration between node-based projects would be one that verifies a token and returns an IndieAuth response?

Prior art

IndieKit

Taking inspiration from the work @grantcodes has been doing with Postr, I recently updated IndieKit so that certain parts are now implemented as Express middleware – discovering this pattern was a revelation, massively simplifying my code!

The code for this can be found here, and is currently set up as follows:

// Require package
const auth = require(process.env.PWD + '/app/lib/auth');

// Configure token endpoint and publication URL
const indieauth = auth.indieauth({
  'token-endpoint': config.indieauth['token-endpoint'],
  url: config.url
});

// Use in route
router.post('/micropub',
  indieauth,
  … // Other middleware
);

(config.indieauth['token-endpoint'] currently points to a variable that defaults to https://tokens.indieauth.com/token, but the default should probably exist in the middleware.)

Postr

@grantcodes has also implemented his IndieAuth token verification as Express middleware, which can be found here. It is set up as follows:

// Require package
const authMiddlewear = require('./middlewear/auth')

// Use in route
router.get('/',
  authMiddlewear
  … // Other middleware
);

authMiddlewear makes calls to other parts of the app (as mine did until yesterday!), but it does appear to cover off more possible exceptions than IndieKit.

Micropub Express

@voxpelli’s package does a little more than verify a token, but the portion of code that deals with validating a token can be found here. Like Postr, it also appears to account for a good number of exceptions that may occur when validating a token.

Common features

  • Built as express middleware
  • Test for a number of exceptions (might be worth listing these out explicitly somewhere)
  • Allow for configuration of endpoint and destination site (me)

Final thoughts

I’m sure there are other common IndieAuth authentication activities that could be moved into a common package, but verifying a token seems like well understood and tightly scoped problem.

@indieweb/express-indieauth-token anyone?

paulrobertlloyd avatar Jul 26 '19 20:07 paulrobertlloyd

Reading this I initially thought similarly to my comment #6 - in that it doesn't really need to be it's own package as the only use case is within a micropub endpoint.

But now, to be honest, I have got myself confused. I think this one may be useful as a separate package only because something like microsub exists now and needs to do a similar thing with tokens.

Even if it doesn't deserve to be its own module, it certainly should be a separate middleware within a micropub endpoint, similar to what I suggested in #6

grantcodes avatar Jul 26 '19 22:07 grantcodes

This would be a good addition, also such a small component that I can partly step back on what I said in #1. Such small focused modules like this could make sense to collaborate on. They are such small, simple and focused modules that there's no harm in doing so.

Does it have to be express centric? Or is there any use in providing both a generic method and an express middleware wrapping that method?

voxpelli avatar Jul 28 '19 17:07 voxpelli

Does it have to be express centric? Or is there any use in providing both a generic method and an express middleware wrapping that method?

Are Node-based endpoints likely to be written in anything else? There’s Connect… not sure what the relation of that project is to Express, but I think there’s some relationship/overlap.

I think an express-indieauth-token would be a good place to start; should others wish to adapt it to another server framework, they’d be free to do so.

paulrobertlloyd avatar Jul 28 '19 23:07 paulrobertlloyd

Using any node.js-based serverless solutions likely wouldn’t use Express. So a static site using Netlify with serverless functions or using something like the serverless npm framework.

Because if that being something I’ve looked a lot into, I would find it beneficial to build functions with as much straight javascript as possible, adding Express only when needed to expose the JavaScript functions to the express framework but not having the actual code rely on Express for anything.

EdwardHinkle avatar Jul 29 '19 03:07 EdwardHinkle

@EdwardHinkle Ah, good point. A long term ambition, once I’ve built something that works as deployed app, is to learn about serverless functions and see what parts of my code would be portable.

How you might structure this package without explicit support for Express… have it accept an access token (and a set of options), and return an object containing either the verified token, or error information and status code? (That’s how I had IndieKit setup until I refactored this component as a middleware).

paulrobertlloyd avatar Jul 29 '19 07:07 paulrobertlloyd

Yeah, I think that would be the structure @paulrobertlloyd – then wrapping it up as a middleware would be very simple (and such a middleware could be provided as part of the package, even if some would not agree that it should)

voxpelli avatar Jul 29 '19 07:07 voxpelli

Good additions from everyone!

I do think that express is still pretty common within serverless functions, but definitely not always necessary. And the beauty of express middleware is that they are just functions that accept the request and response objects, and I think those are either just base node functionality or well enough established that they work across most http servers.

Luckily resusability in node is very easy, so there are lots of options. For this specific module my suggestions would be:

  1. Have a function that accepts the token and endpoint, and returns a response (as @paulrobertlloyd suggested)
  2. Create an express middleware that uses that function.

What is potentially has more opinions is how you export those two parts, and I think that would be something useful to have defined and reused across any other modules. Off the top of my head I can think of at least 3 ways to do it. In these examples I've called the 2 functions middleware and rawFunction

  1. Export as an object
// Export
module.exports = { middleware, rawFunction }
// Import
const { middleware, rawFunction } = require('npm-module')
  1. Export only the main item, and the other is usable at a separate path.
// Export
module.exports = middleware
// Import
const middleware = require('npm-module')
const rawFunction= require('npm-module/raw-function')
  1. Export everything wrapped in a function.
// Export
function mainExport() {
  return middleware
}
mainExport.rawFunction = rawFunction
module.exports = mainExport
// Import
const importedModule = require('npm-module')
const middleware = importedModule()
const rawFunction = importedModule.rawFunction()

My opinions:

  • Basically option number 2 is my favored method, it is the most natural method in node and a even a lot of es6 modules follow that methodology too. It also means people can use only the part they want without importing everything else (even though that is not a huge deal in node compared to client side)
  • I think the main export should be the highest level export, and should make sense with the module name. E.g. In this case if we export a express middleware (which I think we should) that should be the main export and the npm module should be called express-indieauth-token or indieauth-token-middleware or something along those lines.
  • I have seen other express middleware (helmet) using method 3, and I think it is a good format if you want to export a bunch of different middlewares from one module.
  • Method 2 could also be combined with other methods as well.

grantcodes avatar Jul 29 '19 09:07 grantcodes

I would go with number 1, feels simplest to me

voxpelli avatar Jul 29 '19 10:07 voxpelli

I would lean towards number 1 as well. It feels the most common in the library circles I’ve come across.

I will say, I don’t feel strongly against number 2, so I would be okay with that.

Number 3... I’m sure libraries do that? But I’ve never really seen it and would be strongly opposed to that version.

EdwardHinkle avatar Jul 30 '19 03:07 EdwardHinkle