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:
Fabien O'Carroll 2019-02-25 17:03:27 +01:00
parent 121b7d200f
commit 25aac1359d
4 changed files with 32 additions and 18 deletions

View File

@ -69,6 +69,10 @@ module.exports = function MembersApi({
.then(member => encodeToken({
sub: member.id,
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
}))
.then(token => res.end(token))

View File

@ -48,7 +48,6 @@ export default class MembersProvider extends Component {
return this.ready.then(() => {
return new Promise((resolve, reject) => {
this.gateway.call(method, options, (err, successful) => {
console.log({method, options, err, successful});
if (err || !successful) {
reject(err || !successful);
}

View File

@ -24,23 +24,33 @@
}
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 {
const [header, claims, signature] = token.split('.'); // eslint-disable-line no-unused-vars
const parsedClaims = JSON.parse(atob(claims.replace('+', '-').replace('/', '_')));
const expiry = parsedClaims.exp * 1000;
const now = Date.now();
const nearFuture = now + (30 * 1000);
if (expiry > nearFuture) {
return true;
}
return false;
return parsedClaims;
} catch (e) {
return true;
return null;
}
}
@ -48,6 +58,8 @@
const tokenKey = 'members:token:aud:' + audience;
const storedToken = storage.getItem(tokenKey);
if (isTokenExpired(storedToken)) {
const storedTokenKeys = getStoredTokenKeys();
storage.setItem('members:tokens', JSON.stringify(storedTokenKeys.filter(key => key !== tokenKey)));
storage.removeItem(tokenKey);
return null;
}
@ -86,10 +98,10 @@
// @TODO this needs to be configurable
const membersApi = location.pathname.replace(/\/members\/gateway\/?$/, '/ghost/api/v2/members');
function getToken({audience}) {
function getToken({audience, fresh}) {
const storedToken = getStoredToken(audience);
if (storedToken) {
if (storedToken && !fresh) {
return Promise.resolve(storedToken);
}
@ -123,10 +135,9 @@
if (storage.getItem('signedin')) {
window.parent.postMessage({event: 'signedin'}, origin);
} else {
window.parent.postMessage({event: 'signedout'}, origin);
getToken({audience: origin, fresh: true});
}
getToken({audience: origin});
return Promise.resolve();
});

View File

@ -9,15 +9,15 @@ module.exports = function ({
const keyStore = jose.JWK.createKeyStore();
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({
sub,
exp,
plans,
kid: jwk.kid
}, privateKey, {
algorithm: 'RS512',
audience: aud,
expiresIn: '30m',
issuer
}));
}