DatabricksのOAuth user-to-machine (U2M)認証

気づいたら実装されてました。マニュアルはこちら。あとで翻訳見直しておきます。

https://docs.databricks.com/ja/dev-tools/auth/oauth-u2m.html

OAuthに関してはこちらが非常にわかりやすいです。

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

:::note info
**注意**
2024/9/19 タイトルと用語を修正しました。
:::


アカウントレベルの認証とワークスペースレベルの認証がありますが、ここではワークスペースレベルの認証を行なっていきます。

# ステップ 1: OAuthコード検証子(verifier)とコードチャレンジのペアを生成する

Databricksノートブックなどで以下を実行します。

```py
import uuid, hashlib, base64

# UUIDを生成
uuid1 = uuid.uuid4()

# UUIDを文字列に変換
uuid_str1 = str(uuid1).upper()

# コード検証子を作成
code_verifier = uuid_str1 + "-" + uuid_str1

# コード検証子に基づいてコードチャレンジを作成
code_challenge = base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest()).decode('utf-8')

# コードチャレンジからすべてのパディングを削除
code_challenge = code_challenge.replace('=', '')

# コード検証子とコードチャレンジを表示
# OAuth U2M認証のためにアクセストークンを手動で生成する際にこれらを使用します。
print(f"code_verifier:  {code_verifier}")
print(f"code_challenge: {code_challenge}")
```
```
code_verifier:  30EFEF16-E24B-4CBC-9B69-140B6CEA3F1C-30EFEF16-E24B-4CBC-9B69-140B6CEA3F1C
code_challenge: 2pdbCyvBZQ9QbaNkYnuL3G5kgXuZRwlByfevYZYvJ1I
```

# ステップ 2: 認可コードを生成する

ステップ1で作成したコードチャレンジを用いて、認可コードを生成します。以下では改行を挿入していますが、実際には改行は含みません。

- `<redirect-url>`をローカル コンピューターへのリダイレクト URL に置き換えます (例: http://localhost:8020 )。
- `<state>`を認可コードの整合性を検証するために使用できるプレーンテキスト文字列に置き換えます。以下では`takaakiyayoi_oauth_test`としていますが、これはローカルマシンにリダイレクトされた際に、自分が入力したものであることを目検するために使用します。

```
https://<Databricksワークスペースのホスト名>/oidc/v1/authorize?
client_id=databricks-cli&
redirect_uri=http://localhost:8020&
response_type=code&state=takaakiyayoi_oauth_test&
code_challenge=2pdbCyvBZQ9QbaNkYnuL3G5kgXuZRwlByfevYZYvJ1I&
code_challenge_method=S256&
scope=all-apis+offline_access
```

ワークスペースにログインしていない際には、ログイン画面が表示されるのでログインします。すると、上で指定した`<redirect-url>`にリダイレクトされます。ローカルで8020ポートでアプリケーションが動作していなければ、画面には何も表示されませんが、URLに認証コードが含まれています。以下では、灰色に塗りつぶしている箇所となります。これをコピーしておきます。また、`state`の値が上で指定したものと同一であることを確認してください。
![Screenshot 2024-09-17 at 17.27.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/56ee2371-5216-3056-2bf0-85173d3954a4.png)

# ステップ 3: 認可コードを使用して OAuth アクセス権を生成する

`code_verifier`には、ステップ1で準備したコード検証子(verifier)を指定します。

```sh
curl --request POST \
https://<ワークスペースのホスト名>/oidc/v1/token \
--data "client_id=databricks-cli" \
--data "grant_type=authorization_code" \
--data "scope=all-apis offline_access" \
--data "redirect_uri=http://localhost:8020" \
--data "code_verifier=30EFEF16-E24B-4CBC-9B69-140B6CEA3F1C-30EFEF16-E24B-4CBC-9B69-140B6CEA3F1C" \
--data "code=<ステップ2で取得した認可コード>"
```
```json
{"access_token":"<アクセストークン>","refresh_token":"doau....a4","scope":"all-apis offline_access","token_type":"Bearer","expires_in":3600}
```

# ステップ 4: Databricks REST API を呼び出す

```sh
export OAUTH_TOKEN=<アクセストークン>

curl --request GET --header "Authorization: Bearer $OAUTH_TOKEN" \
"https://<Databricksワークスペースのホスト名>/api/2.0/clusters/list" | jq
```

```json
{
  "clusters": [
    {
      "cluster_id": "0828-090225-7qa5vjeb",
      "creator_user_name": "takaaki.yayoi@databricks.com",
      "spark_context_id": 4666640280775891000,
      "driver_healthy": true,
      "cluster_name": "Taka Yayoi's Personal Compute Cluster",
      "spark_version": "15.4.x-cpu-ml-scala2.12",
      "spark_conf": {
        "spark.databricks.cluster.profile": "singleNode",
        "spark.master": "local[*, 4]"
      },
      "aws_attributes": {
        "first_on_demand": 0,
        "availability": "ON_DEMAND",
        "zone_id": "auto",
        "spot_bid_price_percent": 100
      },
      "node_type_id": "i3.xlarge",
      "driver_node_type_id": "i3.xlarge",
      "custom_tags": {
        "ResourceClass": "SingleNode"
      },
      "autotermination_minutes": 120,
      "enable_elastic_disk": true,
      "disk_spec": {},
      "cluster_source": "UI",
      "single_user_name": "takaaki.yayoi@databricks.com",
      "policy_id": "DB62AD9785000228",
      "enable_local_disk_encryption": false,
      "instance_source": {
        "node_type_id": "i3.xlarge"
      },
      "driver_instance_source": {
        "node_type_id": "i3.xlarge"
      },
      "data_security_mode": "SINGLE_USER",
      "runtime_engine": "STANDARD",
      "effective_spark_version": "15.4.x-cpu-ml-scala2.12",
      "state": "TERMINATED",
      "state_message": "Termination requested by takaaki.yayoi+db@databricks.com",
      "start_time": 1724835745533,
      "terminated_time": 1725519639958,
      "last_state_loss_time": 1725519034415,
      "last_activity_time": 1725519080453,
      "last_restarted_time": 1725519034460,
      "num_workers": 0,
      "default_tags": {
        "Vendor": "Databricks",
        "Creator": "takaaki.yayoi@databricks.com",
        "ClusterName": "Taka Yayoi's Personal Compute Cluster",
        "ClusterId": "0828-090225-7qa5vjeb"
      },
      "termination_reason": {
        "code": "USER_REQUEST",
        "type": "SUCCESS",
        "parameters": {
          "username": "takaaki.yayoi@databricks.com"
        }
      },
      "init_scripts_safe_mode": false
    }
  ]
}
```

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)