Password grant handler SPI

1. Introduction

The OAuth 2.0 resource owner password credentials grant (meaning the end-user's username and password) is intended for highly-trusted applications (e.g. the client app is part of the device OS) or when use of a more secure grant type, such as an authorisation code, is not possible.

For a client to make an access token request with the end-user's username and password it must be explicitly registered for the password grant. The client itself is authenticated with the client_id and client_secret credentials obtained during registration, and may optionally specify an explicitly requested scope.

The Connect2id server, being an OpenID Connect provider (OP), can be instructed to include an ID token in the access token response.

You can find more information in the OAuth 2.0 spec and in our article on handling password grants in the Connect2id server.

2. Password grant handler SPI

The Connect2id server offers a flexible Java Service Provider Interface (SPI) for validating and grant username / password and determining the authorisation properties (scope values, etc).

How does the SPI get invoked?

  1. The Connect2id server receives an access token request with a password grant.

  2. The Connect2id server verifies the client credentials (client_id and client_secret) and whether use of the grant is permitted.

  3. On success the SPI gets invoked to verify the grant username / password and if these are correct to determine the authorisation properties, such as scope values, access token lifetime and encoding.

The password handler SPI is defined in the Connect2id server toolkit, which is open sourced (Apache 2.0) and can be freely used to create custom handler implementations:

https://bitbucket.org/connect2id/server-sdk

Features of the password grant handler SPI:

  • Enables initialisation of the handler from a chosen configuration file.

  • The handler is passed the supplied username and password, as well as the registered client ID and metadata, which can then be used to determine the authorisation properties (scope values, etc).

  • Enables implementations to release resources on Connect2id server shutdown.

Important: The Connect2id server can load multiple grant handlers at startup, but only one may be enabled at any one time.

3. Available implementations

3.1 Web-based handler

The Connect2id server comes with a ready SPI implementation which delegates processing of the grant to a remote web service.

Benefits of using this handler approach:

  • You can use any language or framework to implement the username / password verification and the authorisation logic, leveraging your existing IT resources.

  • You can update the handler logic without having to restart or otherwise affect the Connect2id server.

The interface for the web service is simple -- a single HTTP POST method, using JSON objects to convey the request and response parameters.

The web-based handler connector and API are provided in an open source (Apache 2.0) package. It's Git repo is at

https://bitbucket.org/connect2id/password-grant-web-api

There is also an example web service implementation available, which uses an LDAP directory to verify the username / password credentials and some simple logic to determine the allowed scope values:

https://bitbucket.org/connect2id/password-grant-web-api-example

3.1.1 Configuration

To set up your web handler for processing password grants you need to provide the Connect2id server with the following configuration details:

  • The URL of your web service for checking the grant username / password and determining the authorisation scope on success.

  • A long-lived bearer access token for the web service.

  • HTTP connect and read timeouts.

The configuration is stored in WEB-INF/passwordGrantHandlerWebAPI.properties.

Example configuration:

op.grantHandler.password.webAPI.enable = true
op.grantHandler.password.webAPI.url = https://c2id.com/password-grant-handler
op.grantHandler.password.webAPI.apiAccessToken = ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6
op.grantHandler.password.webAPI.connectTimeout = 250
op.grantHandler.password.webAPI.readTimeout = 500

Any configuration file property can be overridden by setting a system-wide property with a matching key, e.g. by using the optional -D argument at JVM startup:

-Dop.grantHandler.password.webAPI.enable=false

3.1.2 Web interface

For each access token request with a password grant the Connect2id server will first check whether the client application has valid client_id and client_secret credentials and whether the client registration permits use of the grant. Only then will the server call the SPI (the connector to the web service) to process the grant.

The web service is expected to perform the following steps:

  • Check the username / password, e.g. by making a connection to an LDAP directory or some other database.

  • On success determine the authorisation scope. The logic for that can use the resolved user ID, the requested scope (if any) and the registration client ID and metadata as inputs.

The web service interface is specified as follows:

HTTP POST

Header parameters:

  • Authorization The configured bearer access token for the web service (see op.grantHandler.password.webAPI.apiAccessToken).

  • Content-Type Will be set to application/json.

Body:

  • A JSON object with the following members:
    • username {string} The username, as specified in the access token request.
    • password {string} The user password, as specified in the access token request.
    • [ scope ] {string array} The requested scope values, as specified in the access token request, empty array or omitted if none.
    • client {object} JSON object containing the client_id and registered metadata for the client. A boolean confidential parameter is also included, to indicate whether the client is confidential or public.

Success:

  • Code: 200

  • Content-Type: application/json

  • Body: {object} A JSON with object with the following authorisation properties:

    • sub {string} The identifier of the authenticated subject (end-user).
    • scope {string array} An array of one or more authorised scope values for the access token. May be different from the originally requested scope values.
    • [ audience ] {string array} Optional explicit list of audiences for the access token, omitted if none.
    • [ long_lived = false ] {true|false} Controls the authorisation lifetime: true for a long-lived authorisation (implies persistence), false for a short-lived one (default value).
    • [ access_token ] {object} Optional JSON object with settings for the access token.
      • [ lifetime ] {integer} The preferred access token lifetime, in seconds. If omitted defaults to the configured lifetime.
      • [ encoding ] {"INTEGER"|"SELF_CONTAINED"} The preferred access token encoding. If omitted defaults to the configured encoding.
    • [ issue_refresh_token = false ] Controls the refresh token issue: true to allow refresh token issue (requires a long-lived authorisation), false to only issue an access token (default value).
    • [ issue_id_token = false ] Controls the ID token issue: true to issue an ID token, false to omit it.
    • [ auth_time ] {integer} The time of the subject (end-user) authentication, as number of seconds since the Unix epoch. If omitted it will be set to now. Applies only if an ID token is issued.
    • [ acr ] {string} The Authentication Context Class Reference (ACR), omitted if not specified. Applies only if an ID token is issued.
    • [ amr ] {string array} The Authentication Methods Reference (AMR) list, omitted if not specified. Applies only if an ID token is issued.
    • [ claims ] {string array} Optional array of the authorised OpenID Connect UserInfo and other claims, omitted if none.
    • [ claims_locales ] {string array} Optional array of the claims locales, omitted if not specified.
    • [ preset_claims ] {object} Optional JSON object specifying preset OpenID Connect claims to return with the ID token and at the UserInfo endpoint:
      • [ id_token ] {object} Additional or preset claims to be included
        in the ID token, omitted if none.
      • [ userinfo ] {object} Additional or preset claims to be included
        in the UserInfo response, omitted if none.
    • [ claims_transport = "USERINFO" ] {"USERINFO"|"ID_TOKEN"} The preferred claims transport, defaults to USERINFO if omitted.

Errors:

Example request from a confidential client app with ID 000123, citing the username, password and scope parameters from the access token request:

POST /password-grant-handler HTTP/1.1
Authorization: Bearer ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6
Content-Type: application/json

{
  "username" : "bob",
  "password" : "secret",
  "scope"    : [ "openid", "email", "profile" ],
  "client"   : { "client_id"                  : "000123",
                 "client_name"                : "My Test App",
                 "confidential"               : true,
                 "grant_types"                : [ "password" ],
                 "response_types              : [],
                 "token_endpoint_auth_method" : "client_secret_basic",
                 "application_type"           : "web" }
}

Example response indicating successful authorisation (an ID token is also to be issued in addition to the access token):

HTTP/1.1 200 OK
Content-Type: application/json

{
  "sub"            : "67890",
  "scope"          : [ "openid", "email", "profile" ],
  "issue_id_token" : true
}

Example response indicating bad username / password credentials:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error"             : "invalid_grant",
  "error_description" : "Bad username/password"
}

4. Receiving support

Our Connect2id support team is available if you need help with configuring the web-based password grant handler or implementing your own.