Skip to content

Commit

Permalink
Add functionality to allow directly provided jwt keysets (#191)
Browse files Browse the repository at this point in the history
Adds ability to provide jwks directly

Co-authored-by: David <david.patrick@auth0.com>
  • Loading branch information
thepieterdc and davidpatrick committed Nov 18, 2020
1 parent c5b58c5 commit 6cfa98f
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 55 deletions.
70 changes: 35 additions & 35 deletions examples/koa-demo/server.js
@@ -1,35 +1,35 @@
const Koa = require('koa');
const Router = require('koa-router');
const jwt = require('koa-jwt');
const jwksRsa = require('../../lib');

const jwksHost = process.env.JWKS_HOST;
const audience = process.env.AUDIENCE;
const issuer = process.env.ISSUER;

// Initialize the app.
const app = new Koa();

app.use(jwt({
secret: jwksRsa.koaJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 2,
jwksUri: `${jwksHost}/.well-known/jwks.json`
}),
audience,
issuer,
algorithms: [ 'RS256' ]
}));

const router = new Router();

router.get('/me', ctx => {
ctx.body = ctx.state.user
});

app.use(router.middleware());

// Start the server.
const port = process.env.PORT || 4001;
app.listen(port);
const Koa = require('koa');
const Router = require('koa-router');
const jwt = require('koa-jwt');
const jwksRsa = require('../../lib');

const jwksHost = process.env.JWKS_HOST;
const audience = process.env.AUDIENCE;
const issuer = process.env.ISSUER;

// Initialize the app.
const app = new Koa();

app.use(jwt({
secret: jwksRsa.koaJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 2,
jwksUri: `${jwksHost}/.well-known/jwks.json`
}),
audience,
issuer,
algorithms: [ 'RS256' ]
}));

const router = new Router();

router.get('/me', ctx => {
ctx.body = ctx.state.user;
});

app.use(router.middleware());

// Start the server.
const port = process.env.PORT || 4001;
app.listen(port);
6 changes: 5 additions & 1 deletion index.d.ts
Expand Up @@ -4,7 +4,7 @@ declare function JwksRsa(options: JwksRsa.ClientOptions): JwksRsa.JwksClient;

declare namespace JwksRsa {
class JwksClient {
constructor(options: ClientOptions);
constructor(options: ClientOptions | ClientOptionsWithObject);

getKeys(cb: (err: Error | null, keys: unknown) => void): void;
getKeysAsync(): Promise<unknown>;
Expand All @@ -31,6 +31,10 @@ declare namespace JwksRsa {
timeout?: number;
}

interface ClientOptionsWithObject extends Omit<ClientOptions, 'jwksUri'> {
jwksObject: { keys: SigningKey[] };
}

interface CertSigningKey {
kid: string;
nbf: string;
Expand Down
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/JwksClient.js
Expand Up @@ -36,6 +36,11 @@ export class JwksClient {
}

getKeys(cb) {
if (this.options.jwksObject) {
this.logger('Returning directly provided keyset.');
return cb(null, this.options.jwksObject.keys);
}

this.logger(`Fetching keys from '${this.options.jwksUri}'`);
request({
uri: this.options.jwksUri,
Expand Down
4 changes: 2 additions & 2 deletions src/integrations/koa.js
Expand Up @@ -3,8 +3,8 @@ import { JwksClient } from '../JwksClient';

module.exports.koaJwtSecret = (options = {}) => {

if (!options.jwksUri) {
throw new ArgumentError('No JWKS URI provided');
if (!options.jwksUri && !options.jwksObject) {
throw new ArgumentError('No JWKS provided. Please provide a jwksUri or jwksObject');
}

const client = new JwksClient(options);
Expand Down
4 changes: 4 additions & 0 deletions src/integrations/passport.js
Expand Up @@ -19,6 +19,10 @@ module.exports.passportJwtSecret = (options) => {
throw new ArgumentError('An options object must be provided when initializing passportJwtSecret');
}

if (!options.jwksUri && !options.jwksObject) {
throw new ArgumentError('No JWKS provided. Please provide a jwksUri or jwksObject');
}

const client = new JwksClient(options);
const onError = options.handleSigningKeyError || handleSigningKeyError;

Expand Down
13 changes: 13 additions & 0 deletions tests/jwksClient.tests.js
Expand Up @@ -5,6 +5,7 @@ import { expect } from 'chai';

import { x5cMultiple } from './keys';
import { JwksClient } from '../src/JwksClient';
import jwksObject from './mocks/jwks.json';

describe('JwksClient', () => {
const jwksHost = 'http://my-authz-server';
Expand Down Expand Up @@ -65,6 +66,18 @@ describe('JwksClient', () => {
});
});

it('should return keys that are directly provided', done => {
const client = new JwksClient({ jwksObject });

client.getKeys((err, respKeys) => {
expect(err).to.be.null;
expect(respKeys).not.to.be.null;
expect(respKeys.length).to.equal(jwksObject.keys.length);
expect(respKeys[1].kid).to.equal(jwksObject.keys[1].kid);
done();
});
});

it('should set request agentOptions when provided', done => {
nock(jwksHost)
.get('./well-known/jwks.json')
Expand Down
2 changes: 1 addition & 1 deletion tests/koa.tests.js
Expand Up @@ -11,7 +11,7 @@ const koaJwt = require('koa-jwt');
const jwksRsa = require('../src');

describe('koaJwtSecret', () => {
it('should throw error if options.jwksUri is null', () => {
it('should throw error if options.jwksUri and options.jwksObject is null', () => {
let err = null;

try {
Expand Down
1 change: 1 addition & 0 deletions tests/mocks/jwks.json
@@ -0,0 +1 @@
{"keys": [{"alg":"RS256","kty":"RSA","use":"sig","x5c":["pk1"],"kid":"ABC"},{"alg":"RS256","kty":"RSA","use":"sig","x5c":[],"kid":"123"}]}
14 changes: 13 additions & 1 deletion tests/passport.tests.js
Expand Up @@ -13,7 +13,7 @@ const ExtractJwt = require('passport-jwt').ExtractJwt;
const jwksRsa = require('../src');

describe('passportJwtSecret', () => {
it('should throw error if options.jwksUri is null', () => {
it('should throw error if options is null', () => {
let err = null;

try {
Expand All @@ -25,6 +25,18 @@ describe('passportJwtSecret', () => {
expect(err instanceof jwksRsa.ArgumentError).to.be.true;
});

it('should throw error if options.jwksUri and options.jwksObject is null', () => {
let err = null;

try {
new jwksRsa.passportJwtSecret({});
} catch (e) {
err = e;
}

expect(err instanceof jwksRsa.ArgumentError).to.be.true;
});

it('should accept the secret function', () => {
new JwtStrategy(
{
Expand Down

0 comments on commit 6cfa98f

Please sign in to comment.