Added support for Admin API key extraction
refs https://github.com/TryGhost/Toolbox/issues/292 - Allows to detect and extract admin api key ID value. The reason why we are not dealing withe the "secret" value here in a similar way as Content API key is to keep the package independent from the model layer. It only provides "identification" information along with the key type so that the version mismatch data service can deal with this information in an optimal way (just one db query).
This commit is contained in:
parent
988acff403
commit
41103000d2
@ -1,16 +1,57 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Remove 'Ghost' from raw authorization header and extract the JWT token.
|
||||||
|
* Eg. Authorization: Ghost ${JWT}
|
||||||
|
* @param {string} header
|
||||||
|
*/
|
||||||
|
const extractTokenFromHeader = (header) => {
|
||||||
|
const [scheme, token] = header.split(' ');
|
||||||
|
|
||||||
|
if (/^Ghost$/i.test(scheme)) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractAdminAPIKey = (token) => {
|
||||||
|
const decoded = jwt.decode(token, {complete: true});
|
||||||
|
|
||||||
|
if (!decoded || !decoded.header || !decoded.header.kid) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded.header.kid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} ApiKey
|
||||||
|
* @prop {string} key
|
||||||
|
* @prop {string} type
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When it's a Content API the function resolves with the value of the key secret.
|
||||||
|
* When it's an Admin API the function resolves with the value of the key id.
|
||||||
*
|
*
|
||||||
* @param {import('express').Request} req
|
* @param {import('express').Request} req
|
||||||
* @returns {string}
|
* @returns {ApiKey}
|
||||||
*/
|
*/
|
||||||
const extractAPIKey = (req) => {
|
const extractAPIKey = (req) => {
|
||||||
let keyValue = null;
|
let keyValue = null;
|
||||||
|
let keyType = null;
|
||||||
|
|
||||||
if (req.query && req.query.key) {
|
if (req.query && req.query.key) {
|
||||||
keyValue = req.query.key;
|
keyValue = req.query.key;
|
||||||
|
keyType = 'content';
|
||||||
|
} else if (req.headers && req.headers.authorization) {
|
||||||
|
keyValue = extractAdminAPIKey(extractTokenFromHeader(req.headers.authorization));
|
||||||
|
keyType = 'admin';
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyValue;
|
return {
|
||||||
|
key: keyValue,
|
||||||
|
type: keyType
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = extractAPIKey;
|
module.exports = extractAPIKey;
|
||||||
|
@ -21,5 +21,7 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"jsonwebtoken": "^8.5.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,22 +3,46 @@ const extractApiKey = require('../index');
|
|||||||
|
|
||||||
describe('Extract API Key', function () {
|
describe('Extract API Key', function () {
|
||||||
it('Returns nulls for a request without any key', function () {
|
it('Returns nulls for a request without any key', function () {
|
||||||
const key = extractApiKey({
|
const {key, type} = extractApiKey({
|
||||||
query: {
|
query: {
|
||||||
filter: 'status:active'
|
filter: 'status:active'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(key, null);
|
assert.equal(key, null);
|
||||||
|
assert.equal(type, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Extracts Content API key from the request', function () {
|
it('Extracts Content API key from the request', function () {
|
||||||
const key = extractApiKey({
|
const {key, type} = extractApiKey({
|
||||||
query: {
|
query: {
|
||||||
key: '123thekey'
|
key: '123thekey'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(key, '123thekey');
|
assert.equal(key, '123thekey');
|
||||||
|
assert.equal(type, 'content');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Extracts Admin API key from the request', function () {
|
||||||
|
const {key, type} = extractApiKey({
|
||||||
|
headers: {
|
||||||
|
authorization: 'Ghost eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjYyNzM4MjQzNDZiZjUxZjNhYWI5OTA5OSJ9.eyJpYXQiOjE2NTIxNjUyNDQsImV4cCI6MTY1MjE2NTU0NCwiYXVkIjoiL3YyL2FkbWluLyJ9.VdPOZ4XffgYd8qn_46zlJR3jW_rPZTw70COkG5IYIuU'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(key, '6273824346bf51f3aab99099');
|
||||||
|
assert.equal(type, 'admin');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Returns null if malformatted Admin API Key', function () {
|
||||||
|
const {key, type} = extractApiKey({
|
||||||
|
headers: {
|
||||||
|
authorization: 'Ghost incorrectformat'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(key, null);
|
||||||
|
assert.equal(type, 'admin');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user