[How To] Request a X-Plex-Token token for your app

Is there a way to assign a custom Logo for your App when you add it?
I’m using the Linux Terminal curl method:

curl -X "POST" "https://plex.tv/users/sign_in.json" \
    -H "X-Plex-Version: 1.0.2" \
    -H "X-Plex-Product: PlexUpdater" \
    -H "X-Plex-Client-Identifier: 4444-s-444" \
    -H "Content-Type: application/x-www-form-urlencoded; charset=utf-8" \
    --data-urlencode "user[password]=YOUR_PASSWORD" \
    --data-urlencode "user[login]=YOUR_USERNAME"

Want to use this: https://i.imgur.com/6zR5eUA.png

or you can do it a super easy way and not even have to go through a URL, just install Tautulli and have it fetch the token for you, after setup and it identifies your server go into Settings>Plex Media Server scroll all the way down to plex.tv account token, click ‘fetch token’ copy/paste.

I’ve written some documentation on how third-party apps can integrate with Plex authentication.

Here’s a Swift version that works for iOS:


    static let componentsToken: URLComponents = {
        var components = URLComponents()
        components.scheme = "https"
        components.host = "plex.tv"
        return components
    }()

    /// Request a Plex server token
    /// - Parameters:
    ///   - login: plex.tv user login.
    ///   - password: plex.tv user password.
    ///   - token: The token retrieved, or nil.
    ///   - error: If not nil, the error that occurred.
    static func requestToken(login: String, password: String, completion: @escaping (_ token: String?, _ error: PlexError?) -> Void) {
        
        var components = Self.componentsToken
        components.path = "/users/sign_in.json"
        
        if let url = components.url {
            
            let session = URLSession.shared
            
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            
            let infoDictionary = Bundle.main.infoDictionary
            
            guard let bundleID = Bundle.main.bundleIdentifier,
                let appName = infoDictionary?["CFBundleName"] as? String
                else {
                    completion(nil, PlexError.tokenError("Failed to get necessary header info from app!"))
                    return
            }
            
            request.setValue(bundleID, forHTTPHeaderField: "X-Plex-Client-Identifier")
            request.setValue(appName, forHTTPHeaderField: "X-Plex-Product")
            request.setValue("1.x", forHTTPHeaderField: "X-Plex-Version")
            
            request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            
            var components = URLComponents()
            components.queryItems = [
                URLQueryItem(name: "user[login]", value: login),
                URLQueryItem(name: "user[password]", value: password)
            ]
            if let bodyStr = components.percentEncodedQuery {
                request.httpBody = bodyStr.data(using: .utf8)
            }
            #if DEBUG
            print(request.allHTTPHeaderFields ?? "")
            #endif
            
            let task = session.dataTask(with: request) { (data, response, error) in
                
                if let error = error {
                    
                    completion(nil, PlexError.tokenError(error.localizedDescription))
                    
                    return
                }
                
                guard let httpResponse = response as? HTTPURLResponse,
                    (200...299).contains(httpResponse.statusCode) else {
                        let httpResponse = response as! HTTPURLResponse
                        let statusCode = httpResponse.statusCode
                        NSLog("---- bad http response: '\(statusCode)' for url '\(url.absoluteString)'")
                        print(httpResponse.allHeaderFields)
                        
                        completion(nil, PlexError.badResponse(statusCode, "\(url.absoluteString)"))
                        
                        return
                }
                
                if let data = data {
                    
                    do {
                        
                        if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? Dictionary<String, Any> {
                            
                            if let user = jsonResult["user"] as? Dictionary<String, Any> {
                                
                                if let token = user["authentication_token"] as? String {
                                    
                                    // We successfully retieved a token
                                    #if DEBUG
                                    print("Plex token: \(token)")
                                    #endif
                                    
                                    // Save the token to the keychain
                                    KeychainWrapper.standard.set(token, forKey: Self.tokenKey)
                                    
                                    completion(token, nil)
                                }
                            }
                        }
                        
                    } catch {
                        
                        completion(nil, PlexError.tokenError("Failed to decode token: '\(error.localizedDescription)'"))
                    }
                    
                    
                } else {
                    completion(nil, PlexError.tokenError("Failed to get token data"))
                    return
                }
            }
            
            task.resume()
        }
    }

I just discovered that there is official documentation for a federated (OAuth) version of retrieving a user’s server token:

So, I’ll be rewriting my code to use this, so the user doesn’t need to hand over their plex.tv credentials to a third-party app…

1 Like

I’ve implemented a Swift package that provides the necessary iOS code to authenticate using the above procedure.

An example iPhone project that demonstrates its usage is provided here

The direct link to the Swift package is here.

I’ve successfully got a login token. However, not all things seem to work with it. When I try to list playlists I get an empty array. If I replace the token with one aquired by the plex web ui it works, however. Most other things work with the token I’ve aquired too.

I can reproduce this in insomnia just fine without any coding when I do what I did in the app

I sent query params similar to those to https://plex.tv/users/sign_in.json and ofc basic auth header.

X-Plex-Client-Identifier=myamazingplexapp
X-Plex-Product=myamazingplexapp
X-Plex-Version=myamazingplexapp-preview

When I then make a request to <server_url>/playlists it returns an empty array. The server is in my local network and I’m using the local address with http so http://192.168.1.x:32400/playlists

Anybody got an idea on why that might be? Most other things work just fine. Like listing library contents.

I also tried the python api for plex, which Ive more or less used as a documentation. The app I’m writing is actually an android app written in kotlin. Same thing when I give it my user credentials it aquires a token that makes it work. When I use one that was created by my app or insomnia it does also return an empty array for playlists.


Oh, wow I just changed X-Plex-Client-Identifier to an actual UUID and now it works… who would have thought. I assumed my app name for the time being (in developement) is unique enough on my Server. The behaviour is still weird. Everything seems to work until some things don’t and there is no indication on why that might be when using the API.

I’m assuming it having to be an unique value for a client means it needs to be generated on first app start for every individual user and stored somewhere? Appears this should be unique for the app not the user using the app, judging by solutions above.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.