Published on June 19, 2026

Quick answer: Each streaming service uses its own OAuth 2.0 implementation with different token lifetimes, scope formats, refresh behaviors, and error responses. Building a multi-service music app means maintaining 12 separate auth flows that break in 12 different ways.
Authentication against a single music API is straightforward. You register an app, redirect the user, exchange an authorization code for tokens, and store them. The problem starts when your product needs to support more than one service.
Here is what actually differs across services:
user-read-private user-library-read). Another uses comma-separated lists. A third uses URL-style scopes.If you have ever built a multi-service integration, you know the auth layer alone can take weeks. The MusicAPI unified integration guide covers the broader architecture challenge, but authentication deserves its own deep treatment.
Quick answer: All 12 services that support user authentication use OAuth 2.0 Authorization Code flow, but their implementations diverge on redirect URI validation, PKCE support, token exchange endpoints, and required headers.
The Authorization Code flow follows the same general pattern everywhere:
That four-step pattern looks simple. The complexity hides in the details.
Some services require exact-match redirect URIs registered in their developer portal. Others allow wildcard subdomains. A few support localhost for development but reject it in production. If your redirect URI does not match exactly what the service expects, the auth flow fails silently or throws a cryptic error.
PKCE adds a code_verifier and code_challenge to prevent authorization code interception attacks. Some services require PKCE for public clients (mobile and SPA apps). Others support it optionally. A few do not support it at all. If you are building a cross-platform app, you need to know which services need PKCE and which ones reject it.
The token exchange step should be a simple POST request. In practice:
client_id and client_secret in the request body.Authorization: Basic header.application/x-www-form-urlencoded.For a broader look at how these services compare beyond authentication, see the future of music API integration.
Quick answer: Token refresh is where multi-service auth gets painful. Expiry times, refresh token rotation policies, and failure modes differ across every platform. Some services silently expire refresh tokens after 7 days of inactivity, others rotate them on every use, and a few require complete re-authorization.
Silent token invalidation: Some services revoke tokens when the user changes their password or revokes access from their account settings. Your stored tokens stop working with no webhook or notification. The only way to detect this is to handle 401 responses gracefully and trigger re-authentication.
Refresh token rotation: Several services issue a new refresh token with every token refresh request. If your app fails to store the new refresh token (because of a network error, a crash, or a race condition between concurrent requests), you lose access permanently. You need atomic token storage with retry logic.
Scope downgrade on refresh: At least two services return a narrower set of scopes when you refresh a token compared to the original authorization. If your app checks scopes at runtime, this mismatch causes unexpected permission errors.
Clock skew issues: Token expiry is based on timestamps. If your server's clock drifts by even 30 seconds, you may try to use expired tokens or refresh tokens too early (which some services treat as suspicious behavior). Always refresh proactively, at least 60 seconds before expiry.
Concurrent refresh requests: If two threads in your app try to refresh the same token simultaneously, the second request may fail because the first already consumed the refresh token. You need a mutex or queue for token refresh operations.
Every one of these edge cases requires its own error handling, retry logic, and monitoring. Multiply that by 12 services, and you are looking at a substantial chunk of your engineering time spent on auth maintenance rather than product development.
MusicAPI handles all of this behind a single OAuth flow. Token refresh, rotation, scope management, and error recovery happen automatically. You authenticate the user once through MusicAPI's auth initialization, and MusicAPI keeps the session alive across all connected services. Check out the authorization docs to see how this works under the hood.
Quick answer: The table below compares OAuth version, token expiry, refresh token support, and required scopes across all 12 streaming services that support user authentication through MusicAPI.
| Service | OAuth Version | Token Expiry | Refresh Token | Refresh Token Rotation | Typical Scopes Required |
|---|---|---|---|---|---|
| Spotify | OAuth 2.0 | 1 hour | Yes | No | user-read-private, user-library-read, playlist-modify-public |
| Apple Music | OAuth 2.0 + JWT | 6 months (developer token) | N/A (developer token model) | N/A | MusicKit access |
| YouTube | OAuth 2.0 | 1 hour | Yes | No | youtube.readonly, youtube.force-ssl |
| Tidal | OAuth 2.0 | 24 hours | Yes | Yes (single-use) | user-read, playlists-read, playlists-write |
| Amazon Music | OAuth 2.0 (LWA) | 1 hour | Yes | No | amazon_music:access |
| Deezer | OAuth 2.0 (simplified) | No expiry | No | N/A | basic_access, manage_library, listening_history |
| SoundCloud | OAuth 2.0 | 1 hour | Yes | Yes | non-expiring (single scope) |
| Napster | OAuth 2.0 | 24 hours | Yes | No | N/A (single access level) |
| Qobuz | OAuth 2.0 | Varies | Yes | No | N/A (app-level access) |
| Boomplay | OAuth 2.0 | Varies | Yes | No | Standard read/write |
| Audiomack | OAuth 2.0 | 1 hour | Yes | No | Standard read/write |
| Audius | OAuth 2.0 | Session-based | No | N/A | Read access (decentralized) |
Key takeaways from this table:
You can explore each service's user profile endpoint to see the data available after authentication: Napster user profiles, Apple Music user profiles, and more across all supported services.
Quick answer: MusicAPI replaces 12 separate OAuth implementations with a single redirect-based flow. Initialize auth with one URL, handle one callback format, and get a unified user identifier that works across all services. No per-service token management required.
Here is how the entire authentication flow works with MusicAPI:
Redirect your user to MusicAPI's authentication page. You can let them choose their service, or pre-select one:
// Generic: let the user pick their streaming service
const authUrl = `https://connect.musicapi.com/auth/{yourAccountSlug}?returnUrl=${encodeURIComponent('https://yourapp.com/callback')}`;
// Pre-select a specific service
const spotifyAuthUrl = `https://connect.musicapi.com/auth/{yourAccountSlug}?returnUrl=${encodeURIComponent('https://yourapp.com/callback')}&musicService=spotify`;
// With a unique ID for polling auth status
const authWithPolling = `https://connect.musicapi.com/auth/{yourAccountSlug}?returnUrl=${encodeURIComponent('https://yourapp.com/callback')}&uniqueId=${crypto.randomUUID()}`;
// Redirect the user
window.location.href = authUrl;
That is it for initialization. No client IDs per service. No scope strings to look up. No PKCE configuration. One URL pattern for all 12 services. See the full initialization docs for additional parameters.
After the user authenticates, MusicAPI redirects to your returnUrl with a base64-encoded data64 parameter:
// Express.js callback handler
app.get('/callback', async (req, res) => {
const data = JSON.parse(
Buffer.from(req.query.data64, 'base64').toString('utf-8')
);
// data.authModel.uuid: use this to fetch user info
// data.authModel.status: 'success' or 'error'
// data.integrationUserUUID: unique user identifier
// data.integration.type: which service they connected (e.g., 'spotify')
if (data.authModel.status === 'success') {
// Fetch the authenticated user's profile
const userInfo = await fetch(
`https://api.musicapi.com/app/integrations/${data.authModel.uuid}`,
{ headers: { 'Content-Type': 'application/json' } }
);
const profile = await userInfo.json();
// profile.integrationUser contains: name, email, imageUrl, country
// Store profile.integrationUser and data.authModel.uuid in your database
res.redirect('/dashboard');
} else {
res.redirect('/auth-error');
}
});
The callback format is identical regardless of which streaming service the user picked. Full callback reference: authentication callback docs.
If you need the service's raw access token for direct API calls, request it from your backend:
// Backend only: requires your Client Secret
const response = await fetch(
`https://api.musicapi.com/public/integrations/user/${integrationUserUUID}`,
{
headers: {
'Authorization': `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,
'Content-Type': 'application/json; charset=utf-8'
}
}
);
const userData = await response.json();
// userData.authData.accessToken: the service's raw access token
// userData.authDataExpiresAt: expiry timestamp in milliseconds
See the original auth tokens documentation for the one-time-token approach for frontend access.
With MusicAPI handling authentication, you skipped:
For the full list of features available after authentication, check the supported features matrix and available endpoints.
Quick answer: When using MusicAPI, you store one identifier per user (the authModel.uuid or integrationUserUUID) instead of raw tokens. If you are managing tokens directly, encrypt at rest, use short-lived access tokens, and never expose refresh tokens to the client.
Your token storage simplifies to a single database column: the integrationUserUUID returned during authentication callback. MusicAPI manages the underlying access and refresh tokens for each service. You never see or store raw streaming service tokens unless you explicitly request them through the original auth tokens endpoint.
Keep the authModel.uuid secure. Anyone with this identifier can retrieve user data through MusicAPI's API. Treat it with the same care you would treat a session token.
For teams that need to store raw service tokens (retrieved via MusicAPI's original token endpoint or obtained directly from services):
Encrypt tokens at rest. Use AES-256 or your cloud provider's KMS. Never store raw access or refresh tokens in plaintext; not in your database, not in environment variables, not in logs.
Separate access and refresh token storage. Store refresh tokens in a higher-security tier than access tokens. If an attacker compromises your access token store, they get tokens that expire in minutes or hours. If they get refresh tokens, they have persistent access.
Implement token refresh as a background job. Do not refresh tokens synchronously during user requests. Run a background worker that refreshes tokens 60 to 120 seconds before they expire. This prevents latency spikes and handles services with strict rate limits on auth endpoints.
Use a mutex for refresh operations. Only one process should refresh a given user's token at a time. Use a distributed lock (Redis, database advisory lock, or similar) to prevent concurrent refresh requests from invalidating each other.
Rotate encryption keys. If your token encryption key is compromised, you need to re-encrypt all stored tokens with a new key. Build this capability before you need it.
Audit token access. Log every token retrieval and refresh operation with the requesting service, user ID, and timestamp. This helps you detect unauthorized access and debug auth failures.
Each music streaming service implements OAuth 2.0 with its own authorization endpoint, token endpoint, scope format, and token lifetime. Your app redirects users to the service's authorization page, receives an authorization code on callback, and exchanges it for access and refresh tokens. The implementation details differ significantly per service, which is why tools like MusicAPI exist to unify them.
All 12 services that support authenticated user access use some variant of OAuth 2.0, but the implementations differ. Most follow the standard Authorization Code flow. One service uses JWT-based developer tokens instead of traditional user OAuth tokens. A few use simplified OAuth flows without refresh tokens. Check the supported music services page for the current list.
Each service has different token expiry times (1 hour to 12 months), and some rotate refresh tokens on every use. You need per-service refresh logic, atomic token storage, and error handling for each service's failure modes. MusicAPI automates this entirely: authenticate once through the unified auth flow, and MusicAPI handles all token lifecycle management behind the scenes.
Scope requirements vary by service and by what data you need. Most services require at least a read-profile scope. Playlist access, library read/write, and playback control each require additional scopes. Some services use a single blanket scope while others have granular permission strings. MusicAPI manages scope negotiation for you based on the features you need.
Yes. Each user can connect multiple streaming accounts. With MusicAPI, each service connection produces its own integrationUserUUID. Store all of them against the same user in your database. The authentication callback tells you which service was connected via the integration.type field.
Each service returns errors in different formats and uses HTTP status codes inconsistently. A 401 might mean "expired token" on one service and "invalid client" on another. MusicAPI normalizes these errors through a consistent callback format: check authModel.status for success or error, and use the included error message for debugging. See the rate limiting docs for handling throttled auth requests.
MusicAPI offers a free trial to get started. Check the pricing page for details on plans and usage limits. You can also contact the team for enterprise plans or custom requirements.
Ready to skip months of OAuth and SDK work? Start your free MusicAPI trial and connect all 12 streaming services with one unified API. Get started with the authentication docs and have your first user connected in minutes.