Remove auth
This commit is contained in:
parent
db01dc234b
commit
390498fefd
|
@ -1,65 +0,0 @@
|
|||
import request from 'supertest';
|
||||
|
||||
import createServer from '../createServer';
|
||||
import withRevokedToken from './utils/withRevokedToken';
|
||||
import withToken from './utils/withToken';
|
||||
|
||||
describe('The /_auth endpoint', () => {
|
||||
let server;
|
||||
beforeEach(() => {
|
||||
server = createServer();
|
||||
});
|
||||
|
||||
describe('POST /_auth', () => {
|
||||
it('creates a new auth token', done => {
|
||||
request(server)
|
||||
.post('/_auth')
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /_auth', () => {
|
||||
describe('with no auth', () => {
|
||||
it('echoes back null', done => {
|
||||
request(server)
|
||||
.get('/_auth')
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(res.body.auth).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a revoked auth token', () => {
|
||||
it('echoes back null', done => {
|
||||
withRevokedToken({ some: { scope: true } }, token => {
|
||||
request(server)
|
||||
.get('/_auth?token=' + token)
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(res.body.auth).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a valid auth token', () => {
|
||||
it('echoes back the auth payload', done => {
|
||||
withToken({ some: { scope: true } }, token => {
|
||||
request(server)
|
||||
.get('/_auth?token=' + token)
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(typeof res.body.auth).toBe('object');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,89 +0,0 @@
|
|||
import request from 'supertest';
|
||||
|
||||
import createServer from '../createServer';
|
||||
import withAuthHeader from './utils/withAuthHeader';
|
||||
import withRevokedToken from './utils/withRevokedToken';
|
||||
import withToken from './utils/withToken';
|
||||
|
||||
describe('The /api/auth endpoint', () => {
|
||||
let server;
|
||||
beforeEach(() => {
|
||||
server = createServer();
|
||||
});
|
||||
|
||||
describe('POST /api/auth', () => {
|
||||
it('creates a new auth token', done => {
|
||||
request(server)
|
||||
.post('/api/auth')
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/auth', () => {
|
||||
describe('with no auth', () => {
|
||||
it('echoes back null', done => {
|
||||
request(server)
|
||||
.get('/api/auth')
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(res.body.auth).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a revoked auth token', () => {
|
||||
it('echoes back null', done => {
|
||||
withRevokedToken({ some: { scope: true } }, token => {
|
||||
request(server)
|
||||
.get('/api/auth?token=' + token)
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(res.body.auth).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a valid auth token', () => {
|
||||
describe('in the query string', () => {
|
||||
it('echoes back the auth payload', done => {
|
||||
const scopes = { some: { scope: true } };
|
||||
|
||||
withToken(scopes, token => {
|
||||
request(server)
|
||||
.get('/api/auth?token=' + token)
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(res.body.auth).toBeDefined();
|
||||
expect(res.body.auth.scopes).toMatchObject(scopes);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('in the Authorization header', () => {
|
||||
it('echoes back the auth payload', done => {
|
||||
const scopes = { some: { scope: true } };
|
||||
|
||||
withAuthHeader(scopes, header => {
|
||||
request(server)
|
||||
.get('/api/auth')
|
||||
.set({ Authorization: header })
|
||||
.end((err, res) => {
|
||||
expect(res.body).toHaveProperty('auth');
|
||||
expect(res.body.auth).toBeDefined();
|
||||
expect(res.body.auth.scopes).toMatchObject(scopes);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
import closeDatabase from './utils/closeDatabase';
|
||||
|
||||
afterAll(closeDatabase);
|
|
@ -1,5 +0,0 @@
|
|||
import data from '../../utils/data';
|
||||
|
||||
export default function closeDatabase() {
|
||||
data.quit();
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import withToken from './withToken';
|
||||
|
||||
function encodeBase64(token) {
|
||||
return Buffer.from(token).toString('base64');
|
||||
}
|
||||
|
||||
export default function withAuthHeader(scopes, done) {
|
||||
withToken(scopes, token => {
|
||||
done(encodeBase64(token));
|
||||
});
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { revokeToken } from '../../utils/auth';
|
||||
import withToken from './withToken';
|
||||
|
||||
export default function withRevokedToken(scopes, done) {
|
||||
withToken(scopes, token => {
|
||||
revokeToken(token).then(() => {
|
||||
done(token);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import { createToken } from '../../utils/auth';
|
||||
|
||||
export default function withToken(scopes, done) {
|
||||
createToken(scopes).then(done);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import { createToken } from '../utils/auth';
|
||||
|
||||
const defaultScopes = {};
|
||||
|
||||
export default function createAuth(req, res) {
|
||||
createToken(defaultScopes).then(
|
||||
token => {
|
||||
res.send({ token });
|
||||
},
|
||||
error => {
|
||||
console.error(error);
|
||||
|
||||
res.status(500).send({
|
||||
error: 'Unable to generate auth token'
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export default function serveAuth(req, res) {
|
||||
res.send({ auth: req.user });
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import { verifyToken } from '../utils/auth';
|
||||
|
||||
function decodeBase64(string) {
|
||||
return Buffer.from(string, 'base64').toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets req.user from the payload in the auth token in the request.
|
||||
*/
|
||||
export default function userToken(req, res, next) {
|
||||
if (req.user !== undefined) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const auth = req.get('Authorization');
|
||||
const token = auth && decodeBase64(auth);
|
||||
|
||||
if (!token) {
|
||||
req.user = null;
|
||||
return next();
|
||||
}
|
||||
|
||||
verifyToken(token).then(
|
||||
payload => {
|
||||
req.user = payload;
|
||||
next();
|
||||
},
|
||||
error => {
|
||||
if (error.name === 'JsonWebTokenError') {
|
||||
res.status(403).send({
|
||||
error: `Bad auth token: ${error.message}`
|
||||
});
|
||||
} else {
|
||||
console.error(error);
|
||||
|
||||
res.status(500).send({
|
||||
error: 'Unable to verify auth'
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import * as auth from '../auth';
|
||||
|
||||
describe('Auth API', () => {
|
||||
beforeEach(done => {
|
||||
auth.removeAllRevokedTokens().then(() => done(), done);
|
||||
});
|
||||
|
||||
it('creates tokens with the right scopes', done => {
|
||||
const scopes = {
|
||||
blacklist: {
|
||||
add: true,
|
||||
remove: true
|
||||
}
|
||||
};
|
||||
|
||||
auth.createToken(scopes).then(token => {
|
||||
auth.verifyToken(token).then(payload => {
|
||||
expect(payload.jti).toEqual(expect.any(String));
|
||||
expect(payload.iss).toEqual(expect.any(String));
|
||||
expect(payload.iat).toEqual(expect.any(Number));
|
||||
expect(payload.scopes).toMatchObject(scopes);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to verify revoked tokens', done => {
|
||||
const scopes = {};
|
||||
|
||||
auth.createToken(scopes).then(token => {
|
||||
auth.revokeToken(token).then(() => {
|
||||
auth.verifyToken(token).then(payload => {
|
||||
expect(payload).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,86 +0,0 @@
|
|||
import crypto from 'crypto';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import data from './data';
|
||||
import { privateKey, publicKey } from './secret';
|
||||
|
||||
function getCurrentSeconds() {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
function createTokenId() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
export function createToken(scopes = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const payload = {
|
||||
jti: createTokenId(),
|
||||
iss: 'https://unpkg.com',
|
||||
iat: getCurrentSeconds(),
|
||||
scopes
|
||||
};
|
||||
|
||||
jwt.sign(payload, privateKey, { algorithm: 'RS256' }, (error, token) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(token);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const revokedTokensSet = 'revoked-tokens';
|
||||
|
||||
export function verifyToken(token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = { algorithms: ['RS256'] };
|
||||
|
||||
jwt.verify(token, publicKey, options, (error, payload) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (payload.jti) {
|
||||
data.sismember(revokedTokensSet, payload.jti, (error, value) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(value === 0 ? payload : null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function revokeToken(token) {
|
||||
return verifyToken(token).then(payload => {
|
||||
if (payload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
data.sadd(revokedTokensSet, payload.jti, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function removeAllRevokedTokens() {
|
||||
return new Promise((resolve, reject) => {
|
||||
data.del(revokedTokensSet, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import redis from 'redis';
|
||||
|
||||
redis.debug_mode = process.env.DEBUG_REDIS != null;
|
||||
|
||||
const client = redis.createClient(
|
||||
process.env.DATA_URL || process.env.OPENREDIS_URL || 'redis://localhost:6379'
|
||||
);
|
||||
|
||||
export default client;
|
|
@ -25,7 +25,6 @@
|
|||
"gunzip-maybe": "^1.4.1",
|
||||
"invariant": "^2.2.4",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsonwebtoken": "^8.4.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"mime": "^2.4.0",
|
||||
"morgan": "^1.9.1",
|
||||
|
|
80
yarn.lock
80
yarn.lock
|
@ -1556,11 +1556,6 @@ buffer-alloc@^1.2.0:
|
|||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-fill@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
|
@ -2187,13 +2182,6 @@ ecc-jsbn@~0.1.1:
|
|||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
|
@ -3925,22 +3913,6 @@ jsonify@~0.0.0:
|
|||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
|
||||
|
||||
jsonwebtoken@^8.4.0:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
|
||||
|
@ -3958,23 +3930,6 @@ jsx-ast-utils@^2.1.0:
|
|||
dependencies:
|
||||
array-includes "^3.0.3"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
||||
|
@ -4053,41 +4008,6 @@ locate-path@^2.0.0:
|
|||
p-locate "^2.0.0"
|
||||
path-exists "^3.0.0"
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
|
|
Loading…
Reference in New Issue