乐闻世界logo
搜索文章和话题

How to test JWT

2月21日 17:52

JWT testing is crucial for ensuring the security and reliability of authentication systems. Here are complete testing strategies and implementation methods:

1. Unit Testing

Test Token Generation

javascript
const jwt = require('jsonwebtoken'); const { expect } = require('chai'); describe('JWT Token Generation', () => { const SECRET_KEY = 'test-secret-key'; const payload = { userId: '123', username: 'testuser' }; it('should generate a valid token', () => { const token = jwt.sign(payload, SECRET_KEY); expect(token).to.be.a('string'); expect(token.split('.')).to.have.lengthOf(3); }); it('should generate token with correct payload', () => { const token = jwt.sign(payload, SECRET_KEY); const decoded = jwt.decode(token); expect(decoded.userId).to.equal('123'); expect(decoded.username).to.equal('testuser'); }); it('should generate token with expiration time', () => { const token = jwt.sign(payload, SECRET_KEY, { expiresIn: '1h' }); const decoded = jwt.decode(token); expect(decoded.exp).to.be.a('number'); expect(decoded.exp).to.be.greaterThan(Math.floor(Date.now() / 1000)); }); it('should generate token with custom claims', () => { const customPayload = { ...payload, iss: 'test-issuer', aud: 'test-audience' }; const token = jwt.sign(customPayload, SECRET_KEY); const decoded = jwt.decode(token); expect(decoded.iss).to.equal('test-issuer'); expect(decoded.aud).to.equal('test-audience'); }); });

Test Token Verification

javascript
describe('JWT Token Verification', () => { const SECRET_KEY = 'test-secret-key'; const payload = { userId: '123', username: 'testuser' }; it('should verify a valid token', () => { const token = jwt.sign(payload, SECRET_KEY); const decoded = jwt.verify(token, SECRET_KEY); expect(decoded.userId).to.equal('123'); expect(decoded.username).to.equal('testuser'); }); it('should throw error for invalid token', () => { const invalidToken = 'invalid.token.here'; expect(() => { jwt.verify(invalidToken, SECRET_KEY); }).to.throw('jwt malformed'); }); it('should throw error for expired token', () => { const expiredToken = jwt.sign(payload, SECRET_KEY, { expiresIn: '-1s' }); expect(() => { jwt.verify(expiredToken, SECRET_KEY); }).to.throw('jwt expired'); }); it('should throw error for wrong secret', () => { const token = jwt.sign(payload, SECRET_KEY); const wrongSecret = 'wrong-secret'; expect(() => { jwt.verify(token, wrongSecret); }).to.throw('invalid signature'); }); it('should verify token with algorithm check', () => { const token = jwt.sign(payload, SECRET_KEY, { algorithm: 'HS256' }); const decoded = jwt.verify(token, SECRET_KEY, { algorithms: ['HS256'] }); expect(decoded.userId).to.equal('123'); }); it('should reject token with wrong algorithm', () => { const token = jwt.sign(payload, SECRET_KEY, { algorithm: 'HS256' }); expect(() => { jwt.verify(token, SECRET_KEY, { algorithms: ['RS256'] }); }).to.throw('invalid algorithm'); }); });

2. Integration Testing

Test Authentication Flow

javascript
const request = require('supertest'); const app = require('../app'); describe('Authentication Flow Integration Tests', () => { let authToken; it('should login and receive token', async () => { const response = await request(app) .post('/auth/login') .send({ username: 'testuser', password: 'password123' }) .expect(200); expect(response.body).to.have.property('token'); expect(response.body).to.have.property('expiresIn'); authToken = response.body.token; }); it('should access protected route with valid token', async () => { const response = await request(app) .get('/api/protected') .set('Authorization', `Bearer ${authToken}`) .expect(200); expect(response.body).to.have.property('data'); }); it('should reject request without token', async () => { await request(app) .get('/api/protected') .expect(401); }); it('should reject request with invalid token', async () => { await request(app) .get('/api/protected') .set('Authorization', 'Bearer invalid-token') .expect(401); }); it('should reject request with expired token', async () => { const expiredToken = jwt.sign( { userId: '123' }, process.env.JWT_SECRET, { expiresIn: '-1s' } ); await request(app) .get('/api/protected') .set('Authorization', `Bearer ${expiredToken}`) .expect(401); }); });

Test Token Refresh

javascript
describe('Token Refresh Integration Tests', () => { let accessToken; let refreshToken; it('should login and receive both tokens', async () => { const response = await request(app) .post('/auth/login') .send({ username: 'testuser', password: 'password123' }) .expect(200); accessToken = response.body.accessToken; refreshToken = response.body.refreshToken; expect(accessToken).to.exist; expect(refreshToken).to.exist; }); it('should refresh access token', async () => { const response = await request(app) .post('/auth/refresh') .send({ refreshToken }) .expect(200); expect(response.body).to.have.property('accessToken'); expect(response.body.accessToken).to.not.equal(accessToken); accessToken = response.body.accessToken; }); it('should access protected route with new token', async () => { await request(app) .get('/api/protected') .set('Authorization', `Bearer ${accessToken}`) .expect(200); }); it('should reject refresh with invalid token', async () => { await request(app) .post('/auth/refresh') .send({ refreshToken: 'invalid-refresh-token' }) .expect(401); }); });

3. Security Testing

Test Algorithm Confusion Attack

javascript
describe('Security Tests - Algorithm Confusion', () => { it('should reject tokens with none algorithm', () => { const maliciousToken = jwt.sign( { userId: 'admin' }, '', { algorithm: 'none' } ); expect(() => { jwt.verify(maliciousToken, SECRET_KEY, { algorithms: ['HS256'] }); }).to.throw(); }); it('should reject tokens with unspecified algorithm', () => { const token = jwt.sign({ userId: '123' }, SECRET_KEY); // Try verifying with different algorithm expect(() => { jwt.verify(token, SECRET_KEY, { algorithms: ['none'] }); }).to.throw('invalid algorithm'); }); });

Test Token Tampering

javascript
describe('Security Tests - Token Tampering', () => { it('should reject tampered token', () => { const token = jwt.sign({ userId: '123' }, SECRET_KEY); // Tamper with token const parts = token.split('.'); parts[1] = Buffer.from(JSON.stringify({ userId: 'admin' })).toString('base64'); const tamperedToken = parts.join('.'); expect(() => { jwt.verify(tamperedToken, SECRET_KEY); }).to.throw('invalid signature'); }); });

4. Performance Testing

Test Token Generation Performance

javascript
const Benchmark = require('benchmark'); const suite = new Benchmark.Suite(); describe('Performance Tests', () => { it('should benchmark token generation', function(done) { this.timeout(10000); suite .add('HS256', () => { jwt.sign({ userId: '123' }, SECRET_KEY, { algorithm: 'HS256' }); }) .add('RS256', () => { jwt.sign({ userId: '123' }, privateKey, { algorithm: 'RS256' }); }) .add('ES256', () => { jwt.sign({ userId: '123' }, privateKey, { algorithm: 'ES256' }); }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')); done(); }) .run({ async: true }); }); it('should handle concurrent token generation', async () => { const concurrency = 1000; const promises = []; for (let i = 0; i < concurrency; i++) { promises.push( new Promise((resolve) => { const start = Date.now(); jwt.sign({ userId: i.toString() }, SECRET_KEY); resolve(Date.now() - start); }) ); } const times = await Promise.all(promises); const avgTime = times.reduce((a, b) => a + b, 0) / times.length; expect(avgTime).to.be.below(10); // Average time should be less than 10ms }); });

5. Mock and Stub

Using Mocks for Testing

javascript
const sinon = require('sinon'); describe('Authentication with Mocks', () => { let verifyStub; beforeEach(() => { verifyStub = sinon.stub(jwt, 'verify'); }); afterEach(() => { verifyStub.restore(); }); it('should handle valid token with mock', async () => { verifyStub.returns({ userId: '123', username: 'testuser' }); const response = await request(app) .get('/api/protected') .set('Authorization', 'Bearer mock-token') .expect(200); expect(verifyStub.calledOnce).to.be.true; }); it('should handle invalid token with mock', async () => { verifyStub.throws(new Error('Invalid token')); const response = await request(app) .get('/api/protected') .set('Authorization', 'Bearer invalid-token') .expect(401); expect(verifyStub.calledOnce).to.be.true; }); });

6. Test Coverage

Using Istanbul/nyc

javascript
// package.json { "scripts": { "test": "jest", "test:coverage": "jest --coverage", "test:watch": "jest --watch" }, "jest": { "collectCoverageFrom": [ "src/**/*.js", "!src/**/*.test.js" ], "coverageThreshold": { "global": { "branches": 80, "functions": 80, "lines": 80, "statements": 80 } } } }

7. End-to-End Testing

Using Cypress

javascript
// cypress/integration/auth.spec.js describe('Authentication E2E Tests', () => { it('should login successfully', () => { cy.visit('/login'); cy.get('input[name="username"]').type('testuser'); cy.get('input[name="password"]').type('password123'); cy.get('button[type="submit"]').click(); cy.url().should('include', '/dashboard'); cy.get('[data-testid="user-info"]').should('contain', 'testuser'); }); it('should redirect to login on token expiry', () => { cy.window().then((win) => { // Simulate token expiration win.localStorage.setItem('token', 'expired-token'); }); cy.visit('/dashboard'); cy.url().should('include', '/login'); }); it('should refresh token automatically', () => { cy.login('testuser', 'password123'); // Wait for token refresh cy.wait(15000); // Verify can still access protected routes cy.visit('/dashboard'); cy.url().should('include', '/dashboard'); }); });

8. Testing Best Practices

Testing Checklist

  • Test token generation
  • Test token verification
  • Test expiration handling
  • Test invalid tokens
  • Test security vulnerabilities
  • Test performance
  • Test concurrency
  • Test edge cases
  • Test error handling
  • Achieve test coverage targets
  • Unit Testing: Jest, Mocha, Chai
  • Integration Testing: Supertest
  • End-to-End Testing: Cypress, Playwright
  • Performance Testing: Benchmark.js, Artillery
  • Security Testing: OWASP ZAP, Burp Suite
  • Coverage: Istanbul/nyc, Jest Coverage

With comprehensive testing strategies, you can ensure the security, reliability, and performance of JWT authentication systems.

标签:JWT