sahat/hackathon-starter · error · Error

Missing or invalid token/tokenSecret

Error message

Missing or invalid token/tokenSecret

What it means

OAuth 1.0a strategies exchange the request token for an access token and tokenSecret; this Tumblr strategy callback throws when either is falsy, meaning the token exchange step failed or returned empty credentials.

Source

Thrown at config/passport.js:593

 * Tumblr API OAuth.
 */
passport.use(
  'tumblr',
  new OAuthStrategy(
    {
      requestTokenURL: 'https://www.tumblr.com/oauth/request_token',
      accessTokenURL: 'https://www.tumblr.com/oauth/access_token',
      userAuthorizationURL: 'https://www.tumblr.com/oauth/authorize',
      consumerKey: process.env.TUMBLR_KEY,
      consumerSecret: process.env.TUMBLR_SECRET,
      callbackURL: '/auth/tumblr/callback',
      state: true,
      passReqToCallback: true,
    },
    async (req, token, tokenSecret, profile, done) => {
      try {
        if (!token || !tokenSecret) {
          throw new Error('Missing or invalid token/tokenSecret');
        }
        // Helper function to generate the OAuth 1.0a authHeader for Tumblr API.
        // This function is not going to make any actual calls to
        // tumblr's /request_token or /access_token endpoints.
        function getTumblrAuthHeader(url, method) {
          const oauth = new OAuth('https://www.tumblr.com/oauth/request_token', 'https://www.tumblr.com/oauth/access_token', process.env.TUMBLR_KEY, process.env.TUMBLR_SECRET, '1.0A', null, 'HMAC-SHA1');
          return oauth.authHeader(url, token, tokenSecret, method);
        }
        const userInfoURL = 'https://api.tumblr.com/v2/user/info';
        const response = await fetch(userInfoURL, { headers: { Authorization: getTumblrAuthHeader(userInfoURL, 'GET') } });
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        // Extract user info from the API response
        const tumblrUser = data.response.user;
        const primaryBlog = tumblrUser.blogs?.find((blog) => blog.primary) || tumblrUser.blogs?.[0];
        const providerProfile = {

View on GitHub (pinned to c12e339564)

Solutions

  1. Verify TUMBLR_KEY and TUMBLR_SECRET are set and match the Tumblr app credentials
  2. Check the callback URL in the Tumblr app matches /auth/tumblr/callback exactly (scheme, host, path)
  3. Watch server logs for the preceding OAuth exchange error; if the user denied consent, prompt them to retry and approve

Example fix

// before
if (!token || !tokenSecret) throw new Error('Missing or invalid token/tokenSecret');
// after: surface a flash message via the done(error) handler / auth/failure route
if (!token || !tokenSecret) return done(new Error('Tumblr authorization was not completed. Please try again.'));
Defensive patterns

Strategy: try-catch

Validate before calling

if (!process.env.TUMBLR_KEY || !process.env.TUMBLR_SECRET) throw new Error('Tumblr env vars missing');

Try / catch

catch (e) { if (/Missing or invalid token/.test(e.message)) return done(null, false, {msg:'Tumblr authorization incomplete'}); throw e; }

Prevention

When it happens

Trigger: Completing the Tumblr OAuth 1.0a handshake where the callback receives token or tokenSecret as null/undefined/empty — e.g. user denied authorization, callback URL mismatch, or Tumblr returned an error payload instead of credentials.

Common situations: TUMBLR_KEY/TUMBLR_SECRET missing or wrong in .env, callback URL registered in the Tumblr app not matching the route, or clock/nonce issues in OAuth 1.0a causing an empty token response.

Understand the failure class

Related errors


AI-assisted analysis of sahat/hackathon-starter@c12e339564 (2026-08-27). Data as JSON: /api/errors/26740b0bbe236c52. Report an issue: GitHub.