jwilsson/spotify-web-api-php

Authentication example could be better

Closed this issue · 15 comments

This library provides a great and broad abstraction of the Spotify Web API. Thank you for that.

The authentication example is not so good though, and doesn't work the way it's written.

This is how I did it. Note that everything is done in a single script, including all app state.

The try/catch should probably cover more than it does.

define('CLIENT_ID', 'xxxxxxxxxxxxxxxxxxxxxx');
define('CLIENT_SECRET', 'xxxxxxxxxxxxxxxxxxxxxxxx');
define('REDIRECT_URI', "http" . (!empty($_SERVER['HTTPS']) ? "s" : "") . "://" . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME']);
define('SESSION', 'spotify_access_token');

...

$session = new SpotifyWebAPI\Session(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI);

if (isset($_GET['code'])):
    $session->requestAccessToken($_GET['code']);
    $accessToken = $session->getAccessToken();
    $_SESSION[SESSION] = $accessToken;
    header('Location: index.php');
    die();
elseif (!isset($_SESSION[SESSION])):
    $options = ['scope' =>
        [
            'user-read-private',
            'user-read-email',
            'user-read-birthdate',
            'user-top-read',
            'playlist-read-private',
            'playlist-modify-private',
            'playlist-read-collaborative',
            'playlist-modify-public',
            'ugc-image-upload'
        ]
    ];
    header('Location: ' . $session->getAuthorizeUrl($options));
    die();
else:
    $accessToken = $_SESSION[SESSION];
endif;

try {
    $api = new SpotifyWebAPI\SpotifyWebAPI();
    $api->setAccessToken($accessToken);
    $api->setReturnType(SpotifyWebAPI\SpotifyWebAPI::RETURN_ASSOC);

    $me = $api->me();
    $userName = $me['display_name'];
    $userId = $me['id'];
    $userPicture = $me['images'][0]['url'];
}
catch (Exception $ex) {
    unset($_SESSION[SESSION]);
    header('Location: index.php');
    die();
}

Hi!
I'm a bit confused, which example are you referring to? The one in the README or the one in the Authorization Using the Authorization Code Flow article? And what seems to be the problem with the example you're referring to?

I'd be happy to improve the docs but I need something more concrete :)

Sorry, I just remembered. There's a third example too, in the Getting Started article. Is it that one you're referring to?

I meant https://github.com/jwilsson/spotify-web-api-php/blob/master/docs/examples/access-token-with-authorization-code-flow.md

It's not wrong per se, rather not complete: It doesn't indicate how to retain state (initially I thought it did that within the SDK). I've further modified it to allow authentication after clicking a button (earlier the first thing the user had to do was to log in, which probably scared away users), but it's roughly the same:

Is Session() always needed?

$session = new SpotifyWebAPI\Session(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI);

if (isset($_POST['logout'])):
    unset($_SESSION[SESSION]);
endif;

if (isset($_POST['login'])):
    $options = ['scope' =>
        [
            'user-read-private',
            'user-read-email',
            'user-read-birthdate',
            'user-top-read',
            'playlist-read-private',
            'playlist-modify-private',
            'playlist-read-collaborative',
            'playlist-modify-public',
            'ugc-image-upload'
        ]
    ];
    header('Location: ' . $session->getAuthorizeUrl($options));
    die();
endif;

if (isset($_GET['code'])):
    $session->requestAccessToken($_GET['code']);
    $_SESSION[SESSION] = $session->getAccessToken();
    header('Location: /');
    die();
endif;

$accessToken = isset($_SESSION[SESSION]) ? $_SESSION[SESSION] : false;

...

                    if ($accessToken !== false):
                        try {
                            $api = new SpotifyWebAPI\SpotifyWebAPI();
                            $api->setAccessToken($accessToken);
                            $api->setReturnType(SpotifyWebAPI\SpotifyWebAPI::RETURN_ASSOC);

                            $me = $api->me();
                            $userName = $me['display_name'];
                            $userId = $me['id'];
                            $userPicture = $me['images'][0]['url'];
                        }
                        catch (Exception $ex) {
                            unset($_SESSION[SESSION]);
                            header('Location: /');
                            die();
                        }

                        // Code for showing user info...
                    else:
                        print "<input class=\"btn btn-primary btn-lg\" type=\"submit\" name=\"login\" value=\"Login to Spotify\" />";
                    endif;

If you are interested, I can PM the link to the site.

Ah, you're right. I'll take a look at clarifying the token management in those examples.

The Session class is needed when first requesting a token, and every time you wish to refresh it.

Then I can probably speed up the code a bit by skipping it normally. Sometimes the Spotiy API is slow/overloaded.

I currently don't do refresh, as users don't stay that long: making playlists based on artists' discographies.

First, of the three Spotify/PHP repos I've seen, this one is, by far, the easiest to read and understand. Your documentation is awesome and well organized. I'm also grateful this issue exists because the initial authorization part was all I really need so all of this discussion is way helpful. I stepped away from Spotify Web API for a few months and, when I got back, every little thing needed authorization and I was struggling to understand it. All my fun & easy projects screeched to a grinding halt. Or, are "screech" and "grind" mutually exclusive?

Regarding the API, if nobody is using the app but me, am I correct in thinking all I need for authorizations are my public/private key? I just want to be able to make my requests again. I just want to access data on artists, albums, and tracks -- absolutely no user data.

I think that should still be simple but I can't figure it out. This is an "issue" with me not understanding (anymore) how to use the API, not with your code.

That worked before I required user logins in my app. Of course adding to a playlist (that I needed) didn't.

Yes, the library is great. It even converts arrays to comma-separated strings, which it took a while before I realized.

Thanks for the kind words guys!

I just pushed an update to the authorization docs which I hope will clarify things. If not, just give me a shout or send a PR with your proposed changes :)

@jotasprout I'm guessing you've seen the Authorization Using the Client Credentials Flow article? That should contain everything you need to know. If you have any other questions, please open a new issue and I'll be happy to help out.

Nope. You guessed wrong. I totally thought "Authorization Flow" was what I needed and so I was trying to figure out which tiny piece(s) I needed. "Client Credentials" looks perfectly appropriate and I can't wait to try it later today.

If there's no need for Spotify user login (depending on your use case), client credentials flow is the way to go. Don't forget to save the access token across page loads.

You don't need tiny pieces. You need the lot.

It works! It works! It totally works!

Then try the authorization code flow (if you intend to affect user data). It's not much harder, except there are more states, and if you want to keep the token for long you need to use the refresh token. In my tests the access token seemed to always last an hour, and my app is fine with that, so I currently don't use the refresh token. The app does a "soft fall" when/if the token expires.

Nope. I have no intention of affecting user data but thanks.

I'm glad we got this figured out!

Since it looks like we're done here, I'm gonna close this issue. If you have any further questions or want it reopened, just let me know.