CIDM Wrapped Developer Guide

Contents

Intended Audience
How to use this guide
Adding the CIDM Wrapped components into your webpages
Intended Audience
How to use this guide
Adding the CIDM Wrapped components into your webpages
Configuring the $jc.qgcidm object (inc. specifying attributes)
Accessing attribute data on the browser-side
Event driven attribute population (i.e. tapping into the on-login and on-logout callback chain)
Identity verification features
Managing Access Tokens
Validating Access Tokens on the Server-side
Calling the Customer Attributes API from your Server Side

Intended audience

This developer guide / walkthrough of CIDM Wrapped is aimed specfically at front-end web developers. It is expected that the reader has a basic understanding of JavaScript, JSON, JQuery, REST services, HTML and CSS.

How to use this guide

Ideally, you should run this HTML file as 'index.html' on http://localhost:8080 using a local server (e.g node.js / apache Tomcat etc.). CIDM Wrapped requires specific 'callback' URLs, and localhost:8080/index.html is one of the allowed URLs in our test environment. By starting on localhost:8080 you can begin building out your solution design immediately without having to wait for client keys, domain white-listing and allocation of official callback URLs (this will obviously come later).

Adding the CIDM Wrapped components into your webpages

Add the following snippet to the head html, this addresses CIDM Wrapped css and JavaScript dependencies.

<link rel="stylesheet" type="text/css" href="https://static.auth.staging-services.qld.gov.au/widget/CIDM/profile/latest/css/cidm.css"> <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> <script src="https://use.fontawesome.com/d9e3b684a5.js"></script>

Then add the following snippet to the body of your html, you can externalise this as a .js file to reduce clutter

<script type="text/javascript"> var cidmLoaderBaseUrl = 'https://static.auth.staging-services.qld.gov.au/widget/CIDM/profile/latest'; requirejs([ cidmLoaderBaseUrl + '/cidm-wrapped-loader.js'], function() { requirejs(['cidm-neo', 'cidm-utils', 'es6-promise','jquery'], function(neo, utils, es6Promise, $) { $(document).ready(function() { $.qgcidm.initialise({ auth_domain: 'auth.staging-services.qld.gov.au', attributes_api_url:'https://api-customer-attributes.auth.staging-services.qld.gov.au/v1', attributes_api_key:'c93B17fVMbaagCAeibtgC6nbiM9ZbJkD2ST1lnfD', clientID: '8aqIFVFNrhpws8dv3W5C3FANUMfiqzK7', level: 'Level_1', returnTo: window.location.href, logoutUrl: window.location.href + '?status=loggedout', faq: "https://identity.qld.gov.au/help/faq.html", authoritative_attributes: { SharingPreference: true, Level: true, QID:true, DateOfBirth:false, Email:true, UserId:true, AuthenticationMethod:true, FirstName:false, FamilyName:false, MiddleName:false }, self_asserted_attributes: { Name:true, Picture:true, AustralianBusinessNumber:false, SamlSubjectId:false, VANguardCredentialType:false, ABAUserId:false, ABAUserName:false, AUSkeyCommonName:false, AUSkeyEmailAddress:false, AUSkeyGivenNames:false, AUSkeySBRPersonId:false, AUSkeyStaleCRLMinutes:false, AUSkeySurname:false } }); $('#qgcidm-avatar').avatar(); // make the id=qgcidm-avatar an avatar for the login widget $.qgcidm.enable(); //uncomment below to see how to tap into the Login and Logout callback chain /* $('#attribute-data') //whatever id on the page you are wanting to modify .onLogin(function(me) { //show attribute data $(me).text(JSON.stringify($jc.qgcidm.profile.attributes, undefined, 3)); }) .onLogout(function(me) { // hide the attribute data $(me).text('You have logged out, attributes will display here after login..'); }); //a different example of using the _onLogin.add feature (i.e. not releated to updating elements on the page) $jc.qgcidm._onLogin.add(function() { console.log('You have now accessed the QID at the optimal time: ' + $jc.qgcidm.searchAttribute('QID')); }) $jc.qgcidm._onLogout.add(function() { console.log('You are now in control of the runtime after logging out'); }) */ }); }); }); </script>

The profile widget

The profile widget is essentially a set of styled buttons and pre-configured functions which can be placed inside a html element such as a div tag, list item, span tag etc. You simply need to add the id value you set up in the configuration step above to the relevant html element. In this case it is 'qgcidm-avatar'. Please note that there is no in built feature that allows the widget to be swapped dynamically between DOM elements or duplicated on screen. Once logged in, the avatar will either show the user's initials or an image (if the user's social network settings allow access to an image).

A 'perfect world' placement of the login widget is shown in the code below (if requested we can supply a complete barebones version of a login page), feel free to use it when testing out the feature (tip: if you have already added the scripts / includes above to a blank html document, then all you need to do is add this tag to your body and you will have a functional widget):

<div id="qgcidm-avatar" style="text-align:right;width:100%;background:gray;"></div>

Obviously, your app or site is not this simple, you may need to shoe-horn the widget into place by leveraging existing DOM elements and applying offsets as per the working example from the code below:

<ul id="tools"> <li class="nav-contact"><a accesskey="4" href="#">Contact us</a></li> <li id="header-search"> <form id="search-form" role="search"> <div> <label for="search-query" class="visuallyhidden">Search website</label> <input accesskey="5" type="search" name="query" id="search-query" size="27" value="" placeholder="Search website" /> <button id="search-button" type="submit">Search</button> </div> </form> </li> <li >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li> <li class="last-child" id="qgcidm-avatar" style="padding-top:4px"></li> </ul>

It is recommended that experienced front-end developers complete the widget integration in alignment with your agencies templates, libraries and responsive display standards.

The example below shows the widget on the current QGov SWE header, it is not responsive and is only a rough view of what widget placement looks like. It is hoped that a SWE implementation template will be available for the widget in early 2018.

Please login with a test account of your own creation using the widget below to begin exploring the features of the $jc.qgcidm client side object.

Making configuration changes to the $jc.qgcidm object

You can configure the the $jc.qgcidm object by passing the config object as an object in the $jc.qgcidm.initialise function (as is done at the top of the page). Alternatively you can modify $jc.qgcidm.config directly and then re-initialize the widget. It is advisable to enable verbose logging in the browser developer tools to get more information about how the $jc.qgcidm object is operating in the background. This is how you access the config:

$jc.qgcidm.config;

	  
	  

The primary use case for configuring the the $jc.qgcidm object after the page loads is changing the attribute request and updating the attribute data. The following commands modify and update attributes:

What attributes are available

Self asserted attributes

These are attributes which have come from social media connections such as Google or Microsoft accounts, they include:

  • Picture
  • FirstName
  • FamilyName
  • MiddleName
  • AustralianBusinessNumber
  • SamlSubjectId
  • VANguardCredentialType
  • AUSkeyCommonName
  • AUSkeyEmailAddress
  • AUSkeyGivenNames
  • AUSkeySBRPersonId
  • AUSkeyStaleCRLMinutes
  • AUSkeySurname
  • Authoritative attributes

    These are attributes which have come from an authoritative source such as CIDM:

    Remove an attribute

    $jc.qgcidm.config.authoritative_attributes.QID = false; $jc.qgcidm.updateProfile();

    Include an attribute

    $jc.qgcidm.config.authoritative_attributes.QID = true; $jc.qgcidm.updateProfile();
    The attibute list will be displayed here

    Accessing attribute data on the browser-side

    We store attributes within the $jc.qgcidm object, the attributes can be accessed as a JSON array after the user logs in. Use the following command:

    $jc.qgcidm.profile.attributes;
    
    		

    Accessing specific attribute values

    We have added a super simple way to get an attribute value in your client application, simply call the $jc.qgcidm.searchAttribute() function as per below:

    $jc.qgcidm.searchAttribute('Email') $jc.qgcidm.searchAttribute('QID') //note this won't show if you have removed it in the previous section of the guide $jc.qgcidm.searchAttribute('AuthenticationMethod')
    Attribute will display here

    Event driven attribute population (i.e. tapping into the on-login and on-logout callback chain)

    You can tap into the login / logout callback chain by adding code under the initialsation as shown in the example above. This page has been enabled, and the output of the demo code below is shown underneath:

    $('#your-element-id') //whatever id on the page you are wanting to modify .onLogin(function(me) { $(me).text('look, I\'ve signed in'); }) .onLogout(function(me) { console.log('look, I've signed out'); });

    This demo code is running in the element below:

    If you are signed in, this text will be updated.

    You can also achieve this by adding something like this after the qgcidm object has initialised (i.e. put it somewhere under the $.qgcidm.initialise({…}); command in the document ready script). This has been added to this document, so you should be able to see these outputs in the console log.

    $jc.qgcidm._onLogin.add(function() { console.log('You have now accessed the QID at the optimal time: ' + $jc.qgcidm.searchAttribute('QID')); }) $jc.qgcidm._onLogout.add(function() { console.log('You are now in control of the runtime after logging out'); })

    It basically adds your function (ahead of the event occurring) to a list of things to do once the user has logged in. You can test it in your console and try clicking between the html view and the console to trigger silent auth. You can also prepare functions to be done once the user has logged out using $jc.qgcidm._onLogout.add(..).

    Identity verification features

    Users can currently provide evidence of identity online with CIDM to achieve Authentication Assurance Level 2 (AAL2) i.e 100 points of ID. You can require that only customers with AAL2 interact with your service. This enables you to build more personalised customer experiences when the identity is proven to 100 points. If the customer allows the sharing of their details, then you also get access to a verifed first name, last name and date of birth (useful for integrating with your back-office systems).

    If you have a service which only wants AAL2 customers then simply specify 'Level_2' as the level in your initialisation configuration (please note that if they don't have AAL2 they will be asked to go through the evidence of identity process by providing 100 points of identity and then they might need to step-up their credential strength if they are using a social connection e.g Google). Customers which fail to reach an AAL2 can still be authenticated to AAL1, so your backend needs to ensure and adequate level has been achieved by checking the 'AuthenticationMethod' attribute of the customer attributes API response or inspect the id_token (if using auth0 libraries instead of our API).

    Promoting a user from AAL1 to AAL2

    It is common by default to log users in as AAL1 (which only requires a QGov account or another approved credential provider such as Google). If a part of your service requires additional evidence of identity (EOI), then you can offer customers to complete an EOI process online. CIDM Wrapped enables a re-authentication flow that will take the user through the EOI process and return them (if successful) with an AAL2 token along with access to the attributes they have agreed to share with your service (e.g. First name, middle name, family name date of birth).

    The $jc.qgcidm object makes this easy, as shown in a simple example below:

    <input type="button" class="your-class" id= "promote-to-aal2" onclick="promoteLoginLevel()" value="Strengthen your QGOV identity to 100 points"> <script> function promoteLoginLevel() { $jc.qgcidm.config.level = 'Level_2'; //increase authentication level $jc.qgcidm.login(); //re-authenticate } </script>

    Why not give it a try?

    If you have already completed EOI and don't require step-up (Google Microsoft) then you will return automatically with an AAL2 token

    Hint: since you are in CIDM test mode you can use the following mock data to complete EOI:

    Since the requested attributes are hardcoded into the configuration on this page, Level 2 attributes will not be accessible upon re-authentication. If your service requires compatibility with both Level 1 and Level 2 users then the Level 2 attributes should be set to 'true'. Alternatively, the attributes requested could be stored in local storage dynamically populated when the user is returned to the page.

    Getting the best of both worlds

    If you offer an online service which can expand its customer offering relative to the identity level of the customer (e.g. My Account) then you can use the 'Level_1' connection as your default level and use the 'AuthenticationMethod' attribute to determine if the customer can be easily promoted to AAL2. The IRAL value indicates if the customer has completed EOI or not:

    The IAAL value indicates the customer's credential strength:

    To determine the customers identity level and promote them automatically, simply run the following code after the customer has logged in:

    if ($jc.qgcidm.searchAttribute('AuthenticationMethod').indexOf('IRAL2')>-1) { $jc.qgcidm.config.level = 'Level_2'; //increase authentication level $jc.qgcidm.login(); //re-authenticate }

    Managing Access Tokens

    CIDM Wrapped uses Access Tokens to authenticate a user session. Access Tokens are a JSON Web Token (JWT) format and operate as bearer tokens, hence anybody else with access to the JWT can impersonate that user in systems that use the JWT to authenticate requests.

    More information about JWTs can be found at jwt.io.

    It is critical that the customer's Access Token is not exposed, leaked or placed on a page where it could be lifted via a cross-site scripting attack.

    The CIDM Wrapped Cross SSO solution maintains state in a centralised manner and distributes JWT through a silent authentication flow constrained by domain whitelisting. Tokens are stored in browser session storage under the namespace 'qgcidm_' to avoid conflicts with any other applications and this prefix is automatically prepended to requests made from the function getStorageItem().

    When customers logout, or their session times out, the JWT is removed from the Session Storage automatically.

    The $jc.qgcidm object provides several functions for interactions with the CIDM Wrapped Session Storage shown below:

    $jc.qgcidm.getStorageItem('access_token'); // a synchronous request e.g. $jc.qgcidm.getStorageItem('access_token'); //note that 'qgcidm_' prefix is not required here $jc.qgcidm.getStorage(a,function(b){}); //e.g. log the JWT: $jc.qgcidm.getStorage('access_token',function(value){ console.log(value)}); //note that 'qgcidm_' prefix is not required here $jc.qgcidm.setStorage(a,function(b){}); //there are no known agency use cases for this function $jc.qgcidm.delStorage(a,b,c..); //e.g. remove JWT and session timers $jc.qgcidm.delStorage('access_token','modified','started'); $jc.qgcidm.session_expired(); //signs the user out of CIDM Wrapped, clears local storage and provides an alert to indicate the session has expired $jc.qgcidm.logout(); //simply signs the user out of CIDM Wrapped and clears local storage.

    Validating Access Tokens on the Server-side

    When a customer signs in using CIDM Wrapped they receive a JWT which can be sent to your application server as part of a customer HTTP request (e.g. a form submission).

    Getting the JWT

    In order to pass the JWT to your server-side, you will first need to get the access to JWT from your browser (this does not apply if you are using the authentication code flow method which enables your server-side to get the token directly).

    <script> var qgcidmJwt = $jc.qgcidm.getStorageItem('access_token'); </script>

    JWT will display here

    Validating the JWT

    There are a couple of ways to go about token validation. You can collect the JWT on your server-side and send it to the CIDM Customer Attributes API to authenticate and identify the the customer making the request. You can also use a standard JWT library that supports jwks token validation (more information here). There are various patterns and symantics around sending JWT in HTTP requests, however they are typically sent in an 'Authorization' header sometimes with or without the prefix 'Bearer ', ultimately it is up to you to manage the transmission of JWT between your client and server.

    It is VERY IMPORTANT that you make sure that the azp parameter in the access token MATCHES your client ID to ensure that the token was generated via your service and not brought into your service from a different agency service. A decoded JWT is shown below, note the client ID in the azp parameter matches this applications client id in the configuration above.

    {
    "https://auth.staging-services.qld.gov.au/session_id": "a43f9dd007871e9d1336c1f80a876105b8014adacf3e484389094d1578c68cc4",
    "https://auth.staging-services.qld.gov.au/partner": "CIDM-AAL1",
    "https://auth.staging-services.qld.gov.au/partner_key": "b336ba070aa3201804e439401bafa5a52533dff935f366b7ab734c11d17156e7a064254691d7433c2a43dabdb41456cb9f6f8131bceb33b09f4f29531b7b08385f5fa591be6323716d5abf5706853172097c968c9249194650f1ec37fba8de9ea852f16eefc97fdb22a91a2e8735f32200fd2bc2452d6a85158be78fbbe2fcf435361f783f6874c3f106",
    "https://auth.staging-services.qld.gov.au/qid": "daed49c8-0f97-44c4-9d63-92877a1c6191",
    "https://auth.staging-services.qld.gov.au/last_login": "2019-01-09T06:01:29.321Z",
    "iss": "https://uat.qld-gov-stg.auth0.com/",
    "sub": "samlp|CIDM-AAL1|daed49c8-0f97-44c4-9d63-92877a1c6191",
    "aud": [
    "https://auth.staging-services.qld.gov.au",
    "https://uat.qld-gov-stg.auth0.com/userinfo"
    ],
    "iat": 1547013696,
    "exp": 1547020896,
    "azp": "8aqIFVFNrhpws8dv3W5C3FANUMfiqzK7",
    "scope": "openid"
    }
    We recommend thorough penetration testing of your implementation prior to production release. CIDM Wrapped is regularly penetration tested and the CIDM team can answer questions you may have in relation to product security.

    Calling the Customer Attribute API from your server-side

    In the browser, the $jc.qgcidm object looks after calling the Customer Attributes API, however if you are verifying a JWT on your server-side then you will need to make a request to the Customer Attributes API directly. This is a REST based API which is aimed to be simple, multi-purposed and compatible with future identity and attribute release frameworks. It handles JWT verification and attribute release in the one API call. Currently it only supports the attributes attached to a CIDM account and will be expanded upon in 2019. You can call the Customer Attributes API using a simple GET request from whichever server-side HTTP request library of your choosing.

    Call the test version Customer Attributes API using the following configurations:

    Endpoint (UAT)

    https://api-customer-attributes.auth.staging-services.qld.gov.au/v1/customer_attributes

    Request Method

    HTTP GET

    Request Headers

    authorization : <Customers JWT Token> x-api-key (this is only for testing, won't work in production) : c93B17fVMbaagCAeibtgC6nbiM9ZbJkD2ST1lnfD

    Query Parameters (any combination of the below, comma separated per attribute type)

    authoritative_attributes=QID,DateOfBirth,Email,UserId,AuthenticationMethod,FirstName,FamilyName&self_asserted_attributes=Name,Picture,FirstName,FamilyName&sign=false

    Response format

    A successful request (200) will return a JSON formatted response similar to that found in $jc.qgcidm.profile.attributes. Anything other than 200 indicates a failure in authentication (typically 4xx) or service availability issue (typically 5xx)

    Specific documentation will be produced to cover the details of the Custommer Attributes API, in the mean time please refer to the Swagger API definition below for more details:

    --- swagger: "2.0" info: version: "2017-11-06T11:38:32Z" title: "CIDM NEO Customer Attributes API" description: | The Customer Attributes API supports the retrieval or both Verified and Unverified Customer Attributes ## Version History: * 0.1 Initial Draft of Customer Attributes API. * 0.2 Updated to have a POST and a GET for Customer Attributes. The GET being a less complex API/request. * 0.3 Removed QID from Urls as they're not available in the JWT * 0.4 Renamed resources to customer_shared and customer_attributes ## Security: 1. Requires AWS API Gateway Key in the x-api-key header 2. Requires Auth0 User JWT in the Authorization header ## Usage: ### Customer Sharing Preferences #### Request: ``` export CUSTOMER_JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2Rldi5xbGQtZ292LWRldi5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDU1MTcyMjA0MzQ1NjM4MDY1NTkiLCJhdWQiOiJLdTFseE1rQlFXY0NETHF1Q1BDNXpvc1cxdmhSTWtvWiIsImV4cCI6MTUxMDYyMTEzNiwiaWF0IjoxNTEwNjEzOTM2fQ.Kt7ifNQlA9IlUoncFRmaGsE5Gx1f8i87ukwl7QdQvRw export API_KEY=cc2f0cbd-be66-431f-8b86-2f8d5ea30625 curl -v -H "Content-Type: application/json" -H "Accept: application/json" -H "x-api-key: ${API_KEY}" -H "Authorization: Bearer ${CUSTOMER_JWT}" https://api-customer-attributes.identity.test-services.qld.gov.au/v1/customer_shared ``` #### Response: ```JSON { "share" : "NOT_ALWAYS" } ``` ### Customer Attributes #### Request: ``` export CUSTOMER_JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2Rldi5xbGQtZ292LWRldi5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDU1MTcyMjA0MzQ1NjM4MDY1NTkiLCJhdWQiOiJLdTFseE1rQlFXY0NETHF1Q1BDNXpvc1cxdmhSTWtvWiIsImV4cCI6MTUxMDYyMTEzNiwiaWF0IjoxNTEwNjEzOTM2fQ.Kt7ifNQlA9IlUoncFRmaGsE5Gx1f8i87ukwl7QdQvRw export API_KEY=cc2f0cbd-be66-431f-8b86-2f8d5ea30625 curl -v -H "Content-Type: application/json" -H "Accept: application/json" -H "x-api-key: ${API_KEY}" -H "Authorization: Bearer ${CUSTOMER_JWT}" https://api-customer-attributes.identity.test-services.qld.gov.au/v1/customer_attributes?authoritative_attributes=QID,DateOfBirth,Email&self_asserted_attributes=Name,UserId,AuthenticationMethod,Picture,FirstName,FamilyName,Nickname&sign=true #Note the below won't actually work on the command line as it's over multiple lines. #Actual request value needs to be a URL encoded string of the JSON object. i.e. JSON.stringify(ATTRIBUTES); export ATTRIBUTES = "\ { "attributes": [ { "name": "GivenName", "definition": { "pedigree": "SELF_ASSERTED", "signed": true, } }, { "name": "FamilyName", "definition": { "pedigree": "AUTHORITATIVE", "signed": true, } }, { "name" : "Senior", "definition": { "source": "FORMULA", "formula" : "Age >= 65" } } ] }" curl -v -H "Content-Type: application/json" -H "Accept: application/json" -H "x-api-key: ${API_KEY}" -H "Authorization: Bearer ${CUSTOMER_JWT}" https://api-customer-attributes.identity.test-services.qld.gov.au/v1/customer_attributes?attributes=${ATTRIBUTES} ``` #### Response: ```JSON { "attributes" : [ { "name" : "GivenName", "value": "Jeremy", "metadata" : [ { "name": "pedigree", "value": "SELF_ASSERTED" } ], "definition": { "source": "ATTRIBUTE", "pedigree": "SELF_ASSERTED", "signed": true, } }, { "name" : "FamilyName", "value": "Ford", "metadata" : [ { "name": "pedigree", "value": "AUTHORITATIVE" } ], "definition": { "source": "ATTRIBUTE",, "pedigree": "AUTHORITATIVE", "signed": true, } }, { "name" : "Senior", "value": "false", "metadata" : [ { "name": "pedigree", "value": "AUTHORITATIVE" } ], "definition": { "source": "FORMULA", "signed": false, "formula" : "Age >= 65" } } ], "signed_attributes" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2Rldi5xbGQtZ292LWRldi5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDU1MTcyMjA0MzQ1NjM4MDY1NTkiLCJhdWQiOiJLdTFseE1rQlFXY0NETHF1Q1BDNXpvc1cxdmhSTWtvWiIsImV4cCI6MTUxMDYyMTEzNiwiaWF0IjoxNTEwNjEzOTM2fQ.Kt7ifNQlA9IlUoncFRmaGsE5Gx1f8i87ukwl7QdQvRw" } ``` ### Verify Customer Attributes #### Request: ``` export SIGNED_ATTRIBUTES_JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2Rldi5xbGQtZ292LWRldi5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDU1MTcyMjA0MzQ1NjM4MDY1NTkiLCJhdWQiOiJLdTFseE1rQlFXY0NETHF1Q1BDNXpvc1cxdmhSTWtvWiIsImV4cCI6MTUxMDYyMTEzNiwiaWF0IjoxNTEwNjEzOTM2fQ.Kt7ifNQlA9IlUoncFRmaGsE5Gx1f8i87ukwl7QdQvRw export API_KEY=cc2f0cbd-be66-431f-8b86-2f8d5ea30625 curl -v -H "Content-Type: application/json" -H "Accept: application/json" -H "x-api-key: ${API_KEY}" -d '{"signed_attributes" :"${SIGNED_ATTRIBUTES_JWT}"}' https://api-customer-attributes.identity.test-services.qld.gov.au/v1/verify_customer_attributes ``` #### Response: ```JSON { "attributes" : [ { "name" : "GivenName", "value": "Jeremy", "metadata" : [ { "name": "pedigree", "value": "AUTHORITATIVE" } ], "definition": { "source": "ATTRIBUTE", "pedigree": "AUTHORITATIVE", "signed": true, } }, { "name" : "FamilyName", "value": "Ford", "metadata" : [ { "name": "pedigree", "value": "AUTHORITATIVE" } ], "definition": { "source": "ATTRIBUTE",, "pedigree": "AUTHORITATIVE", "signed": true, } } } ``` host: "fx7afs9xvf.execute-api.ap-southeast-2.amazonaws.com" basePath: "/v1" schemes: - "https" paths: /health: get: produces: - "application/json" responses: 200: description: "200 response" schema: $ref: '#/definitions/HealthResponse' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 500: description: "Unknown Server Error." schema: $ref: '#/definitions/Error' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" security: - api_key: [] x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" uri: "arn:aws:apigateway::lambda:path/2015-03-31/functions/arn:aws:lambda:::function:/invocations" passthroughBehavior: "when_no_match" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws_proxy" /customer_shared: options: consumes: - "application/json" produces: - "application/json" responses: 200: description: "200 response" headers: Access-Control-Allow-Origin: type: "string" Access-Control-Allow-Methods: type: "string" Access-Control-Allow-Headers: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\"statusCode\": 200}" passthroughBehavior: "when_no_match" type: "mock" get: produces: - "application/json" parameters: - name: "Authorization" in: "header" required: true type: "string" responses: 200: description: "200 response" schema: $ref: '#/definitions/CustomerSharingPreference' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" security: - api_key: [] - auth0JWTAuthorizer: [] x-amazon-apigateway-request-validator: "params-only" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" uri: "arn:aws:apigateway::lambda:path/2015-03-31/functions/arn:aws:lambda:::function:/invocations" passthroughBehavior: "when_no_match" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws_proxy" /customer_attributes: options: consumes: - "application/json" produces: - "application/json" responses: 200: description: "200 response" headers: Access-Control-Allow-Origin: type: "string" Access-Control-Allow-Methods: type: "string" Access-Control-Allow-Headers: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\"statusCode\": 200}" passthroughBehavior: "when_no_match" type: "mock" get: produces: - "application/json" parameters: - name: "authoritative_attributes" in: "query" description: Comma separated list of Attribute Names that are required at an AUTHORITATIVE pedigree required: false type: "string" - name: "self_asserted_attributes" in: "query" description: Comma separated list of Attribute Names that are required at atleast a SELF_ASSERTED pedigree required: false type: "string" - name: "sign" in: "query" description: Boolean flag to indicate if all attributes should be signed required: false type: "string" - name: "Authorization" in: "header" required: true type: "string" responses: 200: description: "HTTP 200 Success Response." schema: $ref: '#/definitions/CustomerAttributesResponse' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 204: description: "No Content. None of the requested Attributes had a value." headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 400: description: "Bad request. No Attributes requested, or at least one invalid Attributes or Formula was provided. Do not attempt the request until the input has been fixed" schema: $ref: '#/definitions/ValidationError' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 401: description: "Unauthorized request. Missing, Invalid, or Expired credentials provided" headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 500: description: "Unknown Server Error." schema: $ref: '#/definitions/Error' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" security: - api_key: [] - auth0JWTAuthorizer: [] x-amazon-apigateway-request-validator: "params-only" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" uri: "arn:aws:apigateway::lambda:path/2015-03-31/functions/arn:aws:lambda:::function:/invocations" passthroughBehavior: "when_no_match" httpMethod: "POST" cacheNamespace: "gkwi0g" cacheKeyParameters: - "method.request.querystring.authoritative_attributes" - "method.request.querystring.self_asserted_attributes" - "method.request.querystring.sign" - "method.request.header.Authorization" contentHandling: "CONVERT_TO_TEXT" type: "aws_proxy" post: produces: - "application/json" parameters: - name: attributes in: body description: Attributes to Retrieve schema: type: object items: $ref: '#/definitions/CustomerAttributesRequest' responses: 200: description: "HTTP 200 Success Response." schema: $ref: '#/definitions/CustomerAttributesResponse' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 204: description: "No Content. None of the requested Attributes had a value." headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 400: description: "Bad request. No Attributes requested, or at least one invalid Attributes or Formula was provided. Do not attempt the request until the input has been fixed" schema: $ref: '#/definitions/ValidationError' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 401: description: "Unauthorized request. Missing, Invalid, or Expired credentials provided" headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 500: description: "Unknown Server Error." schema: $ref: '#/definitions/Error' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" security: - api_key: [] - auth0JWTAuthorizer: [] x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" uri: "arn:aws:apigateway::lambda:path/2015-03-31/functions/arn:aws:lambda:::function:/invocations" passthroughBehavior: "when_no_match" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws_proxy" /verify_customer_attributes: options: consumes: - "application/json" produces: - "application/json" responses: 200: description: "200 response" headers: Access-Control-Allow-Origin: type: "string" Access-Control-Allow-Methods: type: "string" Access-Control-Allow-Headers: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'" method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\"statusCode\": 200}" passthroughBehavior: "when_no_match" type: "mock" post: produces: - "application/json" parameters: - name: signed_attributes in: body description: Signed Attributes to verify schema: type: object items: $ref: '#/definitions/VerifyAttributesRequest' responses: 200: description: "HTTP 200 Success Response." schema: $ref: '#/definitions/VerifyAttributesResponse' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 400: description: "Bad request. Invalid request payload" schema: $ref: '#/definitions/ValidationError' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 401: description: "Unauthorized request. Provided signed_attributes value has been tampered with and must not be trusted" headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" 500: description: "Unknown Server Error." schema: $ref: '#/definitions/Error' headers: Content-Type: type: "string" Access-Control-Allow-Origin: type: "string" security: - api_key: [] x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" uri: "arn:aws:apigateway::lambda:path/2015-03-31/functions/arn:aws:lambda:::function:/invocations" passthroughBehavior: "when_no_match" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws_proxy" securityDefinitions: api_key: type: "apiKey" name: "x-api-key" in: "header" auth0JWTAuthorizer: type: "apiKey" name: "Authorization" in: "header" x-amazon-apigateway-authtype: "custom" x-amazon-apigateway-authorizer: authorizerUri: "arn:aws:apigateway::lambda:path/2015-03-31/functions/arn:aws:lambda:::function:AuthorizationLambda/invocations" authorizerResultTtlInSeconds: 300 type: "token" x-amazon-apigateway-gateway-responses: DEFAULT_5XX: responseParameters: gatewayresponse.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" gatewayresponse.header.Access-Control-Allow-Origin: "'*'" gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" DEFAULT_4XX: responseParameters: gatewayresponse.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" gatewayresponse.header.Access-Control-Allow-Origin: "'*'" gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" x-amazon-apigateway-request-validators: params-only: validateRequestParameters: true validateRequestBody: false definitions: CustomerSharingPreference: type: object required: [share] properties: share: type: string enum: [ALWAYS, NOT_ALWAYS] CustomerAttributeDefinition: type: object required: [source] properties: source: type: string description: Source of Attribute Value enum: [ATTRIBUTE, FORMULA] default: ATTRIBUTE pedigree: type: string enum: [AUTHORITATIVE, SELF_ASSERTED] default: SELF_ASSERTED description: AUTHORITATIVE or SELF_ASSERTED signed: type: boolean default: false description: True if the Attribute will be signed in the Response. forumla: type: string description: Formula for calculating the Attribute Value (i.e. 'Age > 65') where source is FORMULA CustomerAttributeRequest: type: object required: [name] properties: name: type: string description: Name of the Attribute in the Response definition: $ref: '#/definitions/CustomerAttributeDefinition' AttributeMetadata: type: object required: [name, value] properties: name: type: string description: Name of the Metadata Attribute value: type: string description: Value of the Metadata Attribute CustomerAttributesRequest: type: object required: [attributes] properties: attributes: type: array items: $ref: '#/definitions/CustomerAttributeRequest' CustomerAttributesResponse: type: object required: [attributes] properties: attributes: type: array items: $ref: '#/definitions/CustomerAttributeResponse' signed_attributes: type: string description: JWT containing a Payload with each signed Attribute at the top level CustomerAttributeResponse: type: object required: [name, value] properties: name: type: string description: Name of the Attribute in the Response value: type: string description: Value of the Attribute in the Response metadata: type: array items: $ref: '#/definitions/AttributeMetadata' definition: $ref: '#/definitions/CustomerAttributeDefinition' VerifyAttributesRequest: type: object required: [signed_attributes] properties: signed_attributes: type: string description: JWT returned from the attributes request VerifyAttributesResponse: type: object required: [attributes] properties: attributes: type: array items: $ref: '#/definitions/CustomerAttributeResponse' HealthResponse: type: object properties: status: type: string Error: type: object properties: code: type: string message: type: string description: type: string link: type: string ValidationFailures: type: object properties: property: type: string description: Property name failure_reason: type: string description: Failure reason ValidationError: type: object properties: code: type: string message: type: string description: type: string link: type: string validation_failures: type: array items: $ref: '#/definitions/ValidationFailures'

    Centralising your CIDM Wrapped Callback URL

    If you have multiple web pages in your app from which customers can login (e.g a sign-in button in your header template) then you may need to manage customer redirection from a central callback URL. Anymore than 10 callback URLs starts to become an administrative burden, so if you have 10s or 100s of pages in your app or site then you need to set up a single callback URL, and save the return URL in local storage for collecting after the round trip is complete.