Implementing Single Sign-On with the SPiD API

When you have completed this guide, your users can log into your application via SPiD, and you can access the SPiD API on their behalf.

Overview

This is the flow to log in a user:

  • The user is sent to the SPiD login page along with your client ID.
  • Once the user is logged in, they are sent back to your site with a code.
  • You use the code to fetch user information and set up a session.

This is a simple overview explaining the complete process between the client service (yellow) and SPiD (blue): Single Sign on using redirect flow

We'll look at this in detail in the rest of the guide. If you prefer to just dive in, take a look at these working examples.

Configure your application

These variables change between production and staging environments:

  • Your client ID
  • Your client secret
  • Your client signature secret
  • The base URL to SPiD
  • Your base URL

How you choose to configure your application is up to you, but these variables should not be hard coded.

Get ready to receive the user's login code

The SPiD login page expects a redirect URI back to your site, where the user will be sent after logging in. This is where you'll create the user's local session with the given code.

Let's just set up a basic handler for this now, and we can fill it in later.

Java

@RequestMapping("/create-session")
String createSession(@RequestParam String code) {
    return "redirect:/";
}

PHP

<?php // createSession.php
header("Location: /");

Clojure

(defn create-session [code]
  {:status 302
   :headers {"Location" "/"}})

(defroutes routes
  (GET "/create-session" [code] (create-session code)))

Send the user to the SPiD login page

Once you've got your configuration, you can patch together the URL to the SPiD login page. It's on <spid-base-url>/login, with these parameters:

  • client_id: Yeah, it's your client ID.
  • response_type: Which is always code in this version of the API.
  • redirect_uri: The URI where the user is redirected after logging in.

Patch together and use it for your login link.

NB! redirect_uri has to be a full URL back to your site. The domain also has to match the predefined URI that you have registered with SPiD. Only predefined redirect URIs are accepted by the SPiD login page.

Build login URL

Java

String redirectURL = ourBaseUrl + "/create-session";
String loginUrl = spidClient.getFlowURL("login", redirectURL);

PHP

<?php // index.php
$spidClientConfig[VGS_Client::REDIRECT_URI] = "http://{$_SERVER['HTTP_HOST']}/sso/";
$client = new VGS_Client($spidClientConfig);

$user = isset($_SESSION['user']) ? $_SESSION['user'] : false;

if ($user) {
  echo "Hello " . $user['displayName'] . "! <a href='/logout.php'>Log out</a>";
} else {
  echo "<a href='" . $client->getLoginURI() . "'>Log in with SPiD</a>";
}

?>

Clojure

(def create-session-url (str our-base-url "/create-session"))

(def authorize-url
  (str spid-base-url "/flow/auth"
       "?client_id=" client-id
       "&response_type=code"
       "&redirect_uri=" create-session-url))

The login URL can be served directly to your end users for logging in. As SPiD supports remember me type functionality there is no need for users to make a detour through a local /login URL or similar.

Create an API client with the given login code

When the user finishes logging in with SPiD, they will be redirected back to your application via the redirect URI you provided. The redirect will come with a code. Using this code, you can create a client to communicate with the SPiD API on behalf of the user.

Java

// The client itself is immutable and can safely be shared in a multithreaded environment
Properties prop = loadProperties("config.properties");
spidClient = new SpidApiClient.ClientBuilder(
        prop.getProperty("clientId"),
        prop.getProperty("clientSecret"),
        prop.getProperty("clientSignatureSecret"),
        prop.getProperty("ourBaseUrl"),
        prop.getProperty("spidBaseUrl")).build();

PHP

The SPiD SDK for PHP needs a few config variables:

<?php // config.php.sample
$spidClientConfig = [
    VGS_Client::CLIENT_ID          => "<YOUR CLIENT ID>",
    VGS_Client::CLIENT_SECRET      => "<YOUR CLIENT SECRET>",
    VGS_Client::CLIENT_SIGN_SECRET => "<YOUR CLIENT SIGN SECRET>",
    VGS_Client::STAGING_DOMAIN     => "identity-pre.schibsted.com",
    VGS_Client::HTTPS              => true,
    VGS_Client::REDIRECT_URI       => "http://localhost:8181/",
    VGS_Client::COOKIE             => false,
    VGS_Client::API_VERSION        => 2,
    VGS_Client::PRODUCTION         => false
];

Create the client with the config:

<?php // createSession.php
$spidClientConfig[VGS_Client::REDIRECT_URI] = "http://{$_SERVER['HTTP_HOST']}/sso/createSession.php";
$client = new VGS_Client($spidClientConfig);

$session = $client->getSession(); // this rudely fetches the 'code' from the request itself

Clojure

(defn create-client []
  (spid/create-client client-id client-secret
                      {:spid-base-url spid-base-url
                       :redirect-uri create-session-url}))

Fetch user information and create a session

Use the API client you just created to fetch basic user information, and create a local session with it. You should also make sure to hang on to the client. You'll need it later.

Java

@RequestMapping("/create-session")
String createSession( @RequestParam String code, HttpServletRequest request) throws SpidOAuthException, SpidApiException {
    // Retrieve this user's access token
    SpidOAuthToken token = spidClient.getUserToken(code);
    // Use the access token to get info about the user
    SpidApiResponse response = spidClient.GET(token, "/me", null);
    JSONObject user = response.getJsonData();

    // Save token and info in session
    request.getSession().setAttribute("userToken", token);
    request.getSession().setAttribute("userInfo", user);

    return "redirect:/";
}

PHP

<?php // createSession.php
$user = $client->api('/me');

session_start();
$_SESSION['user'] = $user;

header("Location: /sso/");

Clojure

(defn create-session [code]
  (let [client (create-client)
        token (spid/create-user-token client code)
        user (:data (spid/GET client token "/me"))]
    {:status 302
     :headers {"Location" "/"}
     :session {:token token
               :user user}}))

Log user out

When the user wants to log out, just deleting the local session isn't sufficient. They should also be logged out of SPiD. Otherwise they'll have a hard time logging in as another user, and they will still be logged into SPiD even if they think they have logged out.

To get this right, you should:

  • delete the local session
  • redirect the user to the SPiD logout URL

In addition to the user's access token, you pass along another redirect URI, so that the user is sent back to your site after logging out of SPiD.

Java

@RequestMapping("/logout")
String logout( HttpServletRequest request) throws SpidOAuthException {
    SpidOAuthToken token = (SpidOAuthToken)request.getSession().getAttribute("userToken");
    String logoutURL = spidClient.getLogoutURL( token, "http://localhost:8080");

    request.getSession().removeAttribute("userToken");
    request.getSession().removeAttribute("userInfo");

    return "redirect:" + logoutURL;
}

PHP

<?php // logout.php
session_start();
unset($_SESSION['user']);

$spidClientConfig[VGS_Client::REDIRECT_URI] = "http://{$_SERVER['HTTP_HOST']}/sso/";
$client = new VGS_Client($spidClientConfig);

header("Location: " . $client->getLogoutURI());
?>

Clojure

(defn get-logout-url [request]
  (str spid-base-url "/logout"
       "?redirect_uri=" our-base-url
       "&oauth_token=" (-> request :session :token)))

(defn log-user-out [request]
  {:status 302
   :headers {"Location" (get-logout-url request)}
   :session {}})
(defroutes routes
  ;; ...
  (GET "/logout" request (log-user-out request)))

Working examples

If you're unsure on certain details after reading this guide, do check out these working examples:

Table of Contents

Prerequisites

In order to complete this guide, you need to know your:

  • client ID
  • client secret

You should also have gone through the Getting Started guide, in particular that you have downloaded and installed the appropriate SDK for your platform.

See also

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.