Kantara Initiative

OAuth 2.0 Resource Registration

Version:2.0
 
Date:2017-3-12
 
Editor:Eve Maler, ForgeRock
 
Contributors:Maciej Machulak, Self
Justin Richer, Bespoke Engineering

Abstract

This specification defines a method for an OAuth 2.0 resource server to register resources with an authorization server to put them under protection.

Status of This Document

This document is a draft technical specification produced by the User-Managed Access Work Group. See the Kantara Initiative Operating Procedures for more information.

Copyright Notice

Copyright © 2017 Kantara Initiative and the persons identified as the document authors. All rights reserved.

This document is subject to the Kantara IPR Policy - Option Patent & Copyright: Reciprocal Royalty Free with Opt-Out to Reasonable And Non discriminatory (RAND) (HTML version).


Table of Contents


1. Introduction

This specification defines a method for an OAuth 2.0 [RFC6749] resource server to register resources with an authorization server to put them under protection.

There are various circumstances under which a resource server may need to communicate information about its resources to its authorization server:

1.1 Notational Conventions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].

Unless otherwise noted, all protocol properties and values are case sensitive. JSON [RFC7159] data structures defined by this specification MAY contain extension properties that are not defined in this specification. Any entity receiving or retrieving a JSON data structure SHOULD ignore extension properties it is unable to understand. Extension names that are unprotected from collisions are outside the scope of this specification.

1.2 Terminology

This specification introduces the following new terms and enhancements of OAuth term definitions.

resource
A digital resource available through an HTTP service.
protected resource
A resource for which an authorization server manages access grants. Protection begins on successful registration and ends on successful deregistration.
scope
A bounded extent of access to a protected resource. Scopes are associated with particular resources.

1.3 Authorization Server Configuration

If the authorization server declares its endpoints and any other configuration information in a machine-readable form, it SHOULD convey its resource registration endpoint in this fashion as well.


2. Resource Registration

This specification defines a resource registration API. The authorization server MUST use TLS protection over this endpoint, as governed by [BCP195], which discusses deployment and adoption characteristics of different TLS versions. The endpoint for this API SHOULD require some form of authentication to access this endpoint, such as Client Authentication as described in [RFC6749] or a separate OAuth access token. The methods of managing and validating these authentication credentials are out of scope of this specification.

The resource server MAY register for protection a single resource that, from its perspective, has multiple parts, or has dynamic elements such as the capacity for querying or filtering, or otherwise has internal complexity. Note: The resource server alone is responsible for maintaining the necessary mappings between any such complexity (which might result, for example, in different requests coming from the client) and a single resource identifier known to the authorization server.

Note: When a client attempts access to a presumptively protected resource without an access token, as occurs in [UMA], the resource server needs to derive the authorization server and resource identifier associated with that resource from some other aspect of the client's request. This effectively means that the resource server’s API needs to be structured in such a way that the client's request without an access token uniquely identifies the resource. In practice, this information likely needs to be passed through the URI, headers, or body of the request.

2.1 Resource Descriptions

A resource description is a JSON document that describes the characteristics of a resource sufficiently for an authorization server to protect it. A resource description has the following properties:

name
OPTIONAL. A human-readable string naming the resource. The authorization server MAY use this name in any user interface it presents to a resource owner, for example, for resource protection monitoring or policy setting.
description
OPTIONAL. A human-readable string describing the resource at greater length. The authorization server MAY use this description in any user interface it presents to a resource owner, for example, for resource protection monitoring or policy setting.
type
OPTIONAL. A string identifying the semantics of the resource. For example, if the resource is an identity claim that leverages standardized claim semantics for "verified email address", the value of this property could be an identifying URI for this claim. The authorization server MAY use this information in processing information about the resource or displaying information about it in any user interface it presents to a resource owner.
scopes
REQUIRED. An array of strings, serving as scope identifiers, indicating the available scopes for this resource. Any of the strings MAY be either a plain string or a URI. While a URI MAY resolve to a scope description document as defined in Section 2.1.1, and thus scope description documents are possible to standardize and reference publicly, the authorization server is not expected to resolve scope description details at resource registration time or at any other run-time requirement. The resource server and authorization server are presumed to have negotiated any required interpretation of scope handling out of band.
icon_uri
OPTIONAL. A URI for a graphic icon representing the resource. The authorization server MAY use the referenced icon in any user interface it presents to a resource owner, for example, for resource protection monitoring or policy setting.

The resource server MAY encode human-readable strings using the pattern described in Section 2.2 of [RFC7591] for representing multiple languages and scripts, where language tags are added delimited by a "#" character. It is OPTIONAL for the authorization server to make use of this pattern, if present in registered resources, for run-time dynamic string display effects.

For example, this description characterizes a resource (a photo album) that can potentially be viewed or printed; the scope URI points to a scope description as defined in Section 2.1.1:

{  
   "name":"Photo Album",
   "icon_uri":"http://www.example.com/icons/flower.png",
   "scopes":[  
      "view",
      "http://photoz.example.com/dev/scopes/print"
   ],
   "type":"http://www.example.com/rsrcs/photoalbum"
}

See Section 2.2 for how the resource server registers resource descriptions with the authorization server in order to put resources under protection. See Section 7 for a long-form example of resource descriptions used in resource registration.

2.1.1 Scope Descriptions

A scope description is a JSON document that describes the characteristics of a scope sufficiently for an authorization server to protect the resource with this scope. A scope description has the following properties:

name
OPTIONAL. A human-readable string naming the scope. The authorization server MAY use this name in any user interface it presents to a resource owner, for example, for resource protection monitoring or policy setting.
description
OPTIONAL. A human-readable string describing the resource at greater length. The authorization server MAY use this description in any user interface it presents to a resource owner, for example, for resource protection monitoring or policy setting.
icon_uri
OPTIONAL. A URI for a graphic icon representing the scope. The authorization server MAY use the referenced icon in any user interface it presents to a resource owner, for example, for resource protection monitoring or policy setting.

The resource server MAY encode human-readable strings using the pattern described in Section 2.2 of [RFC7591] for representing multiple languages and scripts, where language tags are added delimited by a "#" character. It is OPTIONAL for the authorization server to make use of this pattern, if present in registered scopes, for run-time dynamic string display effects.

For example, this scope description characterizes a scope that involves viewing (vs., say, creating or editing in some fashion):

{  
   "name":"View",
   "icon_uri":"http://www.example.com/icons/reading-glasses"
}

See Section 7 for a long-form example of scope descriptions used in resource registration.

2.2 Resource Registration API

The resource server uses a RESTful API at the authorization server's resource registration endpoint to create, read, update, and delete resource descriptions, along with retrieving lists of such descriptions.

Note carefully the similar but distinct senses in which the word "resource" is used in this section. The resource descriptions are themselves managed as web resources at the authorization server through this API. The initial registration process results in a unique identifier for the web resource that the resource server can later use for managing its description over time and for mapping .

The authorization server MUST present an API for registering resource descriptions as a set of URIs whose first set of path components is the authorization server's resource registration endpoint.

The authorization server is REQUIRED to support the following five registration options; any other operations are undefined by this specification. Here, rreguri stands for the resource registration endpoint and _id stands for the authorization server-assigned identifier for the web resource corresponding to the resource at the time it was created, included within the URL returned in the Location header. Each operation is defined in its own section below.

  • Create resource description: POST rreguri/
  • Read resource description: GET rreguri/_id
  • Update resource description: PUT rreguri/_id
  • Delete resource description: DELETE rreguri/_id
  • List resource descriptions: GET rreguri/

Within the JSON body of a successful response, the authorization server includes common properties, possibly in addition to method-specific properties, as follows:

_id
REQUIRED (except for the List method). A string value repeating the authorization server-defined identifier for the web resource corresponding to the resource. Its appearance in the body makes it readily available as an object identifier for various resource management tasks.
user_access_policy_uri
OPTIONAL. A URI that allows the resource server to redirect an end-user resource owner to a specific user interface within the authorization server where the resource owner can immediately set or modify access policies subsequent to the resource registration action just completed. The authorization server is free to choose the targeted user interface, for example, in the case of a deletion action, enabling the resource server to direct the end-user to a policy-setting interface for an overall "folder" of resources where the deleted resource once resided.

If the request to the resource registration endpoint is incorrect, then the authorization server instead responds with an error message by including one of the following error codes with the response (see Section 3):

unsupported_method_type
The resource server request used an unsupported HTTP method. The authorization server MUST respond with the HTTP 405 (Method Not Allowed) status code.
not_found
The resource requested from the authorization server cannot be found. The authorization server MUST respond with HTTP 404 (Not Found) status code.

2.2.1 Create Resource Description

Adds a new resource description to the authorization server using the POST method. If the request is successful, the resource is thereby registered and the authorization server MUST respond with a status message that includes an _id property.

Form of a create request, with an access token in the header:

POST /rs/ HTTP/1.1 Content-Type: application/json
Authorization: Bearer MHg3OUZEQkZBMjcx
...
{  
   "name":"Tweedl Social Service",
   "icon_uri":"http://www.example.com/icons/sharesocial.png",
   "scopes":[  
      "read-public",
      "post-updates",
      "read-private",
      "http://www.example.com/scopes/all"
   ],
   "type":"http://www.example.com/rsrcs/socialstream/140-compatible"
}

Form of a successful response:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /rs/KX3A-39WE
...
{  
   "_id":"KX3A-39WE",
   "user_access_policy_uri":"http://as.example.com/rs/222/resource/KX3A-39WE/policy"
}

2.2.2 Read Resource Description

Reads a previously registered resource description using the GET method. If the request is successful, the authorization server MUST respond with a status message that includes a body containing the referenced resource description, along with an _id property.

Form of a read request, with an access token in the header:

GET /rs/KX3A-39WE HTTP/1.1
Authorization: Bearer MHg3OUZEQkZBMjcx
...

Form of a successful response:

HTTP/1.1 200 OK
Content-Type: application/json
...
{  
   "_id":"KX3A-39WE",
   "name":"Tweedl Social Service",
   "icon_uri":"http://www.example.com/icons/sharesocial.png",
   "scopes":[  
      "read-public",
      "post-updates",
      "read-private",
      "http://www.example.com/scopes/all"
   ],
   "type":"http://www.example.com/rsrcs/socialstream/140-compatible"
}

If the referenced resource does not exist, the authorization server MUST produce an error response with an error property value of not_found, as defined in Section 2.2.

2.2.3 Update Resource Description

Updates a previously registered resource description, by means of a complete replacement of the previous resource description, using the PUT method. If the request is successful, the authorization server MUST respond with a status message that includes an _id property.

Form of an update request, with an access token in the header:

PUT /rs/9UQU-DUWW HTTP/1.1
Content-Type: application/json
Authorization: Bearer 204c69636b6c69
...
{  
   "name":"Photo Album",
   "icon_uri":"http://www.example.com/icons/sky.png",
   "scopes":[  
      "http://photoz.example.com/dev/scopes/view",
      "public-read"
   ],
   "type":"http://www.example.com/rsrcs/photoalbum"
}

Form of a successful response:

HTTP/1.1 200 OK
...
{  
   "_id":"9UQU-DUWW"
}

2.2.4 Delete Resource Description

Deletes a previously registered resource description using the DELETE method. If the request is successful, the resource is thereby deregistered.

Form of a delete request, with an access token in the header:

DELETE /rs/9UQU-DUWW
Authorization: Bearer 204c69636b6c69
...

Form of a successful response:

HTTP/1.1 204 No content
...

If the referenced resource does not exist, the authorization server MUST produce an error response with an error property value of not_found, as defined in Section 2.2.

2.2.5 List Resource Descriptions

Lists all previously registered resource identifiers for this resource owner using the GET method. The authorization server MUST return the list in the form of a JSON array of {_id} string values.

The resource server uses this method as a first step in checking whether its understanding of protected resources is in full synchronization with the authorization server's understanding.

Form of a list request, with an access token in the header:

GET /rs/ HTTP/1.1
Authorization: Bearer 204c69636b6c69
...

Form of a successful response:

HTTP/1.1 200 OK
...
[  
   "KX3A-39WE",
   "9UQU-DUWW"
]

3. Error Messages

When a resource server attempts to access the resource registration endpoint at the authorization server, if the request is successfully authenticated, but is invalid for another reason, the authorization server produces an error response by adding the following properties to the entity body of the HTTP response:

error
REQUIRED. A single error code. Values for this property are defined throughout this specification.
error_description
OPTIONAL. Human-readable text providing additional information.
error_uri
OPTIONAL. A URI identifying a human-readable web page with information about the error.

4. Security Considerations

This specification largely relies on the base API security method, such as OAuth, for API security and shares its security and vulnerability considerations.

Some form of trust relationship between the authorization server and resource server is presumed. However, where this pairwise trust relationship is weak or loosely coupled, for example, if dynamic client registration is supported for resource servers as OAuth clients of the resource registration API, the authorization server must be extremely careful with any information supplied during resource registration that may be displayed to a resource owner in a user interface or be used to drive authorization server-based workflows.

For example, a rogue resource server could register a resource or scope with a reference to a drive-by download in icons returned by the icon_uri, enticing the resource owner to click on the icon during the policy setting process. The authorization server SHOULD check to see if all URIs defined in the icon_uri resolve to valid web pages. Since these are URI values that are intended to be displayed to the resource owner in a policy-setting interface, the authorization server SHOULD protect the resource owner from malicious content hosted at the URIs where possible.

The authorization server SHOULD also sanitize any display input sourced from resource description documents and scope description documents to mitigate cross-site scripting risks. One approach is to use existing libraries that clean HTML and other types of text fragments.

Additionally, a rogue resource server could supply misleading names for a resource or scope, or mislabel the type of a resource, leading a resource owner to set policy incorrectly.

One approach to mitigating the threat of bad URIs is that, before presenting clickable icons, the authorization server could download the content hosted at the linked URIs, check the content against a malware scanner and blacklist filter, determine whether or not there is mixed secure and non-secure content at the URL, and other possible server-side mitigations. Note that the content in these URIs can change at any time and the authorization server cannot provide complete confidence in the safety of the URIs, but these practices could help. To further mitigate this kind of threat, the authorization server can also warn the user that the links have been provided by a third party, should be treated with caution, and are not hosted by the authorization server itself. For instance, instead of providing the links directly in an HTML anchor, the authorization server can direct the user to an interstitial warning page before allowing the user to continue to the target URI.

An alternative approach may be available in deployment ecosystems where trust relationships with resource servers may be weak but APIs, resource semantics, and scopes have been standardized. A resource server could potentially increase the likelihood of an authorization server trusting the URIs it registers by using well-known and third-party-standardized URIs. This may benefit resource type values as well, if these are represented as URIs.

In many ways, the resource server depends more heavily on elements of the pairwise trust relationship than does the authorization server. Since the resource server needs to outsource a variety of resource protection-related processes to the authorization server, a rogue or impersonated authorization server would present great risk. Using OAuth with its attendant channel security requirements for API authentication would mitigate the risk of impersonation.

A small example of the risk posed by a rogue authorization server is the user_access_policy_uri property, which is the one case of a URI passed from authorization server to resource server rather than the reverse. The resource server, in taking advantage of this URI, could redirect a resource owner to a third-party web page infected with malware or similar. To mitigate this specific risk, the resource server could check that the URI is fully qualified and matches the host and scheme of the authorization server.


5. Privacy Considerations

The communication between the authorization server and resource server may expose personally identifiable information of a resource owner. The context in which this API is used SHOULD account for its own unique privacy considerations.


6. IANA Considerations

This document makes no request of IANA.


7. Example of Registering Resources

The following non-normative example illustrates the intent and usage of resource descriptions and scope descriptions as part of resource registration in the context of [UMA].

This example contains some steps that are exclusively in the realm of user experience rather than web protocol, to achieve realistic illustration. These steps are labeled "user experience only". Some other steps are exclusively internal to the operation of the entity being discussed. These are labeled "internal only".

This example also does not contain any information regarding the mechanism that is used to authenticate the resource server at the authorization server, and mechanisms such as OAuth can be used for this purpose.

A resource owner, Alice Adams, has just uploaded a photo of her new puppy to a resource server, Photoz.example.com, and wants to ensure that this specific photo is not publicly accessible.

Alice has already introduced this resource server to her authorization server, CopMonkey.example.com. However, Alice has not previously instructed Photoz to use CopMonkey to protect any photos of hers.

CopMonkey has a "default-deny" setup, so until such time as Alice maps some other more permissive policies to any resources registered by Photoz, the policy is not to share them. Policies she may eventually map to particular photos or albums might be "Share only with husband@email.example.net" or "Share only with people in my 'family' group".

Photoz has a publicly documented application-specific API that offers two dozen different methods that apply to single photos, such as addTags and getSizes, but rolls them up into two photo-related scopes of access: "view" (consisting of various read-only operations) and "print" (consisting of various printing operations). It defines two scope descriptions that represent these scopes, which it is able to reuse for all of its users (not just Alice), and ensures that these scope description documents are available through HTTP GET requests that may be made by authorization servers.

Photoz constructs scope description documents for the scopes in order to provide data that will help them be displayed in user interfaces. (This means that any scope strings it supplies in resource registration descriptions will need to be URIs referring to these documents. The alternative would have been simple scope strings without the additional user interface data.)

The "name" property values in the scope descriptions are intended to be seen by Alice when she maps authorization constraints to specific resources and scopes while visiting CopMonkey, such that Alice would see the strings "View Photo and Related Info" and "Print Photo", likely accompanied by the referenced icons, in the CopMonkey interface. (Other users of Photoz might similarly see the same labels at CopMonkey or whatever other authorization server they use. Photoz could distinguish natural-language labels per user if it wishes, by pointing to scopes with differently translated names.)

Example of the viewing-related scope description document available at http://photoz.example.com/dev/scopes/view:

{  
   "name":"View Photo and Related Info",
   "icon_uri":"http://www.example.com/icons/reading-glasses.png"
}

Example of the scope description document available at http://photoz.example.com/dev/scopes/print:

{  
   "name":"Print Photo",
   "icon_uri":"http://www.example.com/icons/printer.png"
}

While visiting Photoz, Alice selects a link or button that instructs the site to "Protect" or "Share" this single photo (user experience only; Photoz could have made this a default or preference setting).

As a result, Photoz defines for itself a resource that represents this photo (internal only; Photoz is the only application that knows how to map a particular photo to a particular resource). Photoz also prepares the following resource description, which is specific to Alice and her photo. The "name" property value is intended to be seen by Alice in mapping authorization policies to specific resources and scopes when she visits CopMonkey. Alice would see the string "Steve the puppy!", likely accompanied by the referenced icon, in the CopMonkey interface. The possible scopes of access on this resource are indicated with URI references to the scope descriptions, as shown just above.

{  
   "name":"Steve the puppy!",
   "icon_uri":"http://www.example.com/icons/flower.png",
   "scopes":[  
      "http://photoz.example.com/dev/scopes/view",
      "http://photoz.example.com/dev/scopes/print"
   ]
}

Photoz uses the Create method of CopMonkey's standard OAuth resource registration API, presenting its Alice-specific access token to use the API to register and assign an identifier to the resource description.

POST /rs/ HTTP/1.1
Content-Type: application/json
...
{  
   "name":"Steve the puppy!",
   "icon_uri":"http://www.example.com/icons/flower.png",
   "scopes":[  
      "http://photoz.example.com/dev/scopes/view",
      "http://photoz.example.com/dev/scopes/print"
   ]
}

If the registration attempt succeeds, CopMonkey responds in the following fashion.

HTTP/1.1 201 Created
Content-Type: application/json
...
{  
   "_id":"112210f47de98100",
   "user_access_policy_uri":"http://as.example.com/rs/222/resource/112210f47de98100/policy"
}

At the time Alice indicates she would like this photo protected, Photoz can choose to redirect Alice to CopMonkey for further policy setting, access auditing, and other authorization server-related tasks (user experience only).

Once it has successfully registered this description, Photoz is responsible for outsourcing protection to CopMonkey for access attempts made to this photo.

Over time, as Alice uploads other photos and creates and organizes photo albums, Photoz can use additional methods of the resource registration API to ensure that CopMonkey's understanding of Alice's protected resources matches its own.

For example, if Photoz suspects that somehow its understanding of the resource has gotten out of sync with CopMonkey's, it can ask to read the resource description as follows.

GET /rs/112210f47de98100 HTTP/1.1
Host: as.example.com
...

CopMonkey responds with the full content of the resource description, including its _id, as follows:

Example of an HTTP response to a "read resource description" request, containing a resource description from the authorization server:

HTTP/1.1 200 OK
Content-Type: application/json
...
{  
   "_id":"112210f47de98100",
   "name":"Steve the puppy!",
   "icon_uri":"http://www.example.com/icons/flower.png",
   "scopes":[  
      "http://photoz.example.com/dev/scopes/view",
      "http://photoz.example.com/dev/scopes/print"
   ]
}

If for some reason Photoz and CopMonkey have gotten dramatically out of sync, Photoz can ask for the list of resource identifiers CopMonkey currently knows about:

GET /rs/ HTTP/1.1
Host: as.example.com
...

CopMonkey's response might look as follows:

HTTP/1.1 200 OK
...
[  
   "112210f47de98100",
   "34234df47eL95300"
]

If Alice later changes the photo's title (user experience only) on Photoz from "Steve the puppy!" to "Steve on October 14, 2011", Photoz would use the Update method to ensure that Alice's experience of policy-setting at CopMonkey remains consistent with what she sees at Photoz. Following is an example of this request. Note that the entire updated resource description has to be included in the PUT request.

PUT /rs/112210f47de98100 HTTP/1.1
Content-Type: application/json
Host: as.example.com
...
{  
   "name":"Steve on October 14, 2011",
   "icon_uri":"http://www.example.com/icons/flower.png",
   "scopes":[  
      "http://photoz.example.com/dev/scopes/view",
      "http://photoz.example.com/dev/scopes/print"
   ]
}

CopMonkey would respond as follows.

HTTP/1.1 201 Created
Content-Type: application/json
...
{  
   "_id":"112210f47de98100"
}

There are other reasons Photoz might want to update resource descriptions, having nothing to do with Alice's actions or wishes. For example, it might extend its API to include new features such as photo resizing, and want to add new scopes to all of Alice's and other users' resource descriptions.

if Alice later decides to entirely remove sharing protection (user experience only) on this photo while visiting Photoz, ensuring that the public can get access without any protection, Photoz is responsible for deleting the relevant resource registration, as follows:

DELETE /rs/112210f47de98100 HTTP/1.1
Host: as.example.com
...

CopMonkey would respond as follows.

HTTP/1.1 204 No content
...

8. Acknowledgments

The following people made significant text contributions to the specification:

Additional contributors to this specification include the Kantara UMA Work Group participants, a list of whom can be found at [UMAnitarians].


9. References

9.1 Normative References

[BCP195]Sheffer, Y., “Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)”, May 2015, <https://tools.ietf.org/html/bcp195>.
[RFC2119]Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <http://www.rfc-editor.org/info/rfc2119>.
[RFC6749]Hardt, D., Ed., “The OAuth 2.0 Authorization Framework”, RFC 6749, DOI 10.17487/RFC6749, October 2012, <http://www.rfc-editor.org/info/rfc6749>.
[RFC7159]Bray, T., Ed., “The JavaScript Object Notation (JSON) Data Interchange Format”, RFC 7159, DOI 10.17487/RFC7159, March 2014, <http://www.rfc-editor.org/info/rfc7159>.
[RFC7591]Richer, J., Ed., Jones, M., Bradley, J., Machulak, M., and P. Hunt, “OAuth 2.0 Dynamic Client Registration Protocol”, RFC 7591, DOI 10.17487/RFC7591, July 2015, <http://www.rfc-editor.org/info/rfc7591>.

9.2 Informative References

[OpenIDConnect]Sakimura, N., “OpenID Connect Core 1.0 incorporating errata set 1”, November 2014, <http://openid.net/specs/openid-connect-core-1_0.html>.
[UMA]Maler, E., “User-Managed Access (UMA) Profile of OAuth 2.0”, March 2017, <http://docs.kantarainitiative.org/uma/wg/uma-core-2.0-20.html>.
[UMAnitarians]Maler, E., “UMA Participant Roster”, 2017, <https://kantarainitiative.org/confluence/display/uma/Participant+Roster>.

Authors' Addresses

Eve Maler (editor)
ForgeRock
EMail: eve.maler@forgerock.com

Maciej Machulak
Self
EMail: maciej.machulak@gmail.com

Justin Richer
Bespoke Engineering
EMail: justin@bspk.io