OAuth 2.0 / OpenID Connect client registration explained
The need for registration
The OAuth 2.0 framework as well as OpenID Connect, the new standard for Single Sign-On (SSO) which builds on top of OAuth 2.0, require client applications to have an existing registration with the OAuth 2.0 / OpenID Connect server before requests can be processed.
What does the registration data consists of?
-
The unique identifier (
client_id
) issued to the client application. -
The client application is also typically issued a secret (password) to authenticate itself at the token endpoint. Other client authentication methods, such as assertions based on a public RSA or EC key belonging to the client, can also be supported.
-
One or more redirection URIs for the client application. The server requires a redirection URI to send the browser back to the client application once the end-user has given their consent (or not).
-
Various metadata about the client application, such as name, logo, privacy policy and terms of service, to be displayed to the end-user when prompting for their consent.
The registration specs
The importance of having a standard server endpoint for client registration was recognised early on by the WGs behind OAuth and OpenID Connect. They have published the following documents to address this:
-
OAuth 2.0 Dynamic Client Registration Protocol – an RFC draft (fairly stable now) defining a base RESTful server endpoint and JSON object to facilitate registration of common OAuth 2.0 clients.
-
OAuth 2.0 Dynamic Client Registration Management Protocol – extends the above spec with RESTful operations to permit retrieval (via GET), modification (PUT) and deletion (DELETE) of an OAuth 2.0 client registration.
-
OpenID Connect Dynamic Client Registration 1.0 – specifies additional metadata for OAuth 2.0 clients that are OpenID Connect clients.
The OAuth 2.0 / OpenID Connect SDK for Java has included support for client registration since 2012 and in its latest 3.3 release can also handle software statements.
Discovering the registration endpoint
If an OAuth 2.0 or OpenID Connect server supports the standard registration endpoint how should that be advertised?
The traditional way has been to publish the registration URL in the online docs for the client app developers. This is what social login providers typically do.
A much more efficient and standards-compliant approach is to make the registration endpoint URL discoverable via Web Finger. This is as simple as detailing the server’s endpoints and capabilities in a special JSON file at the following URL:
http://[server-host]/.well-known/openid-configuration
That way developers can quickly get the server details they want with a simple tool such as curl. Moreover, it’s now possible to build client apps that can automatically discover the IdP / authorisation server for a given end-user and then let the app register itself. Automated IdP discovery and client app registration is the future of the web.
Open or managed registration
Access to the client registration endpoint can be open or managed:
-
Open registration – Registration is open to all clients. This is intended for social login providers as well as for IdP’s and services that are set up for automatic discovery. Requests should be rate limited to prevent DoS attacks.
-
Managed registration – An initial OAuth 2.0 access token is required for registration. This access token is issued after the client application has passed an approval or screening process.
Example registration request
Here is an example HTTP request (taken from the Connect2id server API docs) to register a client application with the given redirection URI, name and logo URI:
POST /client-reg HTTP/1.1
Host: server.c2id.com
Authorization: Bearer ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6
Content-Type: application/json
{
"redirect_uris" : [ "https://client.example.org/callback" ],
"client_name" : "My Cool App",
"logo_uri" : "https://client.example.org/logo.png"
}
Example registration response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"client_id" : "s6BhdRkqt3",
"client_secret" :"JBGuX8sIsPhL2aiHtdo_rb8JIMyTjHkLgfVB_zYf2NQ",
"client_secret_expires_at" : 1577858400,
"registration_access_token" : "SQvs1wv1NcAgsZomWWif0d9SDO0GKHYrUN6YR0ocmN0",
"registration_client_uri" : "https://c2id.com/client-reg/s6BhdRkqt3",
"client_name" : "My Cool App",
"logo_uri" : "https://client.example.org/logo.png",
"application_type" : "web",
"grant_types" : [ "authorization_code" ],
"response_types" : [ "code" ],
"redirect_uris" : [ "https://client.example.org/callback" ],
"token_endpoint_auth_method" : "client_secret_basic",
"id_token_signed_response_alg" : "RS256",
"subject_type" : "public"
}
The response contains the generated client ID and secret. It also specifies the permitted OAuth 2.0 grant and response types for the client application, as provisioned by the OpenID Connect server.
Registration using the OAuth 2.0 / OpenID Connect SDK
Registration using the Java SDK is a simple process; the SDK ensures the request is properly constructed and has a ready class for representing and parsing the client metadata.
// The client registration endpoint
URI regEndpoint = new URI("https://server.c2id.com/client-reg");
// Compose the client metadata
OIDCClientMetadata metadata = new OIDCClientMetadata();
metadata.setRedirectionURI(new URI("https://client.example.org/callback"));
metadata.setName("My Cool App");
metadata.setURI(new URI("http://client.example.org"));
metadata.setLogoURI(new URI("http://client.example.org/logo.png"));
// Access token for the request, if required
BearerAccessToken accessToken = new BearerAccessToken("ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6");
// Make the registration request
OIDCClientRegistrationRequest request = new OIDCClientRegistrationRequest(
regEndpoint,
metadata,
accessToken);
HTTPResponse httpResponse = request.toHTTPRequest().send();
// Parse the response
ClientRegistrationResponse response = ClientRegistrationResponse.parse(httpRequest);
if (response instanceof ClientRegistrationErrorResponse) {
// We have a problem
ClientRegistrationErrorResponse errorResponse = (ClientRegistrationErrorResponse)response;
System.out.println(errorResponse.getErrorObject());
}
OIDCClientInformationResponse successResponse = (OIDCClientInformationResponse)response;
OIDCClientInformation clientInfo = successResponse.getOIDCClientInformation();
System.out.println("Client ID: " + clientInfo.getID());
System.out.println("Client secret: " + clientInfo.getSecret().getValue());