A simple JWT implementation that you can add to your Meteor applications in order to share data without the need of a shared database or shared authentication system (such as OAuth providers).
It currently uses node jwt (njwt) but can be easily replaced with any other JWT library.
First install the package on both applications via
$ meteor add leaonline:jwt
Make sure you understand consumer and provider applications. The consumer is the application that requests data from the provider application. The provider is the application that sends data to the consumer application. Consumer has to be able to connect to the provider application via DDP.
In theory, both apps can be consumer and provider at the same time, so you can share data in both directions.
On the consumer side you want to create a JWT factory.
Before you can do that, you need to add your jwt credentials to the Meteor settings (settings.json
)
of applications:
{
"jwt": {
"key": "your-secret-key",
"sub": "a-subject-or-username-or-id",
"expires": 30000
}
}
Now you can create a token factory:
import { Meteor } from 'meteor/meteor'
import { createJWTFactory } from 'meteor/leaonline:jwt'
const { jwt } = Meteor.settings
const tokenFactory = createJWTFactory({
url: Meteor.absoluteUrl(), // you can also define a custom URL here
key: jwt.key,
sub: jwt.sub,
expires: jw.expires, // optional, defaults to 60000 ms
debug: console.debug // optional
})
Now you can generate new JWTS for different methods or publications (or http) endpoints.
Let's say you want to consume a method, named getUserData
from the provider app,
then you can create a JWT like this:
const token = tokenFactory({ name: 'getUserData' })
const remote = DDP.connect('https://provider-app.com')
// ... on successfully connected
remote.call('getUserData', { token }, (error, result) => {
// ... process response
})
On the provider side you want to create a JWT validator so you can validate, if the consumer is allowed to call your methods or publications.
First, you need to add the same jwt credentials to the Meteor settings (settings.json
)
and define allowed / blocked consumers, using a positive list and/or negative list:
{
"jwt": {
"key": "your-secret-key",
"hosts": [
{
"url": "https://consumer-app.com",
"sub": "a-subject-or-username-or-id"
}
]
}
}
import { Meteor } from 'meteor/meteor'
import { createJWTValidator } from 'meteor/leaonline:jwt'
const { jwt } = Meteor.settings
const validator = createJWTValidator({
key: jwt.key,
positives: jwt.hosts, // optional, defaults to [],
negatives: [], // optional, defaults to []
debug: console.debug // optional
})
Meteor.methods({
getUserData({ token }) {
const { valid, reason } = validator(token, 'getUserData')
if (!valid) {
throw new Meteor.Error('unauthorized', `JWT validation failed: ${reason}`)
}
// ... process the request, e.g. return user data
return { userId: '12345', name: 'John Doe' }
}
})
You can also use a mixin for validated methods that automatically validates the JWT and removes it when passing:
import { Meteor } from 'meteor/meteor'
import { createJWTValidator } from 'meteor/leaonline:jwt'
const { jwt } = Meteor.settings
const validator = createJWTValidator({
key: jwt.key,
positives: jwt.hosts, // optional, defaults to [],
negatives: [], // optional, defaults to []
debug: console.debug // optional
})
export const validteJWTMixin = options => {
const { name, validate } = options
options.validate = args => {
const { token, ...data } = args
const { valid, reason } = validator(token, name)
if (!valid) {
throw new Meteor.Error('unauthorized', `JWT validation failed: ${reason}`)
}
// if we remove token then we don't need
// to consider it in the method validations
delete data.token
// run the original validation
validate(data)
}
return options
}
Make sure to run lint
, format
and test
before committing your changes.
Please use conventional commit messages, so we can generate a changelog automatically.
MIT, see LICENSE