Skip to content

Commit

Permalink
Merge branch 'release/2.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
felixpy committed Dec 19, 2018
2 parents e5b2bcf + 9dd3434 commit c84e09f
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 19 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,34 @@ exports.middleware = ['token'];

exports.token = {
type: 'md5',
secret: 'your-magic-secret-key'

apps: {
felixpy: {
secret: 'XnMib79vzwP01gtr',
expires: 30000
},
codetrial: {
secret: 'mi9yNGT6zwrqMv8z',
expires: 30000
}
}
};
```

`type` is the algorithm that can be used to generate hash digests.

See [crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options) for more detail.

Each key of `apps` is the application's code, `secret` is used to generate token and `expires` is the validity period of the token.

The way to generate tokens is as follows:

```js
const ts = Date.now();
const md5Value = md5(`${APP_CODE}:${ts}:${APP_SECRET}`);
const token = base64Encode(`${APP_CODE}:${ts}:${md5Value}`);
```

## Example

This is an example of using axios to request an api:
Expand All @@ -52,9 +72,9 @@ const axios = require('axios');
const hash = crypto.createHash('md5');

const APP_CODE = 'felixpy';
const APP_SECRET = 'your-magic-secret-key';
const APP_SECRET = 'XnMib79vzwP01gtr';

const ts = new Date().getTime();
const ts = Date.now();
const md5Value = hash.update(`${APP_CODE}:${ts}:${APP_SECRET}`).digest('hex');

const token = Buffer.from(`${APP_CODE}:${ts}:${md5Value}`).toString('base64');
Expand Down
18 changes: 14 additions & 4 deletions app/middleware/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

const crypto = require('crypto');

function checkToken(type, secret, token) {
const [ code, ts, auth ] = token.split(':');
function checkToken(type, { code, ts, auth }, { secret, expires }) {
const now = Date.now();

if (now - ts > expires) {
return false;
}

const hash = crypto.createHash(type);

hash.update(`${code}:${ts}:${secret}`);
Expand All @@ -12,19 +17,24 @@ function checkToken(type, secret, token) {
}

module.exports = (options, app) => {
const { type, secret } = options;
const { type, apps } = options;

return async function search(ctx, next) {
const base64Token = ctx.get('egg-api-token') || '';
const token = Buffer.from(base64Token, 'base64').toString();
const [ code, ts, auth ] = token.split(':');

app.loggers.coreLogger.info(
'[egg-token] checking api token %s -> %s',
base64Token,
token
);

if (!token || !checkToken(type, secret, token)) {
if (
!token ||
!apps[code] ||
!checkToken(type, { code, ts, auth }, apps[code])
) {
ctx.throw(401, 'Unauthorized');
}

Expand Down
5 changes: 3 additions & 2 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
* egg-token default config
* @member Config#token
* @property {String} type - encryption algorithm
* @property {String} secret - secret key
* @property {Object} apps - authorized applications
*/
exports.token = {
type: 'md5',
secret: 'your-magic-secret-key',

apps: {},
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "egg-token",
"version": "1.0.2",
"version": "2.0.0",
"description": "Egg.js middleware that uses encrypted token to authenticate.",
"eggPlugin": {
"name": "token"
Expand Down
12 changes: 11 additions & 1 deletion test/fixtures/apps/token-test/config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ exports.middleware = [ 'token' ];

exports.token = {
type: 'md5',
secret: 'XnMib79vzwP01gtr',

apps: {
felixpy: {
secret: 'XnMib79vzwP01gtr',
expires: 30000,
},
codetrial: {
secret: 'mi9yNGT6zwrqMv8z',
expires: 30000,
},
},
};

exports.keys = 'egg-token';
65 changes: 57 additions & 8 deletions test/token.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
const crypto = require('crypto');
const mock = require('egg-mock');

const createToken = function(code, ts, secret) {
const hash = crypto.createHash('md5');
const md5Value = hash.update(`${code}:${ts}:${secret}`).digest('hex');

return Buffer.from(`${code}:${ts}:${md5Value}`).toString('base64');
};

describe('test/token.test.js', () => {
let app;
before(() => {
Expand All @@ -22,15 +29,57 @@ describe('test/token.test.js', () => {
.expect(401);
});

it('should GET /', () => {
const hash = crypto.createHash('md5');
const code = 'felixpy';
const ts = new Date().getTime();
const md5Value = hash
.update(`${code}:${ts}:XnMib79vzwP01gtr`)
.digest('hex');
it('should return unauthorized with wrong secret', () => {
const token = createToken('felixpy', Date.now(), 'SomeWrongSecret');

return app
.httpRequest()
.get('/')
.set('egg-api-token', token)
.expect(401);
});

it('should return unauthorized with wrong application', () => {
const token = createToken('ypxilef', Date.now(), 'XnMib79vzwP01gtr');

return app
.httpRequest()
.get('/')
.set('egg-api-token', token)
.expect(401);
});

it('should return unauthorized with wrong timestamp', () => {
const token = createToken(
'felixpy',
Date.now() - 30001,
'XnMib79vzwP01gtr'
);

return app
.httpRequest()
.get('/')
.set('egg-api-token', token)
.expect(401);
});

it('should authorized with valid timestamp', () => {
const token = createToken(
'felixpy',
Date.now() - 20000,
'XnMib79vzwP01gtr'
);

return app
.httpRequest()
.get('/')
.set('egg-api-token', token)
.expect('hi, token')
.expect(200);
});

const token = Buffer.from(`${code}:${ts}:${md5Value}`).toString('base64');
it('should authorized with app <felixpy>', () => {
const token = createToken('felixpy', Date.now(), 'XnMib79vzwP01gtr');

return app
.httpRequest()
Expand Down

0 comments on commit c84e09f

Please sign in to comment.