How to SSO with php?

I’m trying to write an application on a LAMP stack. I’ve made SSO work before, but I seem to just not understand exactly what the f is going on.

I’m trying to write in php. Let me explain the concepts I think I understand and please correct me where I am wrong.

Setting up the application at the developer site and building the link to the login page is simple enough. I have an HTML page with:

<html>
  <body>
    <a href="https://login.eveonline.com/oauth/authorize?response_type=code&redirect_uri=https://mysite.com/myApp/myCallback.php&client_id=MY_APP_ID&scope=publicData%20esi-killmails.read_killmails.v1">Log in with Eve Online</a>
  </body>
</html>

EZ

The user is directed to login at CCP’s website and if successful my callback url is called with a GET value of code=??

That URL looks like:

https://mysite.com/myCallback.php?code=SOME_CODE

As I understand it, this gives me an almost useless one time use authorization code. The only thing this is good for is retrieving 2 more codes that the API could have just given me when the user authenticated.

Whatever.

I try to use this key to get an Access Token and a Refresh Token.

<?php
//there should be a get field with $code in it.  The code in this URL is a one-use-only authorization code that can be exchanged with the SSO for an Access token and a refresh token.

$code = $_GET['code'] ;
//Headers payload
    $headerData = array(
        //Client ID and Secret Key should be environment variables.
        //They are hardcoded here to rule out issues with incorrect values
        "Authorization:Basic " . base64_encode("HARD_CODED_CLIENT_ID:HARD_CODED_SECRET_KEY"),
        "Content-Type:application/json",
        'Host:login.eveonline.com'
    );
    //Body payload
    $bodyData = array(
        "grant_type" => "authorization_code",
        "code" => $code ,
    );
    //We have built out the request and now we just need to execute it.

    //Curl exec
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,"https://login.eveonline.com/oauth/token");
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($bodyData));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
    curl_close($ch);

    //Server response json decode & display
    $result = json_decode($result, true);
    //print_r($result);

Now what?

Well I can save the Access Token and the Refresh Token SSO replied with, lets do that.

$access_token = $result['access_token'] ;
$refresh_token = $result['refresh_token'];

I don’t understand what happens next.

Am I supposed to be able to use the Access Token to query ESI now?
What does the refresh token even do?

“Save” means you store it in some database so you could retrieve it for later invocations of your pages.
Also I strongly recommend you include some kind of user identification in a callback URL.

AccessToken have 20 minutes lifetime and you can use it, then you need to use refreshToken to get new accessToken.

That is starting to make sense to me. Access token is used for actually getting information from ESI.

Does the refresh token last until the user removes the applications permission?
If I use a refresh token to get a new access token is the refresh token I used now invalid? Or does it continue to be valid?

I think those are the final two thing’s I’m failing to understand.

Also I strongly recommend you include some kind of user identification in a callback URL.

Is this having to due with the state variable? I plan on learning how to use this correctly next.

This is because when callback URL is called, you get an access code, but no indication of its relation to the user on your site. If your visitor’s browser would send the identification cookies on such redirect, it’s all right, but in reality, cross-domain redirects may not be treated so well in some paranoid configurations. Simply said, save yourself the trouble, make sure you get the right information all the time.

1 Like

Yes, the refresh_token last until the user revoke it, If it’s not used for 1 month it will also be revoked. There is also SSO endpoints to revoke a refresh token that you can use if the user decide to delete the account at your site.

Reading the entire SSO documentation is a great way to understand all of it. If you do not, you’re much better off using a library for it, as it’s possible to make mistakes that make the code unsafe (not using state etc.)

1 Like

Based on SSO Endpoint Deprecations - EVE: Developers, you should implement things assuming the refresh token changes every time you use it. This will make you future proof when refresh token rotation is implemented.

2 Likes

Thank you for the great answers.

I’m going through that documentation link and trying to get code to work along the way.

What is the importance of the first Authorization Code you get? I think there is something fundamentally important there that I don’t understand.

Why not just callback with the access token and the refresh token in the first place? Am I supposed to be doing something with that Authorization Code other than immediately getting an access token and forgetting the Authorization Code ever existed?

And a couple of generic suggestions:

  1. Do not generate “Host” header by hands unless you know what you are doing. This header WILL be generated by cURL itself as needed.
  2. Always set both CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT. Default timeouts are in range of minutes, your request would die long before you get a reasonable reply from the network, if something wrong happens in between.
  3. Do not curl_close() explicitly. This is needed very rarely, and as long as you kep the handle open and remote server supports keepalives, you would save on connection times.
  4. Use a wrapper. curl/curl or anrdaemon/net-browser

F.e. your code with the latter would look like

$headers = [
    "accept" => 'Accept: application/json, */*;q=0.1',
    "auth" => "Authorization: Basic " . what(gives),
    "content" => "Content-Type: application/json",
];
$curl = new AnrDaemon\Net\Browser([
  CURLOPT_CONNECTTIMEOUT => 5, // Timeout to open connection to a server
  CURLOPT_TIMEOUT => 20, // Timeout to read data from server
  //CURLOPT_SSL_VERIFYPEER => false, // Can be used for debugging purposes
  //CURLOPT_PROXY => $proxy, // Setup proxy if necessary
  //CURLOPT_HTTPPROXYTUNNEL => true,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_ENCODING => '', // Cause cURL to send all supported content-transfer-encodings at once
  CURLOPT_HTTPHEADER => $headers,
]);

$result = $curl->post("https://login.eveonline.com/oauth/token", json_encode($bodyData));
$result = json_decode($result, true);

$headers["auth"] = "Authorization: Bearer {$that_token}";
$curl->setOpt([CURLOPT_HTTPHEADER => $headers]);
$result = $curl->get("https://api.eveonline.com/latest/endpoint") ;
var_dump($result);
1 Like

use the oauth2 endpoints, not the oauth ones. as the oauth ones will be going away, with no additional notice. (the notice was 'at some point after november 1st, these go away)

As for why you get the first token:

CCP do not give you that first token. They give the client that token. Who then passes it to you. That all happens in the client’s browser.

The full cycle is:
You send the client (a users web browser, for example) a url to go to (the login page)
The client then goes through the login process, finally getting a redirect back to you, including that first token.
You then use that token, and some unique identifiers, to get the actual access and refresh tokens (the first time)

After this, you can use the refresh token to get a new access token on demand, without involving the client.

1 Like

Might be worth looking into just use some library to handle it. E.g. Basic Usage - OAuth 2.0 Client. Can just get away with using the GenericProvider type and plugging in the values.

1 Like

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