LibertyとGoogleを使ったOpenID Connectの動作検証

#はじめに
OpenID Connect関連のテストをするためにLibertyからGoogleのOpenID Connect Providerを利用する構成を行った時のメモです。
GoogleのOpenID Provider(OP)のサービスを利用し、LibertyをRelying Party(RP)として構成し、簡易的なサンプルアプリでOpenID Connectのフローを流してみます。
基本は以下の記事をなぞって動かしています。
参考: [Single sign-on with Google on Liberty](https://developer.ibm.com/wasdev/docs/single-sign-google-liberty/)

OpenID Connectについては以下の辺りを参考に...
[一番分かりやすい OpenID Connect の説明](https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe)

##環境
Windows7
Eclipse 4.7 (Oxygen)
WebSphere Liberty 17.0.0.x

#構成
##Libertyの準備
今後テストすることを考えて、EclipseからLiberty作って管理します。
こちらの手順を参考に...
[LibertyによるWebサービスアプリ開発メモ: (1)環境構築](https://qiita.com/tomotagwork/items/3a63ac7955d053a4feb4)

記事の記述に従って、server.xmlに以下のフィーチャーを追加します。

```server.xml
<featureManager>       
    <feature>ssl-1.0</feature>
    <feature>servlet-3.0</feature>
    <feature>appSecurity-2.0</feature>
    <feature>openidConnectClient-1.0</feature>
    <feature>localConnector-1.0</feature>
    <feature>jsp-2.3</feature>
</featureManager>
```

openidConnectClientが入ってなかったので認識されませんでした。
server.xmlのDesignのビューからフィーチャーのAddを押す。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/553c0af1-d88f-6231-a063-4f2b5bfa6531.png)

ここからOpenID Connect Clientをインストールする。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/7f81833d-5508-9a73-a242-99c8afef387b.png)

あとjsp, servletフィーチャーも警告が出てたのでそれぞれ2.3, 3.1に変えておく。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/22ec1cb1-3177-b64c-97a2-f64e6ac2079b.png)

さらにキーストアの定義も追加。

最終的に変更部分はこんな感じ。

```server.xml
    <!-- Enable features -->
<featureManager>       
    <feature>ssl-1.0</feature>
    <feature>servlet-3.1</feature>
    <feature>appSecurity-2.0</feature>
    <feature>openidConnectClient-1.0</feature>
    <feature>localConnector-1.0</feature>
    <feature>jsp-2.3</feature>
</featureManager>

    <keyStore id="defaultKeyStore" password="keyspass" />

```
一旦Libertyを起動/停止させる。
ここで、keysotreが作成されます。(...<LibertyサーバーDir>\resources\security\key.jks)

ブラウザから以下にアクセスして、Googleの証明書(GTSGIAG3.crt)をダウンロードします。
https://pki.goog/
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/f0faabfd-92eb-cf73-1065-d40d1f755040.png)
(GTS GIAG3のDERファイルをダウンロード)

入手した証明書をキーストア(key.jks)に追加します。
キーストア(key.jks)のあるディレクトリに移って、Java提供のkeytoolコマンドで、入手した証明書をキーストアにインポートします。

```
C:\Users\IBM_ADMIN>cd C:\x\IBM\WebSphere\Liberty_Test\usr\servers\server2\resources\security

C:\x\IBM\WebSphere\Liberty_Test\usr\servers\server2\resources\security>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は A8F7-9750 です

 C:\x\IBM\WebSphere\Liberty_Test\usr\servers\server2\resources\security のディレクトリ

2018/06/07  13:45    <DIR>          .
2018/06/07  13:45    <DIR>          ..
2018/06/07  13:45             2,158 key.jks
2018/06/07  13:45               905 ltpa.keys
               2 個のファイル               3,063 バイト
               2 個のディレクトリ  30,946,590,720 バイトの空き領域

C:\x\IBM\WebSphere\Liberty_Test\usr\servers\server2\resources\security>keytool -importcert -keystore key.jks -storepass keyspass -alias googleG3 -file C:\temp\GTSGIAG3.crt -noprompt
証明書は鍵ストアに追加されました
```

一旦Libertyは置いておく...

##GoogleサイトでのOAuthクライアント登録

以下にアクセスしてプロジェクトを作成
https://console.developers.google.com/apis/dashboard

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/45d524b0-4d38-1511-669c-51cb6a09ee06.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/b2830dc0-2f6e-19dd-073f-ee4ff67365ff.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/2cc526a6-ad9a-e556-ee1a-26963ed00339.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/a632a5de-48db-0660-28ec-af85297f72a4.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/0aba45ce-4c52-2711-5be8-2e0170d3c5c7.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/e7b288db-9a59-fe64-d20c-e6f1308713a8.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/a2d67796-78c1-297e-6b6a-aa05b90d627f.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/5741f8d9-3562-d12a-e492-58e2730b5886.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/0038ccb0-17fd-63b4-7f67-eedddb79ab1d.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/7f1e80ac-f3a6-e799-cd0a-59750dfbccd8.png)

※リダイレクトURIは、とりあえずローカルPCのLibertyサーバーを想定しているので、localhost:xxx というようにローカルのPCのLibertyのhttps構成に基づきURIを指定しています。

こんな感じでクライアントIDとクライアントシークレットが入手できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/4092fdfb-0a07-3cbf-1585-8afdb835c750.png)

この値をコピーしておきます。


##Liberty設定追加

Libertyの設定に戻って...

```server.xml
<openidConnectClient 
    authorizationEndpointUrl="https://accounts.google.com/o/oauth2/auth" 
    clientId="xxxxx" 
    clientSecret="xxxxx" 
    id="googleRP" 
    issuerIdentifier="accounts.google.com" 
    grantType="implicit"
    jwkEndpointUrl="https://www.googleapis.com/oauth2/v2/certs" 
    scope="openid profile email" 
    signatureAlgorithm="RS256" 
    tokenEndpointUrl="https://www.googleapis.com/oauth2/v3/token" 
    userIdentityToCreateSubject="email">
</openidConnectClient>


<application type="war" id="testpage" name="testpage" location="${server.config.dir}/apps/testpage.war">
  <application-bnd>
    <security-role name="All Role">
       <special-subject type="ALL_AUTHENTICATED_USERS" />
    </security-role>
  </application-bnd>
</application>
```

※`clientId`, `clientSecret`には、Googleのサイトで入手したクライアントID、クライアントシークレットの値を指定します。
※ここでは、参照記事の指定に加えて、grantType="implicit"を追加しています。


##サンプルアプリケーションの準備
以下のサイトから、サンプルアプリケーション(testpage.zip)をダウンロード
https://www.ibm.com/developerworks/websphere/library/techarticles/1502_odonnell/1502_odonnell.html#artdownload
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/418899f1-707c-d822-121a-65440cdb0c3b.png)

zipを解凍してwarファイルをLibertyのパス(server.xmlに指定したパス)に配置します。(例 C:\x\IBM\WebSphere\Liberty_Test\usr\servers\server2\apps\testpage.war)

あとはLibertyを再起動すればOK

#動作確認

ブラウザから以下のURLでテストアプリにアクセスしてみます。
https://localhost:9443/testpage/

すると...Googleの認証画面に飛ばされました！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/ad2beb3c-b1ba-c3e3-4f13-ab32b2f9203f.png)

そこでGoogleのIDで認証を行うと...
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/8aec0e5c-d961-4266-187b-2c4a6226d5e7.png)

目的のアプリケーションにアクセスでき、結果が表示されました！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/fda426c1-bab1-721d-074a-d8708ec43dc2.png)

期待した通りに動いたようです！

ちなみに、このページが表示される直前のフローをブラウザの開発ツールで見てみると...
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132331/a8ffca30-7ead-46b1-fbc9-fa0ba76632b7.png)
`POST https://localhost:9443/oidcclient/redirect/googleRP`というようなリクエストが出ていて、リクエストのqueryパラメーター"id_token"でJWT形式のトークンが渡されているのが確認できました。

#補足: サンプルWAR

サンプルのWARをちょっと覗いてみると...
コードの実体としてはこれだけ。JSPの中でuser principalの情報を取得して表示しているだけ。

```index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
import="java.util.List"
import="java.util.Map"
import="java.util.ArrayList"
import="java.util.Collection"
%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>User Login Test Page</title>
</head>
<body style="margin:10px; padding:10px">
<h2>User Login Test Servlet</h2>
<IMG src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgIAyADIAAD//gEC0B8yA7 ...
<IMG src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD//gECAMKdCN...
<br>
<br>
Description: this is a tiny sample servlet that prints the authenticed user principal.<br>
<br>
<%
Object o = request.getUserPrincipal();
if (o == null) {
%>user principal is null<%
} else {
%>And the current user is:<h2><pre>   <%=o.toString()%></pre></h2><%
}
%>
</body>
</html>
```

requestオブジェクトのgetUserPrincipal()でユーザー情報を取得してそれを画面に表示させているだけですね。

web.xmlの設定は以下の通り。

```web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	id="WebApp_ID" version="2.4">
	<display-name>Demo Servlet</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
	<security-role>
		<role-name>All Role</role-name>
	</security-role>

	<security-constraint>
		<web-resource-collection>
			<web-resource-name>Demo Servlet</web-resource-name>
			<url-pattern>/*</url-pattern>
			<http-method>GET</http-method>
			<http-method>PUT</http-method>
			<http-method>POST</http-method>
			<http-method>DELETE</http-method>
		</web-resource-collection>
		<auth-constraint>
			<role-name>All Role</role-name>
		</auth-constraint>
		<user-data-constraint>
			<transport-guarantee>NONE</transport-guarantee>
		</user-data-constraint>
	</security-constraint>

</web-app>
```