Endpoints

This page collects some general workings of endpoints in SPiD.

Response container

All API responses come with a common container. The container object (when the response format is JSON) contains some information about the API itself, and a standard way of retrieving the endpoint return type or information about API failures.

More details are available from the response container type specification. Below is a sample output:

{
    "name": "SPP Container",
    "version": "0.2",
    "api": 2,
    "object": "Utility",
    "type": "element",
    "code": 200,
    "request": {
        "reset": 3537,
        "limit": 3600,
        "remaining": 3
    },
    "debug": {
        "route": {
            "name": "Utility:describe",
            "url": "/api/2/describe/{object}",
            "controller": "Api\/2\/Utility.describe"
        },
        "params": {
            "options": [],
            "where": { "object":"User" }
        }
    },
    "meta": null,
    "error": null,
    "data": { ... }
}

Response formats

Many SPiD endpoints can respond with various response formats. The format used is determined by the URL suffix/file name extension used. Refer to individual endpoint API docs for information on supported formats.

JSON

JSON is the most commonly supported response type used in SPiD. It is also the default format used by most endpoints. To force the use of JSON, use the .json suffix on URLs:

https://identity-pre.schibsted.com/api/2/user/42.json

JSONP

JSONP is supported by most endpoints that support JSON. JSONP ("JSON with padding") is JSON wrapped in a function call, served as JavaScript. Because JavaScript can be served from mixed sources on a web page, JSONP circumvents certain security restrictions and complexity associated with other forms of cross-origin resource sharing, at the expense of higher security risk.

To force the use of JSONP, use the .jsonp suffix on URLs:

https://identity-pre.schibsted.com/api/2/user/42.jsonp

The response will be executable JavaScript:

callback({ ... });

The callback function will be called callback by default. To change its name, use the callback query parameter:

curl https://identity-pre.schibsted.com/api/2/users.jsonp?oauth_token=TOKEN&\
     callback=doit

Which will return something like

doit([...]);

Other formats

A few SPiD endpoints support other response types:

  • XML: The extensible markup language with pointy brackets
  • TGZ: Packaged and compressed files
  • CSV: Comma separated values
  • PNG: An image file

To force their use, provide the lower case abbreviation as the URL suffix, e.g.:

https://staging.payment.schibsted.no/api/2/user/42.xml

Signed responses

Some API endpoints return signed data. This can be enabled upon request, and is enabled by default on the payment API endpoints.

There are 2 fields in the API container that indicate a signed response. The sig parameter is a signature string, while algorithm is the algorithm used to encrypt the signature string.

The signature ensures that the data you are receiving is actually sent by SPiD. It is signed using your signature secret which is only known to you and SPiD. Without this secret, third parties cannot modify the sig parameter without also invalidating the data in the provided response.

The response data must be Base64 URL decoded to JSON before it can be used.

To experiment with signature verification, try this sample response:

{
    "name": "SPP Container",
    "version": "0.2",
    "api": 2,
    "object": "Payment",
    "type": "collection",
    "code": 200,
    "request": {
        "reset": 3600,
        "limit": 360000,
        "remaining": 360000
    },
    "debug": {
        "route": {
            "name": "Search transactions",
            "url": "/api/2/payments",
            "controller": "Api/2/Payment.payments"
        },
        "params": {
            "options": [],
            "where": {
                "clientRef": "521b89dc836b9"
            }
        }
    },
    "meta": {
        "count": 1,
        "offset": 0
    },
    "error": null,
    "data": "eyJvYmplY3QiOiJvcmRlciIsImVudHJ5IjpbeyJvcmRlcl9pZCI6IjMwMDAxNCIsImNoYW5nZWRfZmllbGRzIjoic3RhdHVzIiwidGltZSI6IjIwMTItMDktMzAgMTM6MjE6NDMifSx7Im9yZGVyX2lkIjoiMzAwMDE2IiwiY2hhbmdlZF9maWVsZHMiOiJzdGF0dXMiLCJ0aW1lIjoiMjAxMi0wOS0zMCAxMzoyMTo0MyJ9XX0",
    "algorithm": "HMAC-SHA256",
    "sig": "GTUVPjN1LzdyU1qwHjnMKS2oNxckfGzXWA6WOGHVOOg"
}

// Sign secret: a274de

Decoding responses

PHP

$container is a full response container.

<?php
function base64_url_decode($input) {
    return base64_decode(strtr($input, '-_', '+/'));
}

$data = json_decode(base64_url_decode($container['data']), true);
$sig = base64_url_decode($container['sig']);

To verify the data, recreate the payload with the signature using the specified algorithm and your client signature secret. SHA256 is the default hashing algorithm.

<?php
// $sign_secret = "a274de";
$expected_sig = hash_hmac('sha256', $container['data'], $sign_secret, true);

if (hash_equals($sig, $expected_sig) {
    echo "Authenticity of data verified\n";
} else {
    echo "Authenticity of data cannot be verified. Someone is doing something naughty!\n";
}

Java

import no.spid.api.client.SpidApiResponse;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

class ResponseVerifier {
    private static byte[] base64UrlDecode(String str) {
        return Base64.decodeBase64(str.replace("-", "+").replace("_", "/").trim());
    }

    public static boolean verify(SpidApiResponse response, String signSecret) {
        byte[] signature = base64UrlDecode(response.getResponseSignature());
        byte[] payload = base64UrlDecode(response.getJsonValue("data"));
        byte[] generatedSignature = null;

        try {
            SecretKeySpec sks = new SecretKeySpec(signSecret.getBytes("UTF-8"), "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(sks);
            generatedSignature = mac.doFinal(response.getJsonValue("data").getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException ex) {
            throw ex;
        }

        return MessageDigest.isEqual(generatedSignature, signature);
    }

    public static String decode(SpidApiResponse response, String signSecret) {
        if (!verify(response, signSecret)) {
            throw new Error("Data has been tampered with!");
        }
        return new String(base64UrlDecode(response.getJsonValue("data")));
    }
}

You can also use provided SpidSecurityHelper.decryptAndValidateSignedRequest() method.

Encoding

The SPiD API expects all data to be UTF-8 encoded.

All incoming data is actively checked for valid UTF-8 sequences. For historical reasons, if an invalid sequence is found, the data is presumed to be ISO-8859-1 and converted accordingly to UTF-8. You should not rely on this feature, but instead always use UTF-8.

Pagination

Pagination is supported in many SPiD endpoints. There are four parameters that control paging:

  • limit: The maximum number of results to return. May not exceed 1000.
  • offset: How many results to skip over.
  • until: Entries newer than this are excluded from the results.
  • since: Entries older than this are excluded from the results.

There is a hard limit of 1000 results. Larger result sets will be truncated.

In rapidly growing result sets, using since instead of offset is recommended to avoid overlaps between pages.

Both until and since accept several time formats; timestamps, dates and even fuzzy formats like yesterday and first day of January. Read more about supported date and time formats.

Not all parameters are available for all endpoints. See individual endpoint API docs for details.

Selection

By default, responses will contain full object descriptions, with most or all object properties. If you only need certain properties, you can trim the result by using the fields query parameter. As an example, this is how you would request a certain user with only the firstName, displayName and photo properties included in the response:

GET /user/johndoe@example.com?fields=firstName,displayName,photo

Sorting

For endpoints that return collections, you can sort the results using the sort request parameter in a map-like manner. You may specify multiple sorting properties, and for each one whether to sort ascending (asc) or descending (desc).

Example:

GET https://login.schibsted.com/api/2/orders?sort[status]=desc&sort[updated]=asc

Filters

Many endpoints accept the filters request parameter. It can be used to provide one or more filters, which will affect how the endpoint behaves. The two main types of filters are:

  1. Status filters - these filters (such as active, verified, deleted etc) allow you to control the status of returned items. If you specify multiple status filters, they will be OR-ed, e.g. filters=active,deleted will return all items that are active and all items that are deleted.

  2. Type filters - these filters (such as bundle, subscription etc) allow you to control what kind of items are returned. Multiple type filters are OR-ed, e.g. filters=bundle,subscription will return every product that is either a bundle or a subscription.

Combined status and type filters are AND-ed together: filters=active,bundle,subscription will return all active products that are either bundles or subscriptions.

See individual endpoints for supported filters.

Locales

SPiD supports localized content for some endpoints. Whenever that is the case, the available locales are:

  • nb_NO - For Norwegian BokmÃ¥l
  • sv_SE - For Swedish
  • en_US - For American English (default)

Although rare, you may run into content that has not been localized. In these cases, you will get English content back.

Parameters

Some query parameters support multiple values. Whenever this is the case, you have a few options for providing these values.

You can send a single value:

key=single-value

...you can provide multiple values in a comma-separated list:

key=one-value,other-value

...and you can provide multiple values in array-like notation, supported by some server-side environments (PHP, Rails, others):

key[]=one-value&key[]=other-value

Java request example

Java examples throughout the endpoint API docs are excerpts, to spare you from wading through repeated boilerplate. To put it all in context, the following is a full example, including import statements.

import no.spid.api.client.SpidApiClient;
import no.spid.api.exceptions.SpidApiException;
import no.spid.api.exceptions.SpidOAuthException;
import no.spid.api.oauth.SpidOAuthToken;

import org.apache.commons.codec.binary.Base64;

public class GettingStarted {

    public static void main(String[] args) {
        if(args.length < 3) {
            System.out.println("Run with arguments <clientId> <clientSecret> <email>");
            System.exit(0);
        }
        final String clientId = args[0];
        final String secret = args[1];
        final String signatureSecret = "";
        final String redirectUrl = "http://localhost:8080";
        final String spidBaseUrl = "https://identity-pre.schibsted.com";

        SpidApiClient client = new SpidApiClient.ClientBuilder(
                clientId,
                secret,
                signatureSecret,
                redirectUrl,
                spidBaseUrl
        ).build();

        try {
         SpidOAuthToken token = client.getServerToken();
            final String email = args[2];   // check to see if this email exists
            String base64EncodedEmail = new String(Base64.encodeBase64(email.getBytes()));
            String responseJSON = client.GET(token, "/email/" + base64EncodedEmail + "/status", null).getRawData();
            System.out.println("Status of " + email + ": " + responseJSON);
        } catch (SpidOAuthException e) {
            e.printStackTrace();
        } catch (SpidApiException e) {
            e.printStackTrace();
        }
    }
}

PHP request example

PHP examples throughout the endpoint API docs are excerpts, to spare you from wading through repeated boilerplate. To put it all in context, the following is a full example.

<?php // getting-started.php
require_once('../vendor/autoload.php');

$client = new VGS_Client(array(
    VGS_Client::CLIENT_ID          => $argv[1],
    VGS_Client::CLIENT_SECRET      => $argv[2],
    VGS_Client::CLIENT_SIGN_SECRET => $argv[3],
    VGS_Client::STAGING_DOMAIN     => "identity-pre.schibsted.com",
    VGS_Client::HTTPS              => true,
    VGS_Client::REDIRECT_URI       => "http://localhost:8181/",
    VGS_Client::DOMAIN             => "localhost:8181",
    VGS_Client::COOKIE             => true,
    VGS_Client::API_VERSION        => 2,
    VGS_Client::PRODUCTION         => false
));

$client->auth();
echo var_dump($client->api("/endpoints"));

Table of Contents

Looking for the list of all endpoints? See the API reference.

Help us improve

Did you spot an error? Or maybe you just have a suggestion for how we can improve? Leave a comment, or better yet, send us a pull request on GitHub to fix it (in-browser editing, only takes a moment).

History of this page

Comments/feedback

Do you have questions, or just want to contribute some newly gained insight? Want to share an example? Please leave a comment. SPiD reads and responds to every question. Additionally, your experience can help others using SPiD, and it can help us continuously improve our documentation.