mirror of
https://github.com/hanzoai/node-jwks-rsa.git
synced 2026-09-24 17:00:54 +00:00
Compiling is no longer needed. Node 10+ supports all the features used in this library.
381 lines
10 KiB
JavaScript
381 lines
10 KiB
JavaScript
const request = require('supertest');
|
|
const { expect } = require('chai');
|
|
|
|
const { jwksEndpoint } = require('./mocks/jwks');
|
|
const { publicKey, privateKey, randomPublicKey1 } = require('./mocks/keys');
|
|
const { createToken, createSymmetricToken } = require('./mocks/tokens');
|
|
|
|
const Express = require('express');
|
|
const passport = require('passport');
|
|
const JwtStrategy = require('passport-jwt').Strategy;
|
|
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
|
|
|
const jwksRsa = require('../src');
|
|
|
|
describe('passportJwtSecret', () => {
|
|
it('should throw error if options is null', () => {
|
|
let err = null;
|
|
|
|
try {
|
|
new jwksRsa.passportJwtSecret();
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
|
|
expect(err instanceof jwksRsa.ArgumentError).to.be.true;
|
|
});
|
|
|
|
it('should throw error if options.jwksUri 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(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
);
|
|
});
|
|
|
|
it('should not provide a key if token is invalid', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
)
|
|
);
|
|
|
|
let expectedFlashMessage;
|
|
app.get(
|
|
'/',
|
|
(req, res, next) => {
|
|
req.flash = (type, msg) => {
|
|
expectedFlashMessage = msg;
|
|
};
|
|
next();
|
|
},
|
|
passport.authenticate('jwt', { session: false, failureFlash: true }),
|
|
(req, res) => {
|
|
res.send('OK');
|
|
}
|
|
);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', 'Bearer abc')
|
|
.expect(401)
|
|
.end(() => {
|
|
expect(expectedFlashMessage).to.equal('jwt malformed');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should not provide a key if token is HS256', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
)
|
|
);
|
|
|
|
let expectedFlashMessage;
|
|
app.get(
|
|
'/',
|
|
(req, res, next) => {
|
|
req.flash = (type, msg) => {
|
|
expectedFlashMessage = msg;
|
|
};
|
|
next();
|
|
},
|
|
passport.authenticate('jwt', { session: false, failureFlash: true }),
|
|
(req, res) => {
|
|
res.send('OK');
|
|
}
|
|
);
|
|
|
|
const token = createSymmetricToken('mykey', { sub: 'john' });
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(401)
|
|
.end(() => {
|
|
expect(expectedFlashMessage).to.equal('secret or public key must be provided');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should not provide a key if JWKS endpoint returned multiple keys and no KID was provided', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
)
|
|
);
|
|
|
|
let expectedFlashMessage;
|
|
app.get(
|
|
'/',
|
|
(req, res, next) => {
|
|
req.flash = (type, msg) => {
|
|
expectedFlashMessage = msg;
|
|
},
|
|
next();
|
|
},
|
|
passport.authenticate('jwt', { session: false, failureFlash: true }),
|
|
(req, res) => {
|
|
res.send('OK');
|
|
}
|
|
);
|
|
|
|
const token = createToken(privateKey, null, { sub: 'john' });
|
|
jwksEndpoint('http://localhost', [ { pub: publicKey, kid: '123' }, { pub: publicKey, kid: '456' } ]);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(401)
|
|
.end((err) => {
|
|
expect(expectedFlashMessage).to.equal('secret or public key must be provided');
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
it('should not provide a key if token is RS256 and invalid KID was provided', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => {
|
|
done(null, jwt_payload);
|
|
}
|
|
)
|
|
);
|
|
|
|
let expectedFlashMessage;
|
|
app.get(
|
|
'/',
|
|
(req, res, next) => {
|
|
req.flash = (type, msg) => {
|
|
expectedFlashMessage = msg;
|
|
};
|
|
next();
|
|
},
|
|
passport.authenticate('jwt', { session: false, failureFlash: true }),
|
|
(req, res) => {
|
|
res.send('OK');
|
|
}
|
|
);
|
|
|
|
const token = createToken(privateKey, '456', { sub: 'john' });
|
|
jwksEndpoint('http://localhost', [ { pub: publicKey, kid: '123' } ]);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(401)
|
|
.end(() => {
|
|
expect(expectedFlashMessage).to.equal('secret or public key must be provided');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should not authenticate the user if KID matches but the keys don\'t', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => {
|
|
done(null, jwt_payload);
|
|
}
|
|
)
|
|
);
|
|
|
|
let expectedFlashMessage;
|
|
app.get(
|
|
'/',
|
|
(req, res, next) => {
|
|
req.flash = (type, msg) => {
|
|
expectedFlashMessage = msg;
|
|
};
|
|
next();
|
|
},
|
|
passport.authenticate('jwt', { session: false, failureFlash: true }),
|
|
(req, res) => {
|
|
res.send('OK');
|
|
}
|
|
);
|
|
|
|
const token = createToken(privateKey, '123', { sub: 'john' });
|
|
jwksEndpoint('http://localhost', [ { pub: randomPublicKey1, kid: '123' } ]);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(401)
|
|
.end(() => {
|
|
expect(expectedFlashMessage).to.equal('invalid signature');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should allow returning an error if key not found', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json',
|
|
handleSigningKeyError: (err, cb) => {
|
|
if (err instanceof jwksRsa.SigningKeyNotFoundError) {
|
|
return cb(new Error('this is bad'));
|
|
}
|
|
}
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
)
|
|
);
|
|
|
|
let expectedFlashMessage;
|
|
app.get(
|
|
'/',
|
|
(req, res, next) => {
|
|
req.flash = (type, msg) => {
|
|
expectedFlashMessage = msg;
|
|
};
|
|
next();
|
|
},
|
|
passport.authenticate('jwt', { session: false, failureFlash: true }),
|
|
(req, res) => {
|
|
res.send('OK');
|
|
}
|
|
);
|
|
|
|
const token = createToken(privateKey, '456', { sub: 'john' });
|
|
jwksEndpoint('http://localhost', [ { pub: randomPublicKey1, kid: '123' } ]);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(401)
|
|
.end(() => {
|
|
expect(expectedFlashMessage).to.equal('this is bad');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should work if the token matches a signing key', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
algorithms: [ 'RS256' ]
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
)
|
|
);
|
|
|
|
app.get(
|
|
'/',
|
|
passport.authenticate('jwt', { session: false }),
|
|
(req, res) => {
|
|
res.json(req.user);
|
|
}
|
|
);
|
|
|
|
const token = createToken(privateKey, '123', { sub: 'john' });
|
|
jwksEndpoint('http://localhost', [ { pub: publicKey, kid: '123' } ]);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(200)
|
|
.end((err, res) => {
|
|
expect(res.body.sub).to.equal('john');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should work if the JWKS endpoint returns a single key and no KID is provided', done => {
|
|
const app = new Express();
|
|
passport.use(
|
|
new JwtStrategy(
|
|
{
|
|
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
|
jwksUri: 'http://localhost/.well-known/jwks.json'
|
|
}),
|
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
algorithms: [ 'RS256' ]
|
|
},
|
|
(jwt_payload, done) => done(null, jwt_payload)
|
|
)
|
|
);
|
|
|
|
app.get(
|
|
'/',
|
|
passport.authenticate('jwt', { session: false }),
|
|
(req, res) => {
|
|
res.json(req.user);
|
|
}
|
|
);
|
|
|
|
const token = createToken(privateKey, null, { sub: 'john' });
|
|
jwksEndpoint('http://localhost', [ { pub: publicKey } ]);
|
|
|
|
request(app.listen())
|
|
.get('/')
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.expect(200)
|
|
.end((err, res) => {
|
|
expect(res.body.sub).to.equal('john');
|
|
done();
|
|
});
|
|
});
|
|
});
|