[JAVA] Procedure for building an authorization server using Authlete (CIBA compatible version)

What is OAuth / OpenID Connect CIBA (Client Initiated Backchannel Authentication)?

I went to "OAuth & OIDC Study Group (FAPI & CIBA Special!)" held on 3/28 (Thursday).

The organizer is Authlete, a company that provides Baas that can build OAuth and OpenID Connect (OIDC) authorization servers. Since the cloud service of Authlete was compatible with CIBA, which is a new specification of OAuth / OIDC, it was a study session that also served as an introduction to the cloud service.

Aside from my OAuth, CIBA was my first look, so I learned a lot. Thank you to the people at Authlete who hosted the study session.

This time it was about FAPI (Financial-grade API) and CIBA (Client Initiated Backchannel Authentication), but regarding FAPI, "Restrictions when using OAuth / OIDC in places with higher security requirements such as the financial industry" is specified. You can think of it as a converted product. An easy-to-understand example is that OAuth Client client authentication when taking a token is not basic authentication, so prepare an X.509 certificate, etc. ..

CIBA stands for OpenID Connect Client Initiated Backchannel Authentication Flow --Core 1.0. Whereas OAuth / OIDC (so-called RFC 6749) was ** a premise processing sequence using WEB browser redirection ** [^ 1] , CIBA has a processing sequence of ** sending an authentication request to the back channel authentication endpoint ** and ** notifying the pre-registered user's authentication device "Agree?" **. Is a feature. ..

[^ 1]: It is impressive that the person who was on the stage explained this processing sequence as ** OAuth Dance ** :-)

This allows, for example,

--When you tell Alexa "I'll buy xxx, pay ¥ 3,000!", The Alexa app sends an authentication request to the authorization server. --The authorization server notifies the authentication device (smartphone app) of "Allow?" --If the user presses OK on the smartphone, the payment will be completed.

What a processing sequence that does not require browser redirection can be realized. Well, it's really convenient.

The specific processing sequence looks like this. ..

image.png

Intro

Well, before, I had an opportunity to touch the Authlete service in a certain development project, and I posted the construction procedure at that time to Qiita, although it is about Hello World.

-Building an authorization server using Authlete and communicating from an OAuth client (Web application)

This time, I was lucky enough to have the opportunity to touch the CIBA-compatible Authlete service, so I will record the construction procedure as a reminder. (Thank you!)

Prerequisite knowledge

It is a prerequisite knowledge for reading the construction procedure below, but I will proceed on the assumption that I have OAuth / OIDC and CIBA to some extent. For example, you generally know the processing sequence of Authorization Code Grant Flow or CIBA Flow.

Now, the following is the authorization code grant flow so-called OAuth Dance processing sequence. The position of Authlete is also written together.

--Authorization Code Grant Flow processing sequence

image.png

This time, by touching the CIBA-compatible Authlete, I will try to implement the following ** CIBA Flow processing sequence (described earlier) and get an access token **. For authentication devices, there is an authentication device simulator site, so use that site.

--CIBA Flow processing sequence

image.png

In addition, there are three modes of poll, ping or push as a way for the OAuth Client to know the "result of the user's operation on the authentication device (authorization, denial, timeout etc.)". /openid-client-initiated-backchannel-authentication-core-1_0.html#rfc.section.5), but ** this time I will use "poll" **. In this mode, the OAuth Client polls the authorization server to know the user's operation results. I haven't tried ping and push yet, but in the case of ping, the authorization server sends an HTTPS notification saying "Come to get it, good", and push seems to send the access token as it is. (Reference: 9. Client Notification Endpoint)

Prerequisite environment

The environment around this time is as follows.

$ sw_vers
ProductName:    Mac OS X
ProductVersion:    10.14.4
BuildVersion:    18E226
$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
$ mvn --version
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T03:41:47+09:00)
$
$ curl --version
curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.5 zlib/1.2.11 nghttp2/1.24.1
$
$ jq --version
jq-1.6
$

I'm doing it above, but if maven, curl, jq (and git) work, I think anything including the OS is OK.

Preparation

In addition, although it is a character such as various servers, in previous article

Use Server name URL
Authorization server. A server that manages authorization information for each user ID. java-oauth-server http://oauth.example.com:8080/
Resource server. A server with data and functions for each user ID. java-resource-server http://resource.example.com:8081/
Web application. Web application that uses resources of resource server java-oauth-client http://client.example.com:8082/

However, this time, since the article is about getting the access token (or id_token), the resource server is omitted and the curl command is used as the OAuth Client. Therefore, it has a simple structure as shown below.

Use Server name URL
Authorization server. A server that manages authorization information for each user ID. java-oauth-server http://localhost:8080/
Resource server. A server with data and functions for each user ID. abridgement -
OAuth Client. An app that uses resources from the resource server. This time curl -

Rough flow

The general flow is basically the same as the conventional usage of Authlete.

  1. Sign up for Authlete
  2. Authorization server registration / client registration
  3. Construction of authorization server, construction of resource server (omitted)
  4. Communication from the web application (until the access token is taken)

However, as described in the article Making a CIBA-compatible authorization server using Authlete (Human in) , It seems that you need to contact Authlete to sign up for your account.

Therefore, in this article, the account has been signed up, and the following information has been obtained from the CIBA-compatible Authlete site (usually https://so.authlete.com/, but the CIBA-compatible version has a different URL). I will proceed on the premise that.

--API key / API secret of the authorization server associated with that account

API key API secret
116xxxxxxxx sZUkxxxxxxxxxxxxxxxxxxx

--Client ID / client secret of OAuth Client registered in the authorization server

Client ID Client secret
249xxxxxxx WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

By the way, you can check the API key / API secret of the authorization server from the following service list >> service details. image.png

You can also check the client ID / client secret of the OAuth Client registered on the authorization server from the app list >> app details on the client app developer console. image.png

Environmental setting

Now, here are some preferences to enable CIBA on the Authlete website.

Settings on the authorization server side

The site (service administrator console) that confirmed the API key / API secret on the authorization server side Go to the "Service List >> Service Details >> CIBA" tab and go to

item value
Token delivery mode to support POLL,PING,PUSH
User code support to support

Choose.

image.png

Settings on the OAuth Client side

Transition to "App list >> App details" on the site (client app developer console) where the client ID / client secret of OAuth Client is confirmed.

On the "Basic Information" tab

item value
Client type CONFIDENTIAL

Choose.

image.png

On the "Authorization" tab

item value
Client authentication method CLIENT_SECRET_BASIC

Choose.

image.png

On the "CIBA" tab

item value
Token delivery mode POLL
User code request Request

Choose.

image.png

This completes the environment settings for the authorization server and OAuth Client when operating in POLL mode.

Try to move

Build an authorization server

First, build an authorization server. Clone the GitHub repository and set the API key, API secret, and base_url in the configuration file (authlete.properties).

$ git clone https://github.com/authlete/java-oauth-server.git
$ cd java-oauth-server
$ cat authlete.properties
service.api_key =116xxxxxxxx ← Please change to the correct API key
service.api_secret =sZUkxxxxxxxxxxxxxxxxxxx ← Please change to the correct API secret
base_url = https://api.authlete.com ← Please change to a URL that supports CIBA(Contact Authlete for the value)

start up.

$ mvn clean jetty:run  -Dauthlete.ad.workspace=masatomix/testProject ← Argument is a value to be set in the simulator that will come later

This completes the construction of the authorization server.

Authentication device preparation

The authorization server that receives the backchannel authentication request sends a notification to the authentication device "Can I allow it?", But the code of the cloned authorization server is [Authlete CIBA Simulator](https: //) by default. Notifications are now sent to cibasim.authlete.com/). So we will set up the simulator.

Go to https://cibasim.authlete.com/ and set the argument above,

Namespace Project
masatomix testProject

Specify and click "Create". (The screen capture below is "Open" after it is created.)

image.png

Then, the screen will change to the Authentication Device (AD) simulator screen. The authorization server provided by Authlete has dummy user authentication coded on it.

User ID User Code
1001 675325

You can log in with. Therefore, specify the User ID "1001" as shown in the capture and click "Launch AD simulator".

image.png

The simulator is waiting. Since OAuth authentication / authorization is performed on this screen, please leave the screen open (it seems that a push notification will come here via WebSocket when the authorization server receives a backchannel authentication request).

image.png

By the way, if you open this screen on your smartphone, you will feel a sense of reality. When you send an authentication request from curl on a PC that has nothing to do with it, it looks like a notification will come to your smartphone. image.png

Send backchannel authentication request

Now, let's send an authentication request to the authorization server.

$ clientId=249xxxxxxx ← Client ID
$ clientSecret=WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ← Client secret
$ curl http://localhost:8080/api/backchannel/authentication -X POST \
--user ${clientId}:${clientSecret} \
-d 'login_hint=1001' \
-d 'user_code=675325' \
-d 'scope=openid' 
{
  "auth_req_id": "Xe250q9AoSdUL_xohrPm8txYNUo8VlddhLv1ENUY6SM",
  "interval": 5,
  "expires_in": 600
}
$

I got the auth_req_id (authentication request ID). Make a note of it as you will use it later.

Authentication process on the authentication device

When you send the authentication request, you should see the authorization screen that you are familiar with with OAuth in the simulator. Select "Allow" this time. image.png

Send token request

Now that you have the user's authorization, you can use the auth_req_id you just saw. Now that you are ready to get the token, send a token request.

$ clientId=249xxxxxxx ← Client ID
$ clientSecret=WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ← Client secret
$ curl http://localhost:8080/api/token -X POST \
--user ${clientId}:${clientSecret} \
-H 'Content-type: application/x-www-form-urlencoded' \
-d 'auth_req_id=Xe250q9AoSdUL_xohrPm8txYNUo8VlddhLv1ENUY6SM' \
-d 'grant_type=urn:openid:params:grant-type:ciba'

{
"access_token":"ldofpBo8hO67CNr4sE_Cibt1FYZ8RpQCIPVlUaeokks",
"refresh_token":"U0JyhKdXh-h3h6hDLiu3eart9RpEwnSClMtgGjAFu5o",
"scope":"openid",
"id_token":
"eyJhbGciOiJIUzI1NiJ9.eyJhdF9oYXNoIjoiRkxwdFBEaWlMbUcwdkVLWFZndjBUZyIsInN1YiI6IjEwMDEiLCJhdWQiOiIyNDkzOTMxNjU5OTciLCJhdXRoX3RpbWUiOjE1NTM4NTE3MjQsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTUzOTM4MTM0LCJpYXQiOjE1NTM4NTE3MzR9.7G_VfqlSlDd0cOSjZaRorSrbcH3PoKneM_YalCpFHig","token_type":"Bearer","expires_in":86400
}

You got an access token! Since the scope is set to openid this time, id_token is also obtained.

$ echo eyJhdF9oYXNoIjoiRkxwdFBEaWlMbUcwdkVLWFZndjBUZyIsInN1YiI6IjEwMDEiLCJhdWQiOiIyNDkzOTMxNjU5OTciLCJhdXRoX3RpbWUiOjE1NTM4NTE3MjQsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTUzOTM4MTM0LCJpYXQiOjE1NTM4NTE3MzR9 | base64 -D | jq
{
  "at_hash": "FLptPDiiLmG0vEKXVgv0Tg",
  "sub": "1001",
  "aud": "249393165997",
  "auth_time": 1553851724,
  "iss": "https://authlete.com",
  "exp": 1553938134,
  "iat": 1553851734
}

Actually poll

Since this process is done in poll among the push, ping, and poll modes, you will not be notified if the authentication request is OK. So in reality, it seems that the app will need to poll after sending the authentication request.

For example, try running the following shell.

$ cat ciba_request.sh
#!/bin/bash

clientId=249xxxxxxx ← Client ID
clientSecret=WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ← Client secret

login_hint=1001
user_code=675325

backchannelReq=`cat << EOS
curl http://localhost:8080/api/backchannel/authentication -X POST \
--user ${clientId}:${clientSecret} \
-d 'login_hint=${login_hint}' \
-d 'user_code=${user_code}'  \
-d 'scope=openid' -s | jq .auth_req_id -r
EOS
`

auth_req_id=`eval "${backchannelReq}"`
echo ${auth_req_id}

for i in {0..3}; do
    sleep 3
    tokenReq=`cat << EOS
curl http://localhost:8080/api/token -X POST \
--user ${clientId}:${clientSecret}  \
-H 'Content-type: application/x-www-form-urlencoded' \
-d 'auth_req_id=${auth_req_id}' \
-d 'grant_type=urn:openid:params:grant-type:ciba' -s
EOS
`
    token=`eval "${tokenReq}"`
    error=`echo ${token} | jq 'select(.error_description==null)'`
    
    echo ${token} | jq
    if [ -n "$error" ]; then
        break;
    fi
done
$ ./ciba_request.sh
lrHy0QdUalqpL7K2MRMfV_uhCLuvolCk5hS70LEb7R8
{
  "error_description": "[A200308] The end-user has not been authenticated yet.",
  "error": "authorization_pending",
  "error_uri": "https://www.authlete.com/documents/apis/result_codes#A200308"
}
{
  "error_description": "[A200308] The end-user has not been authenticated yet.",
  "error": "authorization_pending",
  "error_uri": "https://www.authlete.com/documents/apis/result_codes#A200308"
}

//If you press Allow in the simulator here. ..
{
  "access_token": "RexqI1mrrCpVI9fiYJWhxuJMSSOfD6j1ijNXs-cXOts",
  "refresh_token": "x0CuGD-CPK1q2b8WzuU5WYBeCoIxdkBg5aaenLCeeWQ",
  "scope": "openid",
  "id_token": "eyJhbGciOiJIUzI1NiJ9.eyJhdF9oYXNoIjoidVBRWVVnT1BmenVaQ1Jab0E1b21XUSIsInN1YiI6IjEwMDEiLCJhdWQiOiIyNDkzOTMxNjU5OTciLCJhdXRoX3RpbWUiOjE1NTQyNTc4NzksImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU0MzQ0MjgxLCJpYXQiOjE1NTQyNTc4ODF9.1mdVZ2hub3GzwGNNxaL1HxlQHdIioSvLyp0UJfdMDog",
  "token_type": "Bearer",
  "expires_in": 86400
}
$

Sounds good. ..

Summary

This time, I tried to build a CIBA compatible authorization server (poll mode) using the Authlete service. Although it is far from understanding the whole feeling just by building the touch, I was able to easily set up the authorization server by using Authlete.

Thank you for your support.

Related Links

Recommended Posts

Procedure for building an authorization server using Authlete (CIBA compatible version)
Procedure for publishing an application using AWS (5) Publish an application
Procedure for publishing an application using AWS (6) Introduce Nginx
Procedure for publishing an application using AWS (4) Creating a database
Procedure for publishing an application using AWS (7) Automatic deployment by Capistrano
How to build an environment for any version of Ruby using rbenv