mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Added support for serverside rendering of members content (#10522)
no-issue - Added member auth middleware to siteApp - Passed member as context in routing service - set Cache-Control: private for member requests - fucked up some tests - Added member as global template variable - Updated tokens to have expiry of subscription_period_end
This commit is contained in:
parent
121b7d200f
commit
25aac1359d
4 changed files with 32 additions and 18 deletions
|
@ -69,6 +69,10 @@ module.exports = function MembersApi({
|
||||||
.then(member => encodeToken({
|
.then(member => encodeToken({
|
||||||
sub: member.id,
|
sub: member.id,
|
||||||
plans: member.subscriptions.map(sub => sub.plan),
|
plans: member.subscriptions.map(sub => sub.plan),
|
||||||
|
exp: member.subscriptions
|
||||||
|
.map(sub => sub.validUntil)
|
||||||
|
.reduce((a, b) => Math.min(a, b),
|
||||||
|
Math.floor((Date.now() / 1000) + (60 * 60 * 24 * 30))),
|
||||||
aud: audience
|
aud: audience
|
||||||
}))
|
}))
|
||||||
.then(token => res.end(token))
|
.then(token => res.end(token))
|
||||||
|
|
|
@ -48,7 +48,6 @@ export default class MembersProvider extends Component {
|
||||||
return this.ready.then(() => {
|
return this.ready.then(() => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.gateway.call(method, options, (err, successful) => {
|
this.gateway.call(method, options, (err, successful) => {
|
||||||
console.log({method, options, err, successful});
|
|
||||||
if (err || !successful) {
|
if (err || !successful) {
|
||||||
reject(err || !successful);
|
reject(err || !successful);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,23 +24,33 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTokenExpired(token) {
|
function isTokenExpired(token) {
|
||||||
|
const claims = getClaims(token);
|
||||||
|
|
||||||
|
if (!claims) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiry = claims.exp * 1000;
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
const nearFuture = now + (30 * 1000);
|
||||||
|
|
||||||
|
if (expiry < nearFuture) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClaims(token) {
|
||||||
try {
|
try {
|
||||||
const [header, claims, signature] = token.split('.'); // eslint-disable-line no-unused-vars
|
const [header, claims, signature] = token.split('.'); // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
const parsedClaims = JSON.parse(atob(claims.replace('+', '-').replace('/', '_')));
|
const parsedClaims = JSON.parse(atob(claims.replace('+', '-').replace('/', '_')));
|
||||||
|
|
||||||
const expiry = parsedClaims.exp * 1000;
|
return parsedClaims;
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
const nearFuture = now + (30 * 1000);
|
|
||||||
|
|
||||||
if (expiry > nearFuture) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +58,8 @@
|
||||||
const tokenKey = 'members:token:aud:' + audience;
|
const tokenKey = 'members:token:aud:' + audience;
|
||||||
const storedToken = storage.getItem(tokenKey);
|
const storedToken = storage.getItem(tokenKey);
|
||||||
if (isTokenExpired(storedToken)) {
|
if (isTokenExpired(storedToken)) {
|
||||||
|
const storedTokenKeys = getStoredTokenKeys();
|
||||||
|
storage.setItem('members:tokens', JSON.stringify(storedTokenKeys.filter(key => key !== tokenKey)));
|
||||||
storage.removeItem(tokenKey);
|
storage.removeItem(tokenKey);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -86,10 +98,10 @@
|
||||||
|
|
||||||
// @TODO this needs to be configurable
|
// @TODO this needs to be configurable
|
||||||
const membersApi = location.pathname.replace(/\/members\/gateway\/?$/, '/ghost/api/v2/members');
|
const membersApi = location.pathname.replace(/\/members\/gateway\/?$/, '/ghost/api/v2/members');
|
||||||
function getToken({audience}) {
|
function getToken({audience, fresh}) {
|
||||||
const storedToken = getStoredToken(audience);
|
const storedToken = getStoredToken(audience);
|
||||||
|
|
||||||
if (storedToken) {
|
if (storedToken && !fresh) {
|
||||||
return Promise.resolve(storedToken);
|
return Promise.resolve(storedToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,10 +135,9 @@
|
||||||
if (storage.getItem('signedin')) {
|
if (storage.getItem('signedin')) {
|
||||||
window.parent.postMessage({event: 'signedin'}, origin);
|
window.parent.postMessage({event: 'signedin'}, origin);
|
||||||
} else {
|
} else {
|
||||||
window.parent.postMessage({event: 'signedout'}, origin);
|
getToken({audience: origin, fresh: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
getToken({audience: origin});
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,15 @@ module.exports = function ({
|
||||||
const keyStore = jose.JWK.createKeyStore();
|
const keyStore = jose.JWK.createKeyStore();
|
||||||
const keyStoreReady = keyStore.add(privateKey, 'pem');
|
const keyStoreReady = keyStore.add(privateKey, 'pem');
|
||||||
|
|
||||||
function encodeToken({sub, aud = issuer, plans}) {
|
function encodeToken({sub, aud = issuer, plans, exp}) {
|
||||||
return keyStoreReady.then(jwk => jwt.sign({
|
return keyStoreReady.then(jwk => jwt.sign({
|
||||||
sub,
|
sub,
|
||||||
|
exp,
|
||||||
plans,
|
plans,
|
||||||
kid: jwk.kid
|
kid: jwk.kid
|
||||||
}, privateKey, {
|
}, privateKey, {
|
||||||
algorithm: 'RS512',
|
algorithm: 'RS512',
|
||||||
audience: aud,
|
audience: aud,
|
||||||
expiresIn: '30m',
|
|
||||||
issuer
|
issuer
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue