目次
0. はじめに
ナレッジ執筆をしばらくさぼっており、2023年のAdeventCalenderに参加し損ねましたOsyouです。さて、snowflakeでは2023年もたくさんの新機能がリリースされましたが、その中でも、External Network Accessは個人的に好きな機能でした。
既にたくさんの検証記事がでておりますが、多くの記事はAPI認証方法がAPIキー認証であり、SECRETオブジェクトのタイプにGENERIC_STRINGを指定したものだと認識しております。
一方で、snowflakeでは外部APIを利用する際の認証方法として、OAuthをサポートしています。
そこで、この記事ではOAuthを用いた外部APIの利用を検証してみます。
具体的には、「Google Sheets API」を用いて、snowflakeで構築したUDF から、Google Spread Sheetへの書き込みを行ってみます。
公式ドキュメントにGoogle翻訳APIを用いた例がありますので、そちもご参照ください。
1.OAuthとは
概要
OAuthの解説記事は多数ありますが、本記事でも簡単な説明をいたします。
OAuthなんて知ってるよ!という方は本節は飛ばして下さい。
OAuthはWebサービスなどを連携して“権限や許可を与える”ためのプロトコルです。
皆さんも、Googleと何かしらのサービスを連携するみたいなことを実施したことがあると思いますが、そこで利用されています。
OAuthのメリット
OAuthを利用するメリットとしては、以下が挙げられると思います。
- 他サービスとの連携が容易になる
- アクセストークンが悪用される危険が少ない
- ユーザー毎に権限を制限できる
GoogleのAPIもいくつかの認証方法を用意していますが、Google翻訳等の一般データにアクセスする機能は、APIキーを利用しても問題ありません。一方で、GoogleドライブやSpreadSheetなどのユーザーデータにアクセスする場合は、OAuthの利用が推奨されています。
snowflakeで対応しているフロー
OAuthは、RFC 6749で定義されてます。RFC 6749では以下の4つの認可フローを定義しています。
- 認可コードフロー
- インプリシットフロー
- リソースオーナー・パスワード・クレデンシャルズフロー
- クライアントクレデンシャルズフロー
snowflakeでは、上記4つの認可フローのうち、2つをサポートしています。(※公式ドキュメント参照)
- 認可コードフローを使用したOAuth
- クライアントクレデンシャルズフローを使用したOAuth
実は、上記以外の2つは脆弱性が見つかっており、非推奨となっているため、snowflakeでは上記2つのみに対応していると思われます。
GoogleのOAuthでは、「認可コードフロー」に対応しているため、今回は「認可コードフロー」を用いて実装します。
※厳密には、アクセストークン取得の際に同時に発行されるリフレッシュトークンを用いた「リフレッシュトークンフロー」と思います。
OAuthの詳細な説明は、こちらのYoutubeが感動的にわかりやすいです。外部サイトで恐縮ですが、詳細に知りたい方はご覧ください。
2.事前準備(Google Sheet API側の準備)
APIを利用するための事前準備を実施します。事前準備の方法は色々ありますが、私は、こちらのブログを参考にしました。
詳細手順
-  APIの有効化
- GCPでプロジェクトを作成する
 - プロジェクトでAPIを有効化する
 
- OAuthシークレットを作成する
- OAuth同意画面を登録する
- 今回は書き込みを検証するので、スコープには「https://www.googleapis.com/auth/spreadsheets」を指定する
 
 - OAuth2.0クライアントを作成する
 
- OAuth同意画面を登録する
- Refresh TokenからAccess Tokenを獲得できるようにする
- Authorizationコードを取得する
 - Authorizationコードから、「Access Token」と「Refresh Token」を取得する
 - 「Refresh Token」から、「Access Token」を得る
 
2の手順が完了した時点で、以下のようなJSONが得られるはずです。このうち、「client_id」、「client_secret」、「token_uri」はsnowflakeで利用します。
{
	"web": {
		"client_id": "<xxxxxxxx>",
		"project_id": "<xxxxxxxx>",
		"auth_uri": "https://accounts.google.com/o/oauth2/auth",
		"token_uri": "https://oauth2.googleapis.com/token",
		"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
		"client_secret": "<xxxxxxxx>",
		"redirect_uris": [
			"http://localhost:8080"
		]
	}
}また、3.bの手順を実行した時点で、以下のJSONが得られるはずです。このうち、「refresh_token」をsnowflakeで利用します。
{
	"access_token": "xxxxxx",
	"expires_in": 3599,
	"refresh_token": "xxxxxx",
	"scope": "https://www.googleapis.com/auth/spreadsheets",
	"token_type": "Bearer"
}3.snowflake実装
やっと本題です。OAuth認可なAPIを利用するUDFを作成していきます。
ネットワークルールの作成
OAuth以外の方法と同様に、ネットワークルールを作成します。
CREATE OR REPLACE NETWORK RULE google_sheets_apis_network_rule
MODE = EGRESS
TYPE = HOST_PORT
VALUE_LIST = ('sheets.googleapis.com')
;SECURITY統合の作成
ここが少しOAuth利用で特徴的な部分になります。OAuth利用に必要な認証情報をセキュリティ統合として作成します。
CREATE OR REPLACE SECURITY INTEGRATION google_sheets_oauth
TYPE = API_AUTHENTICATION
AUTH_TYPE = OAUTH2
OAUTH_GRANT = 'AUTHORIZATION_CODE'
OAUTH_CLIENT_AUTH_METHOD = CLIENT_SECRET_POST
OAUTH_CLIENT_ID = 'xxxxxx'
OAUTH_CLIENT_SECRET = 'xxxxxx'
OAUTH_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token'
ENABLED = TRUE
COMMENT = ''
;少しだけオプションの補足解説です。詳細は公式ドキュメントを参照ください
- AUTH_TYPE
- OAuth 2.0を使用して外部サービスを認証 します
 
- OAUTH_GRANT
- AUTHORIZATION_CODE- 認可フローに、「認可コードフロー」を利用することを明示します。今回はこちらを利用します
 
 - CLIENT_CREDENTIALS- 認可フローに「クライアント・クレデンシャルズ」を利用することを明示します。
 
 
- OAUTH_CLIENT_ID
- 事前準備で取得した「client_id」を指定します
 
- 事前準備で取得した「
- OAUTH_CLIENT_SECRET
- 事前準備で取得した「client_secret」を指定します
 
- 事前準備で取得した「
シークレットの作成
外部アクセス統合の作成
外部アクセス統合を作成して、ネットワークルールとシークレットを紐づけします。
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION google_sheets_api_integration
ALLOWED_NETWORK_RULES = (google_sheets_apis_network_rule)
ALLOWED_AUTHENTICATION_SECRETS = (oauth_token)
ENABLED = TRUE
;UDFの作成
- 利用するAPIの仕様・サンプル
今回利用するAPIの仕様や、利用例はGoogleの公式ドキュメントを参考にさせてもらいました。処理の内容がわかりにくい場合は、ご参照ください。 - APIの一覧・仕様
- このページの「v4.spreadsheets.values」リソースのupdateメソッドを利用しました。
 
- このページの「
 - APIのサンプル
- このページの「基本的な書式設定」を参考にしました
 
 
- APIの一覧・仕様
実際に、Googleのスプレッドシートに書き込みを実施するUDFを作成します。
(実際Pythonから書き込みする場合は、高度なパッケージを利用することが多いですが、ここではシンプルな動作確認のため、素のAPIをコールしています)
CREATE OR REPLACE FUNCTION write_google_sheets(sheetid STRING)
RETURNS STRING
LANGUAGE PYTHON
RUNTIME_VERSION = 3.11
HANDLER = 'write_google_sheets'
EXTERNAL_ACCESS_INTEGRATIONS = (google_sheets_api_integration)
PACKAGES = ('snowflake-snowpark-python', 'requests')
SECRETS = ('cred' = oauth_token)
AS
$$
import _snowflake
import requests
import json
def write_google_sheets(sheetid):
    # snowflake 関数によるOauthトークン取得
    access_token = _snowflake.get_oauth_access_token('cred')
    headers = {
        'Authorization':f'Bearer {access_token}'
    }
    url = f'https://sheets.googleapis.com/v4/spreadsheets/{sheetid}/values/シート1!A1:D5?valueInputOption=USER_ENTERED'
    sample_data = {
    "range": "シート1!A1:D5",
    "majorDimension": "ROWS",
    "values": [
            ["Item", "Cost", "Stocked", "Ship Date"],
            ["Wheel", "20.50", "4", "3/1/2016"],
            ["Door", "15", "2", "3/15/2016"],
            ["Engine", "100", "1", "3/20/2016"],
            ["Totals", "=SUM(B2:B4)", "=SUM(C2:C4)", "=MAX(D2:D4)"]
        ]
    }
    response = requests.put(url, headers=headers, data=json.dumps(sample_data))
    return response.json()
$$
;以下、補足です
- snowflakeの関数であるget_oauth_access_token()関数で、リフレッシュトークンを利用して、アクセストークンを新規発行してくれています。
- 引数にスプレッドシートのシートIDを指定します。
4.動作確認
動作確認をしてみます。事前に、スプレッドシートは空の状態にしておきます。

UDFにスプレッドシートのIDを指定して実行します
SELECT write_google_sheets('xxxxxxxxxxxxxxxx');スプレッドシートに値が書き込みされました!これで、snowflakeのUDFから、スプレッドシートへとデータが連携できたことになります!


5.おわりに
今回は認可コード付与フローに準じたOAuthを実装してみました。対応した認可サーバーが見つかれば、クライアントクレデンシャルズフローの方も試してみたいと思います。
 
             
              
 
            
          
 
                
               
                
              