-
Notifications
You must be signed in to change notification settings - Fork 61
Getting Tidal Credentials (OAuth Tokens)
This guide explains how to obtain the Tidal OAuth tokens required to configure octo-fiesta with the Tidal provider.
Status. This page documents the native Tidal provider, which talks to Tidal's own API with your account. It is unrelated to the deprecated SquidWTF Tidal backend, which relied on public third-party instances holding somebody else's credentials.
- An active Tidal subscription. A free or expired account can browse and search but cannot stream full tracks.
- A Tidal HiFi Plus plan if you want
HI_RES_LOSSLESS. Standard HiFi tops out atLOSSLESS. - A web browser to approve the device authorization.
- A writable path for the token store, so the refresh token survives restarts.
Tidal does not expose a copy-paste cookie like Deezer or a token in local storage like Qobuz. It uses an OAuth 2.0 device authorization flow, the same one used by TV and console apps.
- octo-fiesta asks
auth.tidal.comfor a device code - You open a short link in your browser and approve the request with your Tidal account
- octo-fiesta polls until you approve, then receives an access token and a refresh token
- The access token expires after four hours and is renewed automatically with the refresh token
You only do the browser step once. After that the provider keeps itself authenticated.
Run the interactive login. It prints a link, waits for you to approve it, and writes the resulting tokens to the token store.
docker compose run --rm octo-fiesta --tidal-logindotnet run -- --tidal-loginThe helper prints something like this.
Open https://link.tidal.com/ABC12 and log in with your Tidal account.
Waiting for approval... (expires in 5 minutes)
Open the link, sign in if prompted, and confirm the device. The helper detects the approval within a few seconds and prints a summary.
Tidal login successful.
User ID: 123456789
Country: FR
Subscription: HIFI_PLUS
Token store: /config/tidal-tokens.json
If the link expires before you approve it, run the helper again to get a fresh one.
Select Tidal as the music service. The tokens themselves live in the token store and do not need to be pasted anywhere.
MUSIC_SERVICE=Tidal
TIDAL_TOKEN_STORE=/config/tidal-tokens.json
TIDAL_QUALITY=LOSSLESS # Optional: HI_RES_LOSSLESS, LOSSLESS, HIGH, LOWMount a volume so the token store persists across container restarts. Without it you have to log in again on every recreate. The bundled docker-compose.yml already mounts ./config on /config for this.
services:
octo-fiesta:
volumes:
- ./config:/config{
"Subsonic": {
"MusicService": "Tidal"
},
"Tidal": {
"TokenStore": "/config/tidal-tokens.json",
"Quality": "LOSSLESS"
}
}octo-fiesta identifies itself to Tidal with a device client id and secret. This is an application credential, not an account one, so it is the same for everyone and the login does not change it. The built-in pair works today.
If Tidal retires it, the symptom is unmistakable. Login and search keep working while every
download fails with subStatus 4005 and the token stops renewing with Client id <n> not found. You can then switch to a working client without waiting for a release.
TIDAL_CLIENT_ID=theWorkingClientId
TIDAL_CLIENT_SECRET=theMatchingSecretTokens belong to the client that issued them, so after the change the stored tokens are ignored and you have to run the login helper again.
If you already have Tidal tokens from another tool, or you prefer to inject them as secrets rather than use the token store, set them explicitly. Values given this way take precedence over the token store on startup.
TIDAL_ACCESS_TOKEN=your_access_token_here
TIDAL_REFRESH_TOKEN=your_refresh_token_here
TIDAL_USER_ID=123456789
TIDAL_COUNTRY_CODE=FRThe refresh token is the important one. The access token is short-lived and is replaced automatically. If you provide a refresh token but no access token, octo-fiesta mints a fresh access token on startup. Renewals are still written to the token store, so keep it writable.
When you start octo-fiesta, the startup validation reports the account the provider will use.
┌─ Tidal ────────────────────────────────────────────────────┐
Tidal Quality: LOSSLESS
Tidal Token store: /config/tidal-tokens.json
Tidal AccessToken: eyJ0********
Tidal credentials: VALID
-> User ID: 123456789
-> Country: FR
-> Subscription: HIFI_PLUS
-> Highest available quality: HI_RES_LOSSLESS
└────────────────────────────────────────────────────────────┘
If the refresh token has been revoked, an error is shown and you need to run the login helper again.
An account without an active subscription authenticates fine but cannot stream, which is reported separately.
Tidal credentials: VALID
-> Subscription: FREE
Tidal playback: NOT ENTITLED
-> This account has no active subscription, so no track can be downloaded
| Value | Format | Bit Depth | Sample Rate | Requirements |
|---|---|---|---|---|
HI_RES_LOSSLESS |
FLAC | 24-bit | up to 192kHz | HiFi Plus |
LOSSLESS |
FLAC | 16-bit | 44.1kHz | HiFi |
HIGH |
AAC | - | 320 kbps | Any paid plan |
LOW |
AAC | - | 96 kbps | Any paid plan |
If not specified, octo-fiesta asks for HI_RES_LOSSLESS. Tidal usually answers with the best tier your subscription is entitled to rather than refusing, so asking high costs nothing. A tier that is refused outright makes octo-fiesta step down one level and try again.
The setting is a ceiling, not a guarantee. A PREMIUM plan asked for HI_RES_LOSSLESS is served HIGH, and the downloaded file is labelled from what Tidal actually delivered, not from what was requested.
HI_RES is also accepted for compatibility with older configurations. It maps to the legacy MQA-encoded tier, which Tidal has been retiring in favour of HI_RES_LOSSLESS. Prefer HI_RES_LOSSLESS for true 24-bit FLAC.
Hi-Res tracks arrive as FLAC inside a fragmented MP4 container and are saved as .m4a. The audio is lossless, only the container differs.
- The access token is valid for four hours. octo-fiesta renews it in the background before it expires, so you never notice.
- The refresh token has no expiry of its own, but changing your Tidal password or signing the device out from the Tidal app revokes it.
- Every renewal is written back to the token store, so the store must stay writable for the lifetime of the install.
- The refresh token may have been revoked. Run the login helper again to get a fresh pair.
- Check that the token store path is writable. A read-only mount silently breaks renewal, and the provider stops working once the stored access token expires.
- octo-fiesta retries one tier lower on its own. If a single track fails while others work, that track is not streamable for your account.
- If every track fails on every tier, the account has no playback entitlement. Startup validation reports this as
Tidal playback: NOT ENTITLED.
- Tidal catalogues differ by country. The country code comes from your account and cannot be overridden to bypass a regional restriction.
- Verify your plan actually includes HiFi Plus. Many tracks are also simply not published in Hi-Res, in which case the provider falls back to the best tier available.
- This is the signature of a token store that is not persisted. Confirm the volume mount and that the file survives a container restart.
- Startup validation says
Tidal playback: NOT ENTITLEDwhen the account has no active subscription. Tidal then answers401withsubStatus4005 on every quality tier, and even preview clips are refused. Subscribing is the fix.
For anyone working on the provider itself, these are the endpoints involved.
| Purpose | Endpoint |
|---|---|
| Device authorization | POST https://auth.tidal.com/v1/oauth2/device_authorization |
| Token exchange and refresh | POST https://auth.tidal.com/v1/oauth2/token |
| Session check | GET https://api.tidal.com/v1/sessions |
| Subscription | GET https://api.tidal.com/v1/users/{userId}/subscription |
| Catalogue and search | https://api.tidal.com/v1 |
| Stream manifest | GET https://api.tidal.com/v1/tracks/{id}/playbackinfopostpaywall |
Notes on the flow.
- The device authorization call takes a
client_idand the scoper_usr+w_usr+w_sub. The token calls additionally need the client secret of a Tidal device client, which is embedded in octo-fiesta rather than configured by the user. The secret goes in the form body, not in an HTTP Basic header, because Tidal's CDN rejects Basic-authenticated token calls with a 403 before they reach the API. Sending the credentials in the body reaches the API and is accepted for the device code grant. - Requests need a device-client user agent for the same reason.
- While polling, Tidal answers HTTP 400 with
sub_status1002 for as long as the user has not approved yet. Any other error status means the flow failed and should not be retried. - A refresh grant returns a new access token but no new refresh token, so the existing one is kept.
- Every catalogue endpoint requires a
countryCodequery parameter. It comes from the account and is cached in the token store. -
playbackinfopostpaywalltakesaudioquality,playbackmode=STREAMandassetpresentation=FULL. It returns a base64-encoded manifest containing the stream URLs, the codec, and akeyIdwhen the stream is encrypted. The response repeats theaudioQualityactually granted, which is often lower than the one requested, so that is the value the file is labelled with. A tier refused outright answers401withsubStatus4005, as does a manifest that fails to decode or comes back as a preview clip, and the request is then retried one tier lower. - Tidal retires device clients over time. A retired client still completes the device flow and reads the catalogue, but its stream requests answer
subStatus4005 and its refresh grant answersClient id <n> not foundafter parsing a token it has just issued. When changing the embedded client, test playback and renewal, not just login. - Playlist payloads carry
createdwith the offset written as+0000rather than+00:00, whichSystem.Text.Jsonrefuses to bind to a date. The field is read as text and parsed separately. - Hi-Res streams arrive as a DASH manifest rather than a single file, which
TidalDashManifestParserflattens into an ordered segment list.
Note: Unlike the deprecated SquidWTF provider, this one streams from your own Tidal account. Your subscription tier, and not a shared public instance, determines what you can download.