Skip to main content

Configuring a Client via API

Before you start

Make sure you obtain the following valid parameters from the Helm chart:

  • AS_URI
  • AS_ADMIN_URI
  • AS_ADMIN_STATIC_TOKEN

Further instruction can be found at here

Overview

This page can be divided into two major sections + four sub sections as a fully functional Client (Service Provider) in FPX requires:

  1. A Client entity which stores basic information of the Service Provider such as base_url or secret.

  2. A ticket which is a collection of the following:

    2.1 A purpose which describe what the ticket is used for.

    2.2 A capability ticket which defines what information is the service provider capable of accessing.

    2.3 A requested resource which represents sensitive information that the FPX network tries to protect. And a scope which represents the client's permission on the requested resources.

    2.4 A linkage between requested resource and capability ticket.

Section 1: Client

A working Client (Service Provider) in FPX, requires data from the following data types, (listed in the order of creation):

  1. oauth-client-metadata
  2. oauth-client
  3. uma-client

Type: "oauth-client-metadata"

Defines additional data for the OAuth Client, e.g. authentication type, scope, secret, etc.

Note

"" means empty string

important

For the oauth-client-metadata entity, do not set both jwksUri and jwksRaw together. If jwksUri is set, ensure that jwksRaw is null.

AttributesDescriptionExample valueRequiredLocalizable
issuerUriProvide host URL of Service Provider. This value can be used as a fallback to retrieve openid configuration details for the Service Provider by calling this URI at the .well-known/openid-configurationhttps://some-service-provider.comYesNo
clientTypeClient typeEither CONFIDENTIAL or PUBLICYesNo
jwksRawPublic key details registered directly into the database. A client can either register the public key set in this parameter or provide a URI in the jwksUri parameter to expose that endpoint and get the public key set. Must be set to null if jwksUri is populated.For an example of this attribute, see the sample request to update oauth-client-metadata, belowYes, if authentication type is private_key_jwt and jwksUri is emptyNo
jwksUriURL of a set of keys containing the public keys. Can be left blank if jwksRaw is populated. Use of jwksUri is preferred over jwksRaw.https://some-service-provider.com/jwksYes, if authentication type is private_key_jwt and jwksRaw is emptyNo
clientAuthenticationTypeAuthentication methodOne of the following:
- client_secret_basic
- client_secret_post
- client_secret_jwt
- private_key_jwt
YesNo
grantTypesGrant type"refresh_token client_credentials authorization_code"YesNo
scopesScopes of client. Likely not required as clients will use capability ticketsregisterNoNo
clientSecretAny string length <= 255client-secretYes, if authentication type is client_secret_post or client_secret_basic. This parameter should be null if the authentication type is set to private_key_jwt.No

Type: "oauth-client"

Defines Client ID and Redirect URI

AttributeDescriptionExample valueRequiredLocalizable
clientIdClient identifier in string.fpx-service-providerYesNo
clientNameA human readable name for the client.Identos WalletNoNo
redirectUrisURL for redirection after the request is authorized.https://some-service-provider.comNoNo
RelationshipDescriptionRequired
oAuthClientMetaDataLinkage to metadata. The ID needs to be created before hand. Yes

Type: "uma-client"

Defines basic information for Service provider. For example, terms of service (tos), policy URL, OAuth client, etc.

AttributeDescriptionExample valueRequiredLocalizable
iconUrlLink to service provider iconhttps://www.identos.ca/wp-content/themes/identos/images/logo.pngYesYes
baseUrlHost of Service providerhttps://identos.com/fpxspYesNo
nameName of service providerService Provider NameYesYes
policyUrlLink to service provider policyhttps://www.identos.ca/privacy-policy/YesYes
tosUrlLink to service provider terms of servicehttps://www.identos.ca/legal/YesYes
umaClientIdUnique identifier of UMA Client IDfpxspYesNo
disabledOnDate and time at which the entity was, or will be, disabled. The value must be in "yyyy-MM-dd'T'HH:mm:ss'Z'" format. In order to re-enable, the value must be reset to the default value null."2021-01-01T11:00:00Z".NoNo
RelationshipDescriptionRequired
oauthClientLinkage to oauth_client. The ID needs to be created beforehand.Yes

Sample Requests

Create a client (Service Provider) - All in one (OAuth Client Metadata, OAuth Client and UMA Client)

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[

{
"op": "add",
"path": "/oauth-client-metadata",
"value": {
"id": 4,
"type": "oauth-client-metadata",
"attributes": {
"issuerUri": "https://some-service-provider.com/",
"clientAuthenticationType": "private_key_jwt",
"clientType": "CONFIDENTIAL",
"grantTypes": "client_credentials",
"jwksRaw": null,
"jwksUri": "https://some-service-provider.com/jwks",
"scopes": "uma_protection",
"clientSecret": null
}
}
},
{
"op": "add",
"path": "/oauth-client",
"value": {
"type": "oauth-client",
"id": 4,
"attributes": {
"clientId": "fpxsp",
"clientName": "FPX Service Provider",
"redirectUris": [
"https://some-service-provider.com",
"{{AS_URI}}"
]
},
"relationships": {
"oAuthClientMetaData": {
"data": {
"type": "oauth-client-metadata",
"id": 4
}
}
}
}
},
{
"op": "add",
"path": "/uma-client",
"value": {
"type": "uma-client",
"id": 1,
"attributes": {
"iconUrl": "",
"baseUrl": "https://identos.com/fpxsp",
"name": "Service Provider Name",
"policyUrl": "https://identos.com/fpxsp/policy/en",
"tosUrl": "https://identos.com/fpxsp/tosUrl/en",
"umaClientId": "fpxsp"
},
"relationships": {
"oauthClient": {
"data": {
"id": 4,
"type": "oauth-client"
}
}
}
}
}
]'
curl --location -g --request GET '{{AS_ADMIN_URI}}/oauth-client-metadata/4' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'
curl --location -g --request GET '{{AS_ADMIN_URI}}/oauth-client/4' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'
curl --location -g --request GET '{{AS_ADMIN_URI}}/uma-client/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'

Update Client - All in one (OAuth Client Metadata, OAuth Client and UMA Client)

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[

{
"op": "replace",
"path": "/oauth-client-metadata/4",
"value": {
"id": 4,
"type": "oauth-client-metadata",
"attributes": {
"issuerUri": "https://some-service-provider.com/",
"clientAuthenticationType": "private_key_jwt",
"clientType": "CONFIDENTIAL",
"grantTypes": "client_credentials",
"jwksRaw": "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"rsa1\",\"alg\":\"RS256\",\"n\":\"o-uKni7MWPz2tYxMIx3g6hhYWBCfdDizk7yhFZ14MX9MpHEjWvH983m1X8HcYaYhqRr3Lz-zGsanOy0DoSlmaXUjv90fy7MzoNs-c7cc3GRQrZAnL_UI2FSh9FhqqwZgyXMpleZY7-YDquEMQfZ9L2W5Q0pyYr5nNfSPX8FQWdV4kRwTjhPJT7RhntJ8A1X6KLj3aBseZge92jVtWAOD702GhxRjfyHcguVelf8-qFe8fJrODpQ2yCRbh1zrq5AY2MDiW1njy7t-GayqQG2alqPjZGH9f7uIHxtHNIKXF92OEVG4A_HPbDlJ2RxidCbYOnyESaho4ZayHOc6KlpXWQ\"}]}",
"jwksUri": null,
"scopes": "uma_protection",
"clientSecret": null
}
}
},
{
"op": "replace",
"path": "/oauth-client/4",
"value": {
"type": "oauth-client",
"id": 4,
"attributes": {
"clientId": "fpxsp",
"clientName": "FPX Service Provider",
"redirectUris": [
"https://some-service-provider.com",
"{{AS_URI}}"
]
},
"relationships": {
"oAuthClientMetaData": {
"data": {
"type": "oauth-client-metadata",
"id": 4
}
}
}
}
},
{
"op": "replace",
"path": "/uma-client/1",
"value": {
"type": "uma-client",
"id": 1,
"attributes": {
"iconUrl": "",
"baseUrl": "https://identos.com/fpxsp",
"name": "UMA-CLIENT-NAME-AZEZHDvMTQPKBgA2ZjazcVVtb9SEra-1653663342152",
"policyUrl": "UMA-CLIENT-POLICY_URI-09LnKmiSAlW2GEAFGRRhDLdYDBMWi5-1653663342153",
"tosUrl": "UMA-CLIENT-TOS_URI-M7dREBywjoKt52XdhZ2bd8CJluFQ53-1653663342155",
"umaClientId": "fpxsp"
},
"relationships": {
"oauthClient": {
"data": {
"id": 4,
"type": "oauth-client"
}
}
}
}
}
]'

Disable Client (UMA Client)

curl --location --request PATCH '{{AS_ADMIN_URI}}/uma-client/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--data-raw '{
"data": {
"type": "uma-client",
"id": "1",
"attributes": {
"disabledOn" : "2012-11-12T01:00:00Z"
}
}
}'

Section 2: Ticket

Tickets represent a bundle of resources that the client will request, leaving it up to the end user to choose sources. Ticket creation involves describing the purpose of the ticket, providing a UI-friendly name for the ticket, and defining which resources and associated scopes will be requested.

Real-world scenario

In order to log in, a Client may require many pieces of information from the end user, such as a name, email address, and address. Each of these attributes may come from a unique resource definition with a unique type, which many Resource Servers may be able to provide, or may be modeled as scopes within a single resource definition.
The available APIs and how they're set up as FPX resources and scopes is unique to your use-case

Type: "purpose"

Defines the purpose of capability ticket or requested resource scopes.

AttributesDescriptionExample valueRequiredLocalizable
nameThe name of the purposeAny string e.g. "Service Provider A wants your email address"YesYes

Sample Requests

Create a Purpose

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "add",
"path": "/purpose",
"value": {
"type": "purpose",
"id": 1,
"attributes": {
"name": "identity-profile access"
}
}
}
]'

Get a Purpose

curl --location -g --request GET '{{AS_ADMIN_URI}}/purpose/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'

Update a Purpose

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "replace",
"path": "/purpose/1",
"value": {
"type": "purpose",
"id": 1,
"attributes": {
"name": "identity-profile access"
}
}
}
]'

Type: "capability-ticket"

A Capability Ticket allows a Client to request a set of pre-registered resources and scopes for a specific Purpose.

AttributesDescriptionExample valueRequiredLocalizable
ticketIdA unique identifier for the Capability Ticket.identity-profileYesNo
ticketNameThe readable name of the ticket.Identity TicketYesYes
disabledOnDate and time at which the entity was, or will be, disabled. The value must be in "yyyy-MM-dd'T'HH:mm:ss'Z'" format. In order to re-enable, the value must be reset to the default value null."2021-01-01T11:00:00Z"NoNo
RelationshipDescriptionRequired
purposeThe purpose of this ticket. E.g. it is used when a Client requests end-user email.Yes
umaClientClient that is assigned to this ticket.Yes

Sample Requests

Create a Capability Ticket

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "add",
"path": "/capability-ticket",
"value": {
"type": "capability-ticket",
"id": 1,
"attributes": {
"ticketId": "identity-profile",
"ticketName": "Identity Ticket"
},
"relationships": {
"purpose": {
"data": {
"id": 1,
"type": "purpose"
}
},
"umaClient": {
"data": {
"id": 1,
"type": "uma-client"
}
}
}
}
}
]'

Get a Capability Ticket

curl --location -g --request GET '{{AS_ADMIN_URI}}/capability-ticket/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'

Update a Capability Ticket

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "replace",
"path": "/capability-ticket/1",
"value": {
"type": "capability-ticket",
"id": 1,
"attributes": {
"ticketId": "identity-profile",
"ticketName": "Identity Ticket"
},
"relationships": {
"purpose": {
"data": {
"id": 1,
"type": "purpose"
}
},
"umaClient": {
"data": {
"id": 1,
"type": "uma-client"
}
}
}
}
}
]'

Disable a Capability Ticket

curl --location -g --request PATCH '{{AS_ADMIN_URI}}/capability-ticket/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '{
"data": {
"type": "capability-ticket",
"id": 1,
"attributes": {
"disabledOn": "2021-01-01T11:00:00Z"
}
}
}'
Using scopes

The resources to be requested can each have different scopes. For example, most Clients may only require a 'read' scope to the email and proof of address resources, while others, such as government services may want 'read' access to email, but 'read' and 'write' access to proof of address so they can update information at the Resource Server directly when the user moves.

Type: "requested-resource"

AttributesDescriptionExample valueRequiredLocalizable
maxPermissionDurationDuration (in milliseconds) that a client is allowed to access the requested resource.300000000YesNo
requestedResourceIdUnique identifier that represents requested resource."requested-resource-2022-01-01-11112020"YesNo
RelationshipDescriptionRequired
resourceDefinitionDefinition of a resource in FPX that needs to be created beforehand.Yes
umaClientThe onboarded Service Provider.Yes

Type: "requested-resource-scopes"

RelationshipDescriptionRequired
scopeThe generic scope entity in the database. It needs to be created beforehand.Yes
requestedResourceThe resource that is requested by the Service Provider.Yes

Sample Requests

Create Requested Resource - All in one

 
curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "add",
"path": "/requested-resource",
"value": {
"type": "requested-resource",
"id": 1,
"attributes": {
"maxPermissionDuration": 300000000,
"requestedResourceId": "8ddd9d0b-6776-4bfe-9865-b2fb9559b12312"
},
"relationships": {
"resourceDefinition": {
"data": {
"id": 1,
"type": "resource-definition"
}
},
"umaClient": {
"data": {
"id": 1,
"type": "uma-client"
}
}
}
}
},
{
"op": "add",
"path": "/requested-resource-scopes",
"value": {
"type": "requested-resource-scopes",
"id": 1,
"attributes": {},
"relationships": {
"scope": {
"data":
{
"id": 1,
"type": "scope"
}
},
"purpose": {
"data": {
"id": 1,
"type": "purpose"
}
},
"requestedResource": {
"data": {
"id": 1,
"type": "requested-resource"
}
}
}
}
},
{
"op": "add",
"path": "/requested-resource-scopes",
"value": {
"type": "requested-resource-scopes",
"id": 2,
"attributes": {},
"relationships": {
"scope": {
"data":
{
"id": 2,
"type": "scope"
}
},
"purpose": {
"data": {
"id": 1,
"type": "purpose"
}
},
"requestedResource": {
"data": {
"id": 1,
"type": "requested-resource"
}
}
}
}
}
]'

Get requested resource

curl --location -g --request GET '{{AS_ADMIN_URI}}/requested-resource/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'

Get requested resource scope

curl --location -g --request GET '{{AS_ADMIN_URI}}/requested-resource-scopes/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'

Update requested resource - All in one

Update requested_resource_id at your own risk

requested_resource_id A.K.A rr_id is a foreign key in the request-resource-scope table.

If you want to update rr_id, you can use the following API to remove related entities first. Otherwise, the API will raise a foreign key update exception.

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "remove",
"path": "/requested-resource-scopes/1"

}
]'
curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "replace",
"path": "/requested-resource/1",
"value": {
"type": "requested-resource",
"id": 1,
"attributes": {
"maxPermissionDuration": 300000000,
"requestedResourceId": "8ddd9d0b-6776-4bfe-9865-b2fb9559b12312"
},
"relationships": {
"resourceDefinition": {
"data": {
"id": 1,
"type": "resource-definition"
}
},
"umaClient": {
"data": {
"id": 1,
"type": "uma-client"
}
}
}
}
},
{
"op": "replace",
"path": "/requested-resource-scopes/1",
"value": {
"type": "requested-resource-scopes",
"id": 1,
"attributes": {},
"relationships": {
"scope": {
"data": {
"id": 1,
"type": "scope"
}
},
"purpose": {
"data": {
"id": 1,
"type": "purpose"
}
},
"requestedResource": {
"data": {
"id": 1,
"type": "requested-resource"
}
}
}
}
}
]'

Type: "requested-resources-capability-tickets"

Linking requested resources with capability ticket

Clients may have multiple tickets, and tickets can have multiple resources. The Admin User should only allow each Client to request the minimum scopes and resources that the Client can justify needing.

Requested Resource Capability

RelationshipDescriptionRequiredLocalizable
capabilityTicketCapability ticket previously defined.YesNo
requestedResourceRequested Resource previously defined.YesNo

Sample Requests

Create Requested Resource Capability Ticket

curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "add",
"path": "/requested-resources-capability-tickets",
"value": {
"type": "requested-resources-capability-tickets",
"id": 1,
"attributes": {},
"relationships": {
"capabilityTicket": {
"data": {
"type": "capability-ticket",
"id": 1
}
},
"requestedResource": {
"data": {
"type": "requested-resource",
"id": 1
}
}
}
}
}
]'

Get Requested Resource Capability Ticket

curl --location -g --request GET '{{AS_ADMIN_URI}}/requested-resources-capability-tickets/1' \
--header 'Content-Type: application/vnd.api+json' \
--header 'ApiVersion: v1.0' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'Accept-Language: en'

Update Requested Resource Capability Ticket


curl --location -g --request PATCH '{{AS_ADMIN_URI}}' \
--header 'Content-Type: application/vnd.api+json; ext=jsonpatch' \
--header 'Authorization: {{AS_ADMIN_STATIC_TOKEN}}' \
--header 'ApiVersion: v1.0' \
--header 'Accept-Language: en' \
--data-raw '[
{
"op": "replace",
"path": "/requested-resources-capability-tickets/1",
"value": {
"type": "requested-resources-capability-tickets",
"id": 1,
"attributes": {},
"relationships": {
"capabilityTicket": {
"data": {
"type": "capability-ticket",
"id": 1
}
},
"requestedResource": {
"data": {
"type": "requested-resource",
"id": 1
}
}
}
}
}
]'
note

The localization feature is covered in more detail here.