Skip to content

Commit a813fbb

Browse files
committed
feat: add cookie for site token
1 parent 86fca17 commit a813fbb

File tree

3 files changed

+121
-37
lines changed

3 files changed

+121
-37
lines changed

packages/backend/src/routers/hosting/puter-site.js

+87-28
Original file line numberDiff line numberDiff line change
@@ -153,41 +153,100 @@ class PuterSiteMiddleware extends AdvancedBase {
153153

154154
if ( site.protected ) {
155155
const svc_auth = req.services.get('auth');
156-
const token = req.query['puter.auth.token'];
157-
158-
acl_config.no_acl = false;
159-
160-
if ( ! token ) {
161-
const e = APIError.create('token_missing');
162-
return this.respond_error_({ req, res, e });
163-
}
164156

165-
const app_actor =
166-
await svc_auth.authenticate_from_token(token);
157+
const get_site_actor_from_token = async () => {
158+
const site_token = req.cookies['puter.site.token'];
159+
if ( ! site_token ) return;
160+
161+
let failed = false;
162+
let site_actor;
163+
try {
164+
site_actor =
165+
await svc_auth.authenticate_from_token(site_token);
166+
} catch (e) {
167+
failed = true;
168+
}
169+
170+
if ( failed ) return;
171+
172+
if ( ! site_actor ) return;
173+
174+
// security measure: if 'puter.site.token' is set
175+
// to a different actor type, someone is likely
176+
// trying to exploit the system.
177+
if ( ! (site_actor.type instanceof SiteActorType) ) {
178+
return;
179+
}
180+
181+
acl_config.actor = site_actor;
182+
183+
// Refresh the token if it's been 30 seconds since
184+
// the last request
185+
if (
186+
(Date.now() - site_actor.type.iat*1000)
187+
>
188+
1000*30
189+
) {
190+
const site_token = svc_auth.get_site_app_token({
191+
site_uid: site.uuid,
192+
});
193+
res.cookie('puter.site.token', site_token);
194+
}
167195

168-
const user_actor =
169-
app_actor.get_related_actor(UserActorType);
196+
return true;
197+
};
170198

171-
const svc_permission = req.services.get('permission');
172-
const perm = await (async () => {
173-
if ( user_actor.type.user.id === site.user_id ) {
174-
return {};
199+
const make_site_actor_from_app_token = async () => {
200+
const token = req.query['puter.auth.token'];
201+
202+
acl_config.no_acl = false;
203+
204+
if ( ! token ) {
205+
const e = APIError.create('token_missing');
206+
return this.respond_error_({ req, res, e });
175207
}
208+
209+
const app_actor =
210+
await svc_auth.authenticate_from_token(token);
176211

177-
return await svc_permission.check(
178-
user_actor, `site:uid#${site.uuid}:access`
179-
);
180-
})();
181-
182-
if ( ! perm ) {
183-
const e = APIError.create('forbidden');
184-
return this.respond_error_({ req, res, e });
212+
const user_actor =
213+
app_actor.get_related_actor(UserActorType);
214+
215+
const svc_permission = req.services.get('permission');
216+
const perm = await (async () => {
217+
if ( user_actor.type.user.id === site.user_id ) {
218+
return {};
219+
}
220+
221+
return await svc_permission.check(
222+
user_actor, `site:uid#${site.uuid}:access`
223+
);
224+
})();
225+
226+
if ( ! perm ) {
227+
const e = APIError.create('forbidden');
228+
this.respond_error_({ req, res, e });
229+
return false;
230+
}
231+
232+
const site_actor = await Actor.create(SiteActorType, { site });
233+
acl_config.actor = site_actor;
234+
235+
// This subdomain is allowed to keep the site actor token,
236+
// so we send it here as a cookie so other html files can
237+
// also load.
238+
const site_token = svc_auth.get_site_app_token({
239+
site_uid: site.uuid,
240+
});
241+
res.cookie('puter.site.token', site_token);
242+
return true;
185243
}
186244

187-
const site_actor = await Actor.create(SiteActorType, { site });
188-
acl_config.actor = site_actor;
189-
190-
console.log('THE SITE ACTOR?', site_actor);
245+
let ok = await get_site_actor_from_token();
246+
if ( ! ok ) {
247+
ok = await make_site_actor_from_app_token();
248+
}
249+
if ( ! ok ) return;
191250

192251
Object.freeze(acl_config);
193252
}

packages/backend/src/services/PuterSiteService.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@ class PuterSiteService extends BaseService {
103103
return rows[0];
104104
}
105105

106-
// async get_subdomain_by_uid (uid) {
107-
// const rows = await this.db.read(
108-
// `SELECT * FROM subdomains WHERE uuid = ? LIMIT 1`,
109-
// [uid]
110-
// );
111-
// if ( rows.length === 0 ) return null;
112-
// return rows[0];
113-
// }
106+
async get_subdomain_by_uid (uid) {
107+
const rows = await this.db.read(
108+
`SELECT * FROM subdomains WHERE uuid = ? LIMIT 1`,
109+
[uid]
110+
);
111+
if ( rows.length === 0 ) return null;
112+
return rows[0];
113+
}
114114
}
115115

116116
module.exports = {

packages/backend/src/services/auth/AuthService.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* You should have received a copy of the GNU Affero General Public License
1717
* along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
*/
19-
const { Actor, UserActorType, AppUnderUserActorType, AccessTokenActorType } = require("./Actor");
19+
const { Actor, UserActorType, AppUnderUserActorType, AccessTokenActorType, SiteActorType } = require("./Actor");
2020
const BaseService = require("../BaseService");
2121
const { get_user, get_app } = require("../../helpers");
2222
const { Context } = require("../../util/context");
@@ -130,6 +130,17 @@ class AuthService extends BaseService {
130130
type: actor_type,
131131
});
132132
}
133+
134+
if ( decoded.type === 'actor-site' ) {
135+
const site_uid = decoded.site_uid;
136+
const svc_puterSite = this.services.get('puter-site');
137+
const site =
138+
await svc_puterSite.get_subdomain_by_uid(site_uid);
139+
return Actor.create(SiteActorType, {
140+
site,
141+
iat: decoded.iat,
142+
});
143+
}
133144

134145
throw APIError.create('token_auth_failed');
135146
}
@@ -159,6 +170,20 @@ class AuthService extends BaseService {
159170

160171
return token;
161172
}
173+
174+
get_site_app_token ({ site_uid }) {
175+
const token = this.modules.jwt.sign(
176+
{
177+
type: 'actor-site',
178+
version: '0.0.0',
179+
site_uid,
180+
},
181+
this.global_config.jwt_secret,
182+
{ expiresIn: '1h' },
183+
);
184+
185+
return token;
186+
}
162187

163188
async create_session_ (user, meta = {}) {
164189
this.log.info(`CREATING SESSION`);

0 commit comments

Comments
 (0)