Table of contents

Use the VSP with your service

GOV.UK Verify uses SAML (Security Assertion Markup Language) to securely exchange information about identities. A Relying Party can use the Verify Service Provider (VSP) to generate SAML to send to the Verify Hub, and translate the SAML responses returned by the Verify Hub.

This tutorial explains how to integrate with the VSP in your local environment using the Compliance Tool as a placeholder for the GOV.UK Verify Hub. You will find out how to to send a SAML AuthnRequest and how to handle a SAML Response.


To be able to follow this tutorial you must:

You can check if your VSP is set up properly by doing a GET request to the admin/healthcheck endpoint to confirm it is running correctly.

Step 1: Generate a SAML AuthnRequest

Make a POST request to the /generate-request endpoint to generate a SAML AuthnRequest. The request body must also contain the level of assurance for your service.

Example call

> POST /generate-request HTTP/1.1
> Content-Type: application/json
> { "levelOfAssurance": "LEVEL_2" }

Example response

    "samlRequest": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8vY29tc...",
    "requestId": "_f43aa274-9395-45dd-aaef-25f56f49084e",
    "ssoLocation": ""

The parts of the response represent:

  • samlRequest - your base64 encoded AuthnRequest
  • requestId - a token that identifies the AuthnRequest. It is used to connect the user’s browser with a specific request.
  • ssoLocation - the URL to send the AuthnRequest to

Step 2: Store the requestId from the response

You will need to access the requestId later in the process to link the identities received from GOV.UK Verify with the correct user.

You must store the requestId securely and link it to the user’s session. We recommend you store the requestId in a secure cookie.

Step 3: Send the AuthnRequest to Compliance Tool

The AuthnRequest is sent via the user’s browser in a form. We recommend you do this by rendering an HTML form and JavaScript to submit it, as per SAML HTTP Post Binding.

The HTML form should:

  • escape inputs - to make sure no symbols or special characters are processed
  • contain JavaScript to auto-post - to automatically send the user on to the Hub
  • include page styling to display if JavaScript is disabled - to prompt users to turn on JavaScript. This should look like your service

Example HTML form from passport-verify

    <form class='passport-verify-saml-form' method='post' action='${escape(ssoLocation)}'>
      <h1>Continue to next step</h1>
      <p>Because Javascript is not enabled on your browser, you must press the continue button</p>
      <input type='hidden' name='SAMLRequest' value='${escape(samlRequest)}'/>
      <input type='hidden' name='relayState' value=''/>
      <button class='passport-verify-button'>Continue</button>
      var form = document.forms[0]
      form.setAttribute('style', 'display: none;')
      window.setTimeout(function () { form.removeAttribute('style') }, 5000)
    <style type='text/css'>
      body {
        padding-top: 2em;
        padding-left: 2em;
      .passport-verify-saml-form {
        font-family: Arial, sans-serif;
      .passport-verify-button {
        background-color: #00823b;
        color: #fff;
        padding: 10px;
        font-size: 1em;
        line-height: 1.25;
        border: none;
        box-shadow: 0 2px 0 #003618;
        cursor: pointer;
      .passport-verify-button:hover, .passport-verify-button:focus {
        background-color: #00692f;

The response from Compliance Tool should contain "status": "PASSED" and a responseGeneratorLocation URL which you can use to access the test scenarios.

Example response from Compliance Tool

    "status": {
      "status": "PASSED",
      "message": null
    "responseGeneratorLocation": ""

If the status is not PASSED, you may need to re-initialise the Compliance Tool. You should also check the Compliance Tool initialisation request matches the VSP configuration.

Go to the URL in responseGeneratorLocation using your browser. The response will contain the test scenarios for possible responses.

Example test scenarios from Compliance Tool

  "id" : "_6817b389-4924-479c-9851-db089c4e639c",
  "testCases" : [ {
    "executeUri" : "",
    "id" : 10,
    "title" : "Verified User On Service With Non Match Setting",
    "description" : "Issues a successful response where the user has been successfully verified."
  }, {
    "executeUri" : "",
    "id" : 11,
    "title" : "No Authentication Context Response With Non Match Setting",
    "description" : "Issues a response with NoAuthnContext status. This happens when the user cancels or fails to authenticate at an appropriate level of assurance."
  }, {
    "executeUri" : "",
    "id" : 13,
    "title" : "Authentication Failed Response",
    "description" : "Issues an Authentication Failed response. The user was not authenticated successfully."
  }, {
    "executeUri" : "",
    "id" : 14,
    "title" : "Fraudulent match response with assertions signed by hub",
    "description" : "Issues a response with an assertion signed with the hub's private key. Your service should return an error to the user because your service should only trust assertions signed by your matching service adapter."
  } ]

Access the URL for a particular scenario to test that your service can handle that particular response.

Step 4: Receive the SAML Response

The SAML Response will be submitted to the URL you specified when initialising the Compliance Tool, via the user’s browser. For example, passport-verify-stub-relying-party uses /verify/response. The SAML Response will be URL form encoded, application/x-www-form-urlencoded.

Example form body submitted from the user’s browser


Step 5: Translate the SAML Response into JSON

Send a POST request to /translate-non-matching-response to translate the SAML Response into JSON.

The call must contain:

  • samlResponse - the base64 encoded SAML from the Compliance Tool you got in Step 4
  • requestId - the token you stored in Step 2
  • levelOfAssurance - to validate that the user meets the minimum level of assurance you have requested

Example call

> POST /translate-non-matching-response HTTP/1.1
> Content-Type: application/json
  "samlResponse" : "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8vY29tc...",
  "requestId" : "_64c90b35-154f-4e9f-a75b-3a58a6c55e8b",
  "levelOfAssurance" : "LEVEL_2"

Example successful response

> HTTP/1.1 200 OK
> Content-Type: application/json
    "scenario": "IDENTITY_VERIFIED",
    "pid": "etikgj3ewowe",
    "levelOfAssurance": "LEVEL_2",
    "attributes": pie

Step 6: Handle the JSON response

When you receive an HTTP 200 response, with one of 4 scenarios:

Scenario Description
IDENTITY_VERIFIED The user successfully completed the Verify journey.
CANCELLATION User opted to cancel verification at some point in their journey. Response will be empty.
AUTHENTICATION_FAILED User could not be identified by identity provider.
REQUEST_ERROR Internal error. Contact

In the IDENTITY_VERIFIED scenario, the response will also contain:

  • pid - a unique identifier for a user
  • levelOfAssurance - the level of assurance the user verified at
  • attributes - information about the user’s identity

Your service can now use the provided identity or error messages to further guide the user in their journey in your service.