FAPI Basics Supplement: Integration with Reference Implementations

Preface

This document describes how to evaluate FAPI-compliant authorization flow and API request using an an Authlete service, that has been configured with settings described in Financial-grade API (FAPI) Basics (hereinafter “FAPI Basics”), and other components including Authlete’s reference implementations.

Components in this tutorial

Prerequisites

Before doing this, you are expected to have knowledge of OpenID Connect and Authlete by running through the following tutorial.

You also need to have an Authlete service that has been configured with settings in accordance with FAPI Basics below. Run the instructions (A complete example walk through) in the document to make sure that your Authlete service works properly.

The following software components are used in this tutorial. Prepare your lab environment that can execute them.

  • Required for this tutorial
  • Reommended, but you can choose other alternative tools
    • Apache HTTP Server (hereinafter “Apache”): A reverse proxy for authorization server and resource server
    • curl: An API client
    • OpenSSL: A tool to generate private keys and public key certificates

Configuring an authorization server

In this section, we will cover the following tasks:

  • Deploying a java-oauth-server package
  • Testing connection

The diagram below illustrates components configured in this section. A Web browser and curl send requests to an authorization server (java-oauth-server) whose endpoint is http://localhost:8080.

Components including a configured authorization server (java-oauth-server)

Configuring java-oauth-server

We will download and configure java-oauth-server. Detailed instructions are described in its document.

Downloading the source

Download java-oauth-server using git command.

$ git clone https://github.com/authlete/java-oauth-server.git

Configuration

Edit the following entries in its configuration file, authlete.properties. Put the API key and the secret of your Authlete service that has been configured in accordance with FAPI Basics.

service.api_key = <your API key e.g. 1450...0338>
service.api_secret = <your API key e.g. VEIl...0cEs>

Starting the server

Start java-oauth-server using mvn command.

$ mvn jetty:run

Test

Connect to http://localhost:8080 using a Web browser and the following page should be displayed.

Top page of java-oauth-server

Executing FAPI authorization flow (1)

Generate a request object in accordance with instructions in FAPI Basics and craft an authorization request using the object. The following values are used in this tutorial.

{
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":"1756...3766",
"scope":"openid payment",
"exp":15549730000,
"aud":"https://as.example.com",
"claims":{
  "id_token":{
    "acr":{
      "essential":true,
      "values":["urn:example:psd2:sca"]
    }
  }
},
"nonce":"n-0S6_WzA2Mj"
eyJr...YifQ.ewoi...iCn0.ztl2...tjAA
  • Authorization request (replace the values of client_id (1756...3766) and request (eyJr...YifQ.ewoi...iCn0.ztl2...tjAA) with yours determined above)
http://as.example.com:8080/api/authorization
 ?redirect_uri=https://client.example.org/cb/example.com
 &scope=openid+payment
 &response_type=code+id_token
 &client_id=1756...3766
 &nonce=n-0S6_WzA2Mj
 &request=eyJr...YifQ.ewoi...iCn0.ztl2...tjAA

Enter the crafted request string (URL) to your Web browser’s location field.

Enter the authorization request URL

An authorization page should be displayed. Enter john / john to Login ID / Password fields and click Authorize button.

Authorization page of java-oauth-server

Your Web browser will attempt (and fail) to connect https://client.example.org/cb/example.com/#code=... and show an error page stating that the host client.example.org doesn’t exist.

Error stating client.example.org not found

Authorization response

Copy the URL of the error page like below.

https://client.example.org/cb/example.com#code=0Io2....sdkQ&id_token=eyJr...YifQ.eyJz...aiJ9.-EPu...Dkkw

Extract code parameter in the URL.

code=0Io2...sdkQ

Token request

Let’s craft a token request that contains the value of code (0Io2...sdkQ for example) and send it to java-oauth-server’s token endpoint (/api/token).

In this section, we will try to do client authentication using client_id / client_secret as follows. Replace values of these parameters with yours that have been automatically generated during FAPI Basics.

$ curl -s -X POST http://localhost:8080/api/token \
 -u '1756...3766:EXE7...UxHg' \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode 'redirect_uri=https://client.example.org/cb/example.com' \
  --data-urlencode 'code=0Io2...sdkQ'

Token response

You will receive a token response like this:

{
  "error_description": "[A244308] Because the feature of certificate-bound 
  access tokens is enabled, the client must establish a mutual TLS connection
  to the token endpoint.",
  "error": "invalid_request",
  "error_uri": "https://docs.authlete.com/#A244308"
}

Execution example

That means:

  • java-oauth-server was unable to extract any client certificates from the token request as no mutual TLS connection has been established between the client (curl command in this tutorial) and java-oauth-server
  • Thus java-oauth-server didn’t include any client certificates in a request to Authlete’s (/auth/token API)
  • So Authlete couldn’t issue any certificate-bound access tokens and made a response with A244308 error

In the next section, we will add a reverse proxy to accept client certificates from clients to solve the error.

Enabling TLS for the authorization server

In this section, we will cover the following tasks:

  • Configuring a reverse proxy
  • Configuring a client

The diagram below illustrates components configured in this section. In the previous section, both Web browsers and clients connect directly to java-oauth-server (http://localhost:8080). The destination will be switched to Apache (https://as.example.com:8443) that resides in front of the server as a reverse proxy. On the client side, curl will attempt to connect to Apache using a client certificate to get authenticated.

Enabling TLS for the authorization server

Configuring a reverse proxy (1)

FQDN settings

Add the following entry to /etc/hosts so that both Web browsers and clients can find Apache to be installed in your lab environment, using as.example.com as FQDN.

127.0.0.1 as.example.com

Installing Apache

Install Apache in your lab environment.

$ brew install httpd

This tutorial uses /usr/local/etc/httpd directory as the primary location of Apache’s configuration files.

Preparing a server certificate

Creating a private key and a public key certificate

Create an RSA private key and a public key certificate for Apache, using openssl command.

$ openssl req -x509 -newkey rsa:2048 \
 -keyout server.key -out server.crt 
 -days 3650 -sha256 -nodes \
 -subj '/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Server/CN=as.example.com'
Generating a 2048 bit RSA private key
..........................+++
....+++
writing new private key to 'server.key'
-----

Two files, server.key (a private key) and server.crt (a public key certificate) are created.

Deploying the private key and the public key certificate

Deploy these two files to arbitrary location in your lab environment. This tutorial copies them under /usr/local/etc/httpd directory.

$ sudo cp -p server.key server.crt /usr/local/etc/httpd/.
Password:

Configuring TLS for Apache

httpd.conf

We will configure basic Apache settings to:

  • Disable listening port 8080 (as java-oauth-server uses it)
  • Enable mod_proxy and mod_ssl related modules (to get Apache working as a reverse proxy and accepting TLS connection)

Actual changes to the configuration file, httpd.conf are as follows. TLS related settings are to be included from another file (extra/httpd-ssl.conf).

$ diff /usr/local/etc/httpd/httpd.conf.orig \
/usr/local/etc/httpd/httpd.conf
52c52
< Listen 8080
---
> #Listen 8080
92c92
< #LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
---
> LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
131c131
< #LoadModule proxy_module lib/httpd/modules/mod_proxy.so
---
> LoadModule proxy_module lib/httpd/modules/mod_proxy.so
134c134
< #LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
---
> LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
150c150
< #LoadModule ssl_module lib/httpd/modules/mod_ssl.so
---
> LoadModule ssl_module lib/httpd/modules/mod_ssl.so
524c524
< #Include /usr/local/etc/httpd/extra/httpd-ssl.conf
---
> Include /usr/local/etc/httpd/extra/httpd-ssl.conf

extra/httpd-ssl.conf

In this tutorial, we configure TLS and reverse proxy settings to:

  • Use as.example.com as its server name (while its listening port (8443) unchanged)
  • Meet the FAPI provisions (by using TLS 1.2 and limited sets of cypher suites)
  • Include a client certificate in X-Ssl-Cert header (so that /api/token endpoint of java-oauth-server can extract the value and include it in a request to Authlete API (/auth/token API), if it is set in the request from the reverse proxy)
  • Work as a reverse proxy that forwards requests accepted at https://as.example.com:8443/ to http://localhost:8080/ (java-oauth-server)

Changes to the configuration file, extra/httpd-ssl.conf are as follows.

$ diff /usr/local/etc/httpd/extra/httpd-ssl.conf.orig \
 /usr/local/etc/httpd/extra/httpd-ssl.conf
125c125
< ServerName www.example.com:8443
---
> ServerName as.example.com:8443
289a290,302
> SSLEngine on
> SSLProtocol TLSv1.2
> SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256
> SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
> SSLCACertificateFile "/usr/local/etc/httpd/server.crt"
> SSLVerifyClient optional_no_ca
> SSLOptions +StdEnvVars +ExportCertData
> SSLCompression off
> RequestHeader set X-Ssl-Cipher "%{SSL_CIPHER}e" env=SSL_CIPHER
> RequestHeader set X-Ssl-Cert "%{SSL_CLIENT_CERT}e" env=SSL_CLIENT_CERT
> ProxyPreserveHost on
> ProxyPass "/" "http://localhost:8080/"
> ProxyPassReverse "/" "http://localhost:8080/"

Starting Apache

Start the Apache using apachectl command.

$ apachectl start

Once it’s started, connect to https://as.example.com:8443 using your Web browser and confirm if the content is the same as http://localhost:8080, the top page of java-oauth-server.

In the next section, we wlll prepare a private key and a public key certificate for a client (curl) that makes a token request.

Settings for a client

Preparing a client certificate

Creating an RSA private key and a public key certificate

Execute openssl command as follows to create a private key and a public key certificcate.

$ openssl req -x509 -newkey rsa:2048 \
 -keyout client.key -out client.crt 
 -days 3650 -sha256 -nodes \
 -subj '/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Server/CN=client.example.org'
Generating a 2048 bit RSA private key
..........................+++
....+++
writing new private key to 'client.key'
-----

Two files, client.key (a private key) and client.crt (a public key certificate) are created. We will use them to make token requests with curl.

Authorization request and response

Make an authorization request in the same way as Executing FAPI authorization flow (1).

Extract a value of code parameter from an authorization response. The value in this tutorial is as follows.

7Y5h...zp_4

Token request

Craft a token request and send it to the token endpoint of java-oauth-server (/api/token). The different parts from the previous example (Executing FAPI authorization flow (1)) are:

  • Endpoint URL: using https://as.example.com:8443 instead of http://localhost:8080
  • Client authentication method: using the private key (client.key) and the public key certificate (client.crt) instead of client_secret

Thus the curl command to be executed is like below. (replace the values of client_id and code with appropriate ones in your lab environment)

$ curl --insecure -X POST https://as.example.com:8443/api/token \
--key client.key --cert client.crt \
--data-urlencode 'client_id=1756...3766' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://client.example.org/cb/example.com' \
--data-urlencode 'code=7Y5h...zp_4'

Token response

You should receive a token response like below.

{
  "access_token": "nbN3...LAU8",
  "refresh_token": "SWUu...V8_w",
  "scope": "openid payment",
  "id_token": "eyJr...YifQ.eyJz...qIn0.GPJr...STPQ",
  "token_type": "Bearer",
  "expires_in": 86400
}

Now you have successfully got an access token (nbN3...LAU8 for example) through mutual TLS connection. In the backstage, Authlete has made the access token bound to the client certificate.

In the next section, we will attempt to make an API request with an access token to a resource server.

Configuring a resource server

In this section, we will cover the following tasks:

  • Deploying a java-resource-server package
  • Testing connection

The diagram below illustrates components configured in this section.

Components including a configured resource server (java-resource-server)

Configuring java-resource-server

We will download and configure java-resource-server. Instructions are described in its document.

Downloading the source

Download java-resource-server using git command.

$ git clone https://github.com/authlete/java-resource-server.git

Configuration

Edit the following entries in its configuration file, authlete.properties. Put the API key and the secret that are the same values as specified for java-oauth-server.

service.api_key = <your API key e.g. 1450...0338>
service.api_secret = <your API key e.g. VEIl...0cEs>

Starting the server

Start java-resource-server using mvn command.

$ mvn jetty:run

Test

Connect to http://localhost:8081 using a Web browser and the following page should be displayed.

Top page of java-resource-server

Executing an API request (1)

Let’s try to make an API request with an access token to “Country Endpoint” of java-resource-server, using curl command.

The access token can be obtained by doing the flow described in Executing FAPI authorization flow (2). In this tutorial, the following value is used.

6Ivp...LiM4

Make a request with the access token specified in Authorization header. The examples below show the request and the subsequent response. -v option is added to curl command so that it can enable verbose output especially header information.

$ curl -v -X GET http://localhost:8081/api/country/JP \
-H 'Authorization: Bearer 6Ivp...LiM4' 
...
> GET /api/country/JP HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.64.1
> Accept: */*
> Authorization: Bearer 6Ivp...LiM4
> 
< HTTP/1.1 401 Unauthorized
< Date: Mon, 18 May 2020 02:22:32 GMT
< Cache-Control: no-store, no-transform
< Pragma: no-cache
< WWW-Authenticate: Bearer error="invalid_token",
 error_description="[A065304] The client failed to present 
 a certificate and the access token is MTLS bound.",
 error_uri="https://docs.authlete.com/#A065304"
< Content-Length: 0
< Server: Jetty(9.3.7.v20160115)
...

That response means:

  • java-resource-server was unable to extract any client certificates from the token request as no mutual TLS connection has been established between the client (curl command) and java-resource-server
  • Thus, java-resource-server didn’t include any client certificates in a request to Authlete’s /auth/introspection API
  • So, Authlete couldn’t check the binding between the access token and its corresponding client certificate and made a response with A065304 error

In the next section, we will configure additional settings for the reverse proxy.

Enabling TLS for the resource server

In this section, we will cover the following tasks that are basically the same as the settings for java-oauth-server done in the former section:

  • Additional settings for the reverse proxy

The diagram below illustrates components configured in this section. curl will attempt to connect to Apache (https://rs.example.com:8443) using a client certificate to get authenticated.

Enabling TLS for the resource server

Configuring a reverse proxy (2)

FQDN settings

Modify the entry that has been added to /etc/hosts during Configuring a reverse proxy (1) so that curl can connect to Apache using rs.example.com.

127.0.0.1 as.example.com rs.example.com

Configuring TLS for Apache

extra/httpd-ssl.conf

Add the following entries to the extra/httpd-ssl.conf file. These settings, the same as Configuring a reverse proxy (1) except the server name and destinations of the reverse proxy, are to:

  • Declare these settings are applied to rs.example.com:8443
  • Meet the FAPI provisions (by using TLS 1.2 and limited sets of cypher suites)
  • Include a client certificate in X-Ssl-Cert header (so that /api/country endpoint of java-resource-server can extract the value and include it in a request to Authlete API (/auth/introspection API), if it is set in the request from the reverse proxy)
  • Work as a reverse proxy that forwards requests accepted at https://rs.example.com:8443/ to http://localhost:8081/ (java-resource-server)

The additional entries to the configuration file, extra/httpd-ssl.conf are as follows.

<VirtualHost _default_:8443>
DocumentRoot "/usr/local/var/www"
ServerName rs.example.com:8443
ServerAdmin you@example.com
ErrorLog "/usr/local/var/log/httpd/error_log"
TransferLog "/usr/local/var/log/httpd/access_log"
SSLEngine on
SSLProtocol TLSv1.2
SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256
SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
SSLCertificateFile "/usr/local/etc/httpd/server.crt"
SSLCACertificateFile "/usr/local/etc/httpd/server.crt"
SSLVerifyClient optional_no_ca
SSLOptions +StdEnvVars +ExportCertData
SSLCompression off
RequestHeader set X-Ssl-Cipher "%{SSL_CIPHER}e" env=SSL_CIPHER
RequestHeader set X-Ssl-Cert "%{SSL_CLIENT_CERT}e" env=SSL_CLIENT_CERT
ProxyPreserveHost on
ProxyPass "/" "http://localhost:8081/"
ProxyPassReverse "/" "http://localhost:8081/"
</VirtualHost>                                  

Starting Apache

Stop and start the Apache using apachectl command.

$ apachectl stop && apachectl start

After the restart, connect to https://rs.example.com:8443 using your Web browser and confirm if the content is the same as http://localhost:8081, the top page of java-resource-server.

Executing an API request (2)

Make an API request using curl command in the same way as Executing an API request (1).

The access token can be obtained by doing the flow described in Executing FAPI authorization flow (2). In this tutorial, the following value is used.

6Ivp...LiM4

Make a request with the access token specified in Authorization header. The different parts from the previous example (Executing an API request (1)) are:

  • Endpoint URL: using https://rs.example.com:8443 instead of http://localhost:8081
  • Client authentication method: using the private key (client.key) and the public key certificate (client.crt) in addition to the access token

Thus the curl command to be executed is like below.

$ curl -v -X GET http://localhost:8081/api/country/JP \
-H 'Authorization: Bearer 6Ivp...LiM4' \
--key client.key --cert client.crt --insecure
...

You should receive a successful API response like below.

{
  "name": "Japan",
  "alpha2": "JP",
  "alpha3": "JPN",
  "numeric": 392,
  "currency": "JPY"
}

Execution example

It means that:

  • java-resource-server extracted the client certificate from the mutual TLS connection and sent it to Authlete,
  • Authlete validated the binding between the client certificate and the access token, and
  • java-resource-server has processed the API request as expected.

Conclusion

In this tutorial, we confirmed FAPI-compliant authorization flow and API request using a FAPI-enabled Authlete service, reference implementations (java-oauth-server and java-resource-server) and a reverse proxy.