Authentication

We provide an OpenID Connect compliant interface that should work well with any OpenID Connect certified relying party software.

This document will provide a high level overview, but we recommend that users familiarise themselves with the following specs:

Base URL:

Discovery

https://identity.moneyhub.co.uk/oidc/.well-known/openid-configuration

OpenID Connect Discovery Spec

Our discovery document is available here. It will contain our up-to-date machine readable configuration and for example will list our:

  • token endpoint
  • authorization endpoint
  • jwks endpoint
  • scopes that we support
  • claims that we support
  • the cryptographic algorithms we support
  • the response types we support

Examples of discovery metadata from other providers are:

Response Types

Our discovery doc will list the response types that we support. Currently these are: code, code id_token and id_token.

code is fairly straight forward and is the standard OAuth 2 authorization code flow.

code id_token is one of the variants of the hybrid flow and isn’t always understood. At a basic level it means that we will send an id_token along with the authorization code when we redirect the user back to your redirect_uri. This id_token doesn’t contain any identity information, but is rather a detached signature which cryptographically binds the authorization_code we send back with the nonce and the state that you sent to us. It prevents a certain class of code interception attacks and we encourage implementers to use it rather than the basic authorization code flow.

🚧

Response type configuration

When using response_type "code id_token", the redirect URL should be https, rather than http, and grant types configured for the API Client should include "implicit". This is required for production use.

Alternatively, during development, use the "code" response type, without the "implicit" grant type, and you can use http redirect URLs.

Token Endpoint

https://identity.moneyhub.co.uk/oidc/token

To obtain an Access Token, an ID Token, and optionally a Refresh Token, the RP (Client) sends a Token Request to the Token Endpoint to obtain a Token Response when using the Authorization Code Flow.

OpenID Connect Token Spec

We support the following grant types:

  • authorization_code
  • client_credentials
  • refresh_token

Authenticating on the Token Endpoint

Depending on the configured token endpoint method, the authentication sent to the token end point will differ. If you are using a client with client_secret_basic (which is not a recommended approach) then an Authorization header will be sent with a value of Basic base_64_env(${client_id}:${client_secret}).

If your client has a token endpoint authentication method of private_key_jwt you will also need a JWT when getting a token. The JWT will have the following body:

1. `iss` - your client id
2. `sub` - your client id
3. `jti` - A unique identifier for the token, which can be used to prevent reuse
4. `aud` - our token endpoint, i.e. [`https://identity.moneyhub.co.uk/oidc/token`](https://identity.moneyhub.co.uk/oidc/token)
5. `iat` - the time at which the token was issued
6. `exp` - the time at which the token will expire

The JWT is put in the token endpoint body in the property client_assertion. The client_assertion_type will need to be set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer.

Example

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlRxVk1laV9XdUtqZW5HWlJUbnJpeUxXRnZuS2tzTjNvLWFuWXBqS0JEbVUifQ.eyJqdGkiOiJmRnVjb0JpZTc2MGhJcXA3Vn5CeGQiLCJpc3MiOiJodHRwczovL2lkZW50aXR5Lm1vbmV5aHViLmNvLnVrL29pZGMiLCJpYXQiOjE2MjI4MTIzNzUsImV4cCI6MTYyMjgxMjk3NSwiYXVkIjoiOTc5YWJkYWItZmQ2NC00MmU2LWFiZDItZWU3NDU0MmYzNDkzI.E0FJk9gJaPXJzsQj2mWT5W4WTX4KNQtI0rnpuBHonexwbLV1jwz62rLkVDeevfVisdNst4K_bl5XsU8W4akuj4BTai5IPZq_CF5Zi6UGU5psD7cPYvqBNTvg41A2hkNwFPU8GJNd3fwhmuqkkzwH_JYAZlWTK_NYo1kp98lxYCmDdG2dWzHUXkq2FDi7k-mNHs2pjcOOEPfknvsOHyDpsC0R8nJaJjDPFSUIkwoUxxrt1fuQ26QbFKUNzUZponq9HsJeFsbFX5YnLnRM3Nqfo6lLT6vZNsnYEv5tbXGD_EUhk01cuOQZuBvspbm3F7tIVrgmRgiMgbVsyGbEx_wFSg

{
  "jti": "fFucoBie760hIqp7V~Bxd",
  "iss": "https://identity.moneyhub.co.uk/oidc",
  "iat": 1622812375,
  "exp": 1622812975,
  "aud": "979abdab-fd64-42e6-abd2-ee74542f3493"
}

Notes

  • You will need to provide a unique token identifier and set it to the jti property for all JWTs.
  • An issued at and expiry time will be required in the JWTs.
  • We would recommend using our API Client Library to generate these auth URLs and token requests if you are developing in JavaScript, or to find an OpenID Connect library for your chosen language that supports request objects.

More information about JWT requests is available here

Client credentials

For a detailed look at the client credentials grant, please look at this document.

Authorization Code

For a detailed look at the authorization code grant, please look at this document.

Refresh Token

The refresh_token grant type is implemented exactly as according to the specs in RFC6749 and OIDC.

JWKS Endpoints & Asymmetric Signatures

We use jwks endpoints to support the rotation of signing keys

As part of registering your client software with us, we will ask you for your own jwks endpoint.

If you don’t yet have one, we strongly encourage you to implement one. JWKS endpoints provide a neat method to achieve key rotation without any interruption to service or any need for bilateral communication.

They enable you to manage your keys in which ever manner you see fit and removes the need for the client_secret.

For more information about the benefits of this approach or advice on implementing, please contact us.

Code Example

Below is a Python code example of making a client_credentials grant for a client with a private key authentication method.

import jwt, jwcrypto.jwk as jwk, datetime, requests
from datetime import datetime, timedelta, timezone
import time
import random
import string

letters = string.ascii_lowercase

client_id = "client_id here"
private_key = {
}
key = jwk.JWK(**private_key)

def generate_jti():
  return "".join(random.choice(letters) for i in range(32))

def generate_jwt(client_id, identity_server):
  iat = datetime.now()
  exp = datetime.now() + timedelta(hours=1)

  payload = { 
    "iss": client_id,
    "sub": client_id,
    "aud": "{}/oidc/token".format(identity_server),
    "iat": time.mktime(iat.timetuple()),
    "exp": time.mktime(exp.timetuple()),
    "jti": generate_jti()
  }

  return jwt.encode(
    payload,
    key.export_to_pem(private_key=True, password=None), 
    algorithm="RS256",
  )

def get_client_credentials_token(client_id, private_key, scope, identity_server = "https://identity.moneyhub.co.uk"):
  assertion = generate_jwt(client_id=client_id, identity_server=identity_server)
  params = {
    "scope": scope,
    "grant_type": "client_credentials",
    "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    "client_assertion": assertion,
  }

  headers = {
    "Content-Type": "application/x-www-form-urlencoded",
  }

  return requests.post("{}/oidc/token".format(identity_server), data=params, headers=headers)

r = get_client_credentials_token(client_id=client_id, private_key=private_key, scope="payee:create")

print(r.text)