LINE WORKS を ID プロバイダーとして利用した OAuth 2.0 SSO

## 1. はじめに

### 背景

SSO（シングル サインオン）は、複数のアプリケーション間でユーザー認証を統合するための重要な技術です。特に企業内で利用されるツールやシステム間での一貫したユーザー認証は、運用効率を向上させ、セキュリティを強化します。

LINE WORKS は OpenID Connect (OIDC) に対応し、OAuth 2.0 を拡張して ID トークンを提供する仕組みを提供しました。これにより、LINE WORKS を ID プロバイダーとして利用した SSO アプリケーションの構築が可能になりました。

### この記事の目的

本記事では、LINE WORKS を ID プロバイダーとして利用し、OIDC に基づく SSO アプリケーションを構築する方法を解説します。サンプルコードを用いて、実装手順を分かりやすく紹介します。

## 2. 必要な知識と準備

### 必要な知識
- **OAuth 2.0** と **OpenID Connect** の基本概念
- **JWT（JSON Web Token）** の構造と署名検証
- **Flask** を用いた Python アプリケーションの構築

### 準備するもの
1. **LINE WORKS Developer Console でのアプリ登録**
   - Client ID と Client Secret の取得方法
   - リダイレクト URI の設定
2. **Python 環境の構築**
   - 必要なライブラリ（`Flask`, `requests`, `PyJWT`）のインストール
     ```bash
     pip install flask requests pyjwt
     ```

## 3. 実装手順

### ステップ1: Flask アプリケーションの構築
まず、Flask を用いて基本的なアプリケーション構成を作成します。  
以下はログイン画面のデザインと基本構成です。

```python
@app.route("/")
def index():
    return render_template_string('''
    <html>
    <head><title>Login</title></head>
    <body>
        <h1>Sign in with LINE WORKS</h1>
        <a href="/login">Login with LINE WORKS</a>
    </body>
    </html>
    ''')
```

### ステップ2: OIDC 認証フローの実装

#### 1. 認証リクエストの送信
認可コード フローを用いて認証リクエストを作成します。

```python
@app.route("/login")
def login():
    state = os.urandom(16).hex()
    session["state"] = state
    auth_url = (
        f"{AUTH_URL}?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}"
        f"&response_type=code&scope=openid%20email%20profile&state={state}"
    )
    return redirect(auth_url)
```

#### 2. トークンの取得と ID トークンの検証
リダイレクト後に、認可コードを使用してトークンを取得します。その後、ID トークンの署名を公開鍵で検証します。

```python
@app.route("/callback")
def callback():
    # 認可コードを取得
    code = request.args.get("code")
    state = request.args.get("state")
    # トークン取得
    token_data = {
        "code": code,
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "redirect_uri": REDIRECT_URI,
        "grant_type": "authorization_code",
    }
    token_response = requests.post(TOKEN_URL, data=token_data).json()
    id_token = token_response.get("id_token")

    # ID トークン検証
    jwks_client = PyJWKClient(JWK_URL)
    signing_key = jwks_client.get_signing_key_from_jwt(id_token)
    decoded_token = jwt.decode(
        id_token, signing_key.key, algorithms=["RS256"], audience=CLIENT_ID
    )
    return f"Welcome, {decoded_token['name']}!"
```

### ステップ3: ダッシュボードの表示
ID トークンから取得した情報を表示するダッシュボードを作成します。

```python
@app.route("/dashboard")
def dashboard():
    user_info = session.get("user_info")
    return render_template_string('''
    <html>
    <head><title>Dashboard</title></head>
    <body>
        <h1>Welcome {{ user_info["name"] }}</h1>
        <p>Email: {{ user_info["email"] }}</p>
        <p>Locale: {{ user_info["locale"] }}</p>
    </body>
    </html>
    ''', user_info=user_info)
```

## 4. 完成したコード
以下は、完成版のコードです。すべての機能が統合されています。
ブラウザ上から試すことも可能です。

[Google Colab ノートブックはこちら](https://colab.research.google.com/drive/1flHuDqUDT3VOdpmaUtRHkCMavRn7J5Tz?usp=sharing)

### SSO アプリの SSO による画面遷移

![temp_1735087936238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/ea967777-a5ca-7706-1c2b-cbd1829cd85a.png)

![temp_1735087947495.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/a456f78d-f46e-b6c4-a9af-f26f95338a03.png)

![temp_1735087995092.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/0e530812-d689-4513-e52d-9937b5fedcd9.png)

## 5. 解説: OIDC と LINE WORKS の活用方法

- **OIDC の基本**
  - OAuth 2.0 の上に拡張された認証プロトコル。
  - ID トークンを使用してユーザー情報を取得。
- **LINE WORKS の特徴**
  - ID トークンに署名を付与し、安全性を確保。
  - ユーザー情報の取得が簡易化。

## 6. 今後の拡張ポイント

- **アクセストークンの利用**
  - LINE WORKS API を利用して追加のユーザー情報やグループ情報を取得。
- **リフレッシュトークン**
  - 認証セッションを維持する実装。
- **セキュリティ強化**
  - トークンの暗号化や有効期限管理。

## 7. まとめ

OIDC 対応により、LINE WORKS を ID プロバイダーとした SSO アプリケーションが簡単に構築できるようになりました。本記事を参考に、OAuth 2.0 による SSO を利用したシステムの統合を進めてみてください。