From 5f023e4d2676bf0a14c5ee0c6b8a287f4f783fa3 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Tue, 31 Mar 2026 14:53:29 +0200 Subject: [PATCH] fix: make access tokens non-expiring (matching Mastodon behavior) Access tokens were expiring after 1 hour, causing Phanpy/Elk/Moshidon to get 401 errors and become unusable. Mastodon itself creates non-expiring access tokens (valid until revoked). Removed expiresAt from all three grant types (authorization_code, client_credentials, refresh_token). Refresh tokens keep their 90-day expiry as a safety measure. Removed expires_in from JSON responses. --- lib/mastodon/routes/oauth.js | 12 ++++-------- package.json | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/mastodon/routes/oauth.js b/lib/mastodon/routes/oauth.js index 1748ee8..3965263 100644 --- a/lib/mastodon/routes/oauth.js +++ b/lib/mastodon/routes/oauth.js @@ -467,7 +467,6 @@ router.post("/oauth/token", async (req, res, next) => { accessToken, createdAt: new Date(), grantType: "client_credentials", - expiresAt: new Date(Date.now() + 3600 * 1000), }); return res.json({ @@ -475,7 +474,6 @@ router.post("/oauth/token", async (req, res, next) => { token_type: "Bearer", scope: "read", created_at: Math.floor(Date.now() / 1000), - expires_in: 3600, }); } @@ -510,9 +508,9 @@ router.post("/oauth/token", async (req, res, next) => { $set: { accessToken: newAccessToken, refreshToken: newRefreshToken, - expiresAt: new Date(Date.now() + 3600 * 1000), refreshExpiresAt: new Date(Date.now() + 90 * 24 * 3600 * 1000), }, + $unset: { expiresAt: "" }, }, ); @@ -522,7 +520,6 @@ router.post("/oauth/token", async (req, res, next) => { scope: existing.scopes.join(" "), created_at: Math.floor(existing.createdAt.getTime() / 1000), refresh_token: newRefreshToken, - expires_in: 3600, }); } @@ -590,8 +587,9 @@ router.post("/oauth/token", async (req, res, next) => { } } - // Generate access token and refresh token with expiry. - const ACCESS_TOKEN_TTL = 3600 * 1000; // 1 hour + // Generate access token and refresh token. + // Access tokens do not expire (matching Mastodon behavior — valid until revoked). + // Refresh tokens expire after 90 days as a safety measure. const REFRESH_TOKEN_TTL = 90 * 24 * 3600 * 1000; // 90 days const accessToken = randomHex(64); const refreshToken = randomHex(64); @@ -601,7 +599,6 @@ router.post("/oauth/token", async (req, res, next) => { $set: { accessToken, refreshToken, - expiresAt: new Date(Date.now() + ACCESS_TOKEN_TTL), refreshExpiresAt: new Date(Date.now() + REFRESH_TOKEN_TTL), }, }, @@ -613,7 +610,6 @@ router.post("/oauth/token", async (req, res, next) => { scope: grant.scopes.join(" "), created_at: Math.floor(grant.createdAt.getTime() / 1000), refresh_token: refreshToken, - expires_in: 3600, }); } catch (error) { next(error); diff --git a/package.json b/package.json index cbacbd1..6d7d1b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rmdes/indiekit-endpoint-activitypub", - "version": "3.12.3", + "version": "3.12.4", "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.", "keywords": [ "indiekit",