Third Party Developer Blog

Jul
8

ESI Step by Step - SSO to Authenticated Calls

Team Tech Co | 2017-07-08 00:01

This blog post is part of a series of blogs examining best practices for ESI development. Each blog will be published on the 8th of each month during the journey towards XML API and CREST’s termination date. The legacy APIs will be terminated on May 8th, 2018, or earlier if metrics signal a trivial level of usage.


Last month, we talked about auto generating a client library for ESI using Swagger Codegen. This month we will expand on this by stepping through the SSO authentication flow and using that authentication to make a call to the ESI endpoint /characters/{character_id}/standings. Typically, one would set up a server to handle most of this process, but today we're going to do it manually with curl, to lay out the steps in the process. It is assumed the reader already knows what SSO is, if a refresher is needed look at our third party documentation on it here.

This blog assumes that you have worked through last month's blog on Swagger Codegen and a workspace with your work from this blog available. We're going to be adding a few lines of code to it at the end of this blog.

SSO Registration

Before the authentication flow can be implemented you must first go to EVE Online's developer site and login with your EVE Online account. Once you've logged in, click on Applications on the top toolbar. View and accept the developers license if prompted and on the next page click Create New Application. Once here there will be a page to fill in a Name and Description for your application. Because this blog will focus on getting access to the /characters/{character_id}/standings endpoint, name the application "Character Standings Checker" and set the description to "A simple tool designed to check the standings of characters.".

Next, for Connection Type, pick the radio button next to Authentication & API Access. This particular connection type is used for access to our third party APIs. Once this connection type is selected, a list of available scopes will be presented. Navigating to the /characters/{character_id}/standings swagger documentation and hovering over the red "!" sign will show that the esi-characters.read_standings.v1 scope is needed for this particular endpoint:

Find the esi-characters.read_standings.v1 scope in the Available Scopes List box and click on it. It will move over to the Requested Scopes List box when clicked on.

In the Callback URL field, enter the following URL: "http://localhost/oauth-callback". This is the URL that your users will be redirected to after login, and would typically be pointing at a webserver. As a security measure, the EVE SSO will only redirect users of your application to this callback URL.

Click the Create Application button to finish. Once the application has been created click on View Application and keep this tab open in your browser for later.

Executing the Authentication Flow

EVE Online's SSO uses what's known as the Authorization Code Grant OAuth 2 authorization flow (the RFC for this can be found here). A typical flow would function like this:

  • Log in to EVE Online's SSO using a URL with specific GET parameters.
  • After Logging in, be redirected to a running server at the callback URL that was defined in your SSO application (in this case http://localhost/oauth-callback). This redirection will include an authorization code supplied from EVE Online's SSO as a GET parameter.
  • Inside the server that handles the SSO callback, make a POST request back to EVE Online's SSO with the authorization code provided. This request will use HTTP basic authentication and will use an SSO application client ID as the user and the secret key as the password. The client ID and secret key can be found on the eve developers website, in the browser tab that we suggested to keep open at the end of the SSO Registration process above.
  • Finally, the response given back to the server will contain an access token that can be used to make an authenticated call to ESI.

In the next section, we're going to step through this by hand using curl. Along the way, you're going to see fields labelled {client id} and {client secret}. When you see these, substitute the client ID and client secret from the View Application page on https://developers.eveonline.com that you were viewing at the end of SSO Registration. Throughout this blog, we're going to use {} syntax to indicate "you should remove these brackets and everything between them, and replace it with a suitable value of your own in this place".

Create the log-on URL

The first step is to create a URL that users can follow to begin the logon process. This URL has the following structure:

https://{login server base url}/oauth/authorize?response_type={response type}&redirect_uri={redirect uri}&client_id={client id}&scope={scopes}&state={state}

{login server base url} is the url for the sso server. On tranquility, that url is "login.eveonline.com".

The following query parameters are mandatory:

  • Response type can be 'code' or 'token'. Setting response type to token will cause you to go down an alternate flow called the Implicit Grant, but we want an Authorization Code Grant, so we're going to set it to 'code'.

  • Redirect uri is where the user will be redirected after login. In other OAuth servers this might be a freeform field, but the EVE SSO will return an error if this does not match your clients designated callback, so we're going to set it to "http://localhost/oauth-callback" to match the value we entered during SSO registration.

  • Client ID is your application's client ID from the developers website.

The following query parameters are optional (although we need scopes in this case):

  • Scopes is a list of space-separated scopes that you wish to request for this specific authorization call. It can be a subset of the scopes that were chosen when creating the client. We're going to set it to "esi-characters.read_standings.v1".

  • State is a field that you can use to pass some amount of state information from your client to your server via the SSO. Anything entered in this parameter will be passed on to the redirect URI by the eve SSO if the login succeeds. We don't need it in this case.

Our final URL looks like this:

https://login.eveonline.com/oauth/authorize?response_type=code&redirect_uri=http://localhost/oauth-callback&client_id={client id}&scope=esi-characters.read_standings.v1

This is the URL that you would embed in your website with a "log into eve online" button.

Navigate to this URL in your browser, and log in.

Extract the Authorization Token from the redirected URL

Once you log into the SSO, the browser will be redirected to a URL that looks something like this:

http://localhost/oauth-callback?code=ckEZIa6JUOdoN6ijmqBI...qgpU-SmPsZ0

Typically, a server would receive this as a GET request from the browser, and automatically extract the code, but we're going to just copy it out of the URL manually. Copy the code query parameter value into a text editor.

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.

Exchanging the authorization code for an access token.

To exchange the authorization code for an access token, we need to make a post request with curl.

The URL of the POST request has the following structure:

https://{login server base url}/oauth/token

In addition, the POST request needs headers and a body. We must send an Authorization header and a Content-Type header.

The Content-Type can be application/json or application/x-www-form-urlencoded. We're going to use JSON.

The Authorization header must be the word "Basic" followed by the base-64 encoded string of {client id}:{client_secret}. If the client id was "client_id" and the secret was "clientsecret1", the final Authorization header value would be "Basic Y2xpZW50X2lkOmNsaWVudHNlY3JldDE=". You can see how this was encoded by decoding the string on this website, but you should encode your own secret on your local computer, as it's unwise to input secrets in third party websites.

The body will be a json string, and looks like this:

    {
      "grant_type":"authorization_code",
      "code":"{the authorization code}"
    }

If we make this into a curl request, we end up with this:

curl -XPOST -H "Content-Type:application/json" -H "Authorization:Basic Y2xpZW50X2lkOmNsaWVudHNlY3JldDE=" -d '{"grant_type":"authorization_code", "code":"ckEZIa6JUOdoN6ijmqBI...qgpU-SmPsZ0"}' https://login.eveonline.com/oauth/token

Note that I've used a fake client ID and secret to generate the basic authentication code in this example. Remember to not use the code provided but instead replace it with one generated from your own client id and client secret. Execute the above call with your own authentication string, and the authorization code from the previous step. This call will succeed only once. Attempts to use the code a second time will result in the error "Authorization code not found".

You will get a response that looks like this:

{"access_token":"jZOzkRtA8B...LQJg2","token_type":"Bearer","expires_in":1199,"refresh_token":"RGuc...w1"}

The access token is the part we will need for the next step. Copy the access_token value given to you in the response into a text editor.

Note that access tokens are only valid for 20 minutes, after which you can re-run this step with the same headers and the following body to use the refresh token to get another access token at any time:

    {
      "grant_type":"refresh_token",
      "refresh_token":"{the refresh token}"
    }

Use the access token to get the character ID

We'll need a character ID for the next step. The SSO will provide this information from access tokens.

To get the character ID of a token, make a GET request to the following url:

https://{login server base url}/oauth/verify with the header Authorization: Bearer {access token}

In our case, it's going to be this:

curl -XGET -H 'Authorization: Bearer {access token from the previous step}' https://login.eveonline.com/oauth/verify

Which will return something similar to the following:

{"CharacterID":95465499,"CharacterName":"CCP Bartender","ExpiresOn":"2017-07-05T14:34:16.5857101","Scopes":"esi-characters.read_standings.v1","TokenType":"Character","CharacterOwnerHash":"lots_of_letters_and_numbers","IntellectualProperty":"EVE"}

Copy the character ID into a text editor.

Make an authenticated call.

In the previous blog we set up an environment with a python swagger client, and made an unauthenticated call to ESI. You'll need that environment for the next part of this blog, so if you don't have it set up, work through the codegen blog and return here once it's complete.

Once again, open a python shell in the appropriate virtualenv, and execute the following:

import swagger_client
from swagger_client.rest import ApiException
from swagger_client import Configuration

api = swagger_client.CharacterApi()
api.api_client.set_default_header('User-Agent', 'my-test-agent') # Set a relevant user agent so we know which software is actually using ESI
api.api_client.host = "https://esi.tech.ccp.is"
Configuration().access_token = "{access token}" # fill in your access token here
try:
    response = api.get_characters_character_id_standings({character id}) # fill in the character id here
    print(response)
except ApiException as e:
    print("Exception when calling CharacterApi->get_characters_character_id_standings: %s\n" % e)

And you're done! You've made an authenticated call to ESI.

XML Transition Project Update

Since the last blog, the following XML API endpoints have been ported to ESI:

category XML endpoint ESI parity
char    
. AccountBalance https://esi.tech.ccp.is/latest/#!/Wallet/get_characters_character_id_wallet
. CharacterSheet (not finished)
https://esi.tech.ccp.is/latest/#!/Character/get_characters_character_id
https://esi.tech.ccp.is/latest/#!/Clones/get_characters_character_id_clones
https://esi.tech.ccp.is/dev/#!/Skills/get_characters_character_id_skills
https://esi.tech.ccp.is/latest/#!/Skills/get_characters_character_id_attributes
. ContractBids https://esi.tech.ccp.is/latest/#!/Contracts/get_characters_character_id_contracts_contract_id_bids
. ContractItems https://esi.tech.ccp.is/latest/#!/Contracts/get_characters_character_id_contracts_contract_id_items
. Contracts https://esi.tech.ccp.is/latest/#!/Contracts/get_characters_character_id_contracts
. WalletJournal https://esi.tech.ccp.is/dev/#!/Wallet/get_characters_character_id_wallet_journal
. WalletTransactions https://esi.tech.ccp.is/latest/#!/Wallet/get_characters_character_id_wallet_transactions
eve    
. CertificateTree Deprecated
. RefTypes All expanded in relevant endpoints

Complete and up-to-date list can be found here.

back