Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth invalid grant errors #359

Open
hannah-subtle opened this issue Jan 27, 2025 · 6 comments
Open

OAuth invalid grant errors #359

hannah-subtle opened this issue Jan 27, 2025 · 6 comments

Comments

@hannah-subtle
Copy link

I am currently receiving errors when trying to refresh my token

Jan 26 22:55:00 Access token expired. Attempting to refresh...
Jan 26 22:55:00 Refresh token invalid or expired.
Jan 26 22:55:00 Failed to refresh access token: Reauthentication required.
Jan 26 22:55:00 Error uploading track to SoundCloud:
Jan 26 22:55:00 Error Message: Reauthorization required. Refresh token is invalid or expired.
Jan 26 22:55:00Access token expired. Attempting to refresh...
Jan 26 22:55:00Refresh token invalid or expired.
Jan 26 22:55:00Failed to refresh access token: Reauthentication required.
Jan 26 22:55:00Error uploading track to SoundCloud:
Jan 26 22:55:00Error Message: Reauthorization required. Refresh token is invalid or expired.

Could you help me to understand what is wrong in my authorisation flow.

Access Token Management:

Tokens are stored in a tokens.json file. access_token expiry is calculated using: tokens.expires_at = Date.now() + tokens.expires_in * 1000
The refresh_token is used to renew the access_token when it expires.

async function refreshAccessToken(refreshToken) {
    try {
        const postData = querystring.stringify({
            client_id: clientId,
            client_secret: clientSecret,
            grant_type: 'refresh_token',
            refresh_token: refreshToken,
        });

        const response = await axios.post('https://secure.soundcloud.com/oauth/token', postData, {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        });

        const data = response.data;
        if (data.access_token) {
            data.expires_at = Date.now() + data.expires_in * 1000; // Calculate new expiry time
            saveTokens(data); // Save updated tokens
            console.log('Access token refreshed successfully.');
            return data.access_token;
        } else {
            throw new Error('Failed to refresh access token. Response data is invalid.');
        }
    } catch (error) {
        if (error.response?.data?.error === 'invalid_grant') {
            console.error('Refresh token invalid or expired.');
            throw new Error('Reauthentication required.');
        }
        throw error;
    }
}
async function getAccessToken() {
    const tokens = readTokens(); // Load tokens from file or database

    // Check if the current access token is still valid
    if (tokens && tokens.access_token && tokens.expires_at > Date.now()) {
        console.log('Access token is valid.');
        return tokens.access_token;
    }

    // Attempt to refresh the token if a refresh token is available
    if (tokens && tokens.refresh_token) {
        try {
            console.log('Access token expired. Attempting to refresh...');
            const newAccessToken = await refreshAccessToken(tokens.refresh_token);
            return newAccessToken;
        } catch (error) {
            if (error.message.includes('Reauthentication required')) {
                console.error('Refresh token invalid or expired. Reauthentication required.');
                // Trigger notification or process to reauthenticate
                throw new Error('Reauthentication required. Please reauthorize the app.');
            } else {
                console.error('Error refreshing access token:', error.message);
                throw error;
            }
        }
    }

    // No valid tokens available, require reauthentication
    console.error('No valid access or refresh token available.');
    throw new Error('Reauthentication required. No valid tokens found.');
}
@dpreussler
Copy link
Contributor

Hi there,
the refresh code looks correct.
Some things to check:

  • You can only use a refresh token once! Invalidate it after use
  • Instead of relying on expiration time, better refresh the token when you get a 401

@dpreussler
Copy link
Contributor

@hannah-subtle where you able to get this solved?

@hannah-subtle
Copy link
Author

@dpreussler working on this today, will keep you posted!

@jasongrishkoff
Copy link

My refresh code has been in place for ages now, but 3-4 days ago it started failing repeatedly. Sometimes it works, sometimes it doesn't. It seems sporadic. Could it be related to #361?

@dpreussler
Copy link
Contributor

@jasongrishkoff what is the error you're getting when trying to refresh.
And yes it might be related!

@jasongrishkoff
Copy link

Here's an example response. Hope it helps?

{"statusCode":400,"content":"{"error":"invalid_grant"}","headers":{"connection":"close","content-encoding":"gzip","content-length":"51","content-type":"application/json; charset=utf-8","date":"Tue, 04 Feb 2025 09:09:13 GMT","referrer-policy":"no-referrer","server":"am/2","strict-transport-security":"max-age=63072000","vary":"Origin","via":"1.1 134f499632d1e15750219cb766bdc50c.cloudfront.net (CloudFront)","x-amz-cf-id":"IDOuI1W5yxZzpKJzDq1kP_RzWa5MVOTQHHdhpBEeOwLkTzCxkAADRQ==","x-amz-cf-pop":"JFK50-P3","x-cache":"Error from cloudfront","x-content-type-options":"nosniff","x-frame-options":"DENY","x-robots-tag":"noindex"},"ok":false,"data":{"error":"invalid_grant"}}

I think most are refreshing just fine -- I'd guess the fail rate is 5%? I haven't actually tracked it. But I'm refreshing user tokens regularly (once per hour), so over the course of a day that means I'm needing to ask quite a few people to reconnect their accounts, as the connection failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants