LambdaでDropboxのアクセストークンを取得する

LambdaでDropboxのアクセストークンを取得したときの個人メモ

[LambdaでS3にある画像をDropboxにアップロードする](https://qiita.com/nkmk1215/items/da11594807552db64e93)では
アクセストークンはコンソール画面から取得していた。
ただ、有効期間が4時間しかなく毎回手動で取得していたため処理に組み込んだ。

---

# 認証コード取得
- App keyをコンソール画面から取得

![000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/62513339-b3fe-eec7-5d19-a4b57ee76f76.jpeg)

- 以下URLにブラウザからアクセス  
  　※`token_access_type=offline`を付与しない場合、  
  　　リフレッシュトークンが取得できない

```
https://www.dropbox.com/oauth2/authorize?client_id=（App key）&response_type=code&token_access_type=offline
```
- 「次へ」を押下

![001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/9a6c87a9-af90-efaa-bfc2-26a8db180c0f.jpeg)

- 「個人」を押下

![002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/f2affeee-3255-591e-2bae-243443140216.jpeg)

- 認証コードを取得

![003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/5b51000f-ae47-f10a-1658-22eed4987631.jpeg)



# リフレッシュトークン取得
- App secretをコンソール画面から取得

![004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/7ab32c12-1952-6bde-0251-3274e23b20f7.jpeg)

- 取得した認証コードを利用してCloudShellにてcurlコマンド実行  
  　※同時にアクセストークンも取得できるが  
  　　期限が切れると使えなくなるのでここでは無視する

```
curl https://api.dropbox.com/oauth2/token \
    -d code=（認証コード） \
    -d grant_type=authorization_code \
    -u （App key）:（App secret）
```

- リフレッシュトークンを取得

![005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/fb03b521-6a10-a10f-bc20-2761cc8bbe67.jpeg)



# アクセストークン取得
- 取得したリフレッシュトークンを利用してCloudShellにてcurlコマンド実行  
  　※Lambdaでリフレッシュトークンを利用する前の確認のため

```
curl https://api.dropbox.com/oauth2/token \
    -d grant_type=refresh_token \
    -d refresh_token=（リフレッシュトークン） \
    -u （App key）:（App secret）
```

- アクセストークンを取得できることを確認

![006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242897/7bfd9d9d-7778-d92b-2987-e4d3cb0bfb72.jpeg)



# 実装
- `get_token()`にてアクセストークンを取得、後続処理に利用

```lambda_function.py
import os
import logging
import boto3
import base64
import requests
import json

# ログ設定
logger = logging.getLogger(__name__)
logger.setLevel(os.environ['LOG_LEVEL'])

# S3設定
S3_BUCKET = '（バケット名）'

# Dropbox設定
APP_KEY = '（App key）'
APP_SECRET = '（App secret）'
REFRESH_TOKEN = '（リフレッシュトークン）'

# 画像ファイル情報
FOLDER_PATH = "image/"
FILE_NAME = 'escle.png'


# リフレッシュトークンからアクセストークンを取得する
# return:アクセストークン
def get_token():
    logger.debug('start get_token()')

    data = {
      'grant_type': 'refresh_token',
      'refresh_token': REFRESH_TOKEN
    }
    
    response = requests.post('https://api.dropbox.com/oauth2/token', data=data, auth=(APP_KEY, APP_SECRET))
    logger.info('response:' + json.dumps(response.json(),default=str))

    access_token = response.json()['access_token']
    logger.debug('access_token:' + access_token)

    logger.debug('end get_token()')

    return access_token


# S3から指定したバケット、フォルダ、ファイル名の画像を取得する
# 取得できる画像サイズはLambdaのメモリーに依存
# return:画像のバイナリデータ
def get_img_from_s3():
    logger.debug('start get_img_from_s3()')

    s3 = boto3.client('s3')
    bucket_name = S3_BUCKET
    file_path = FOLDER_PATH + FILE_NAME
    response = s3.get_object(Bucket=bucket_name, Key=file_path)
    body = response['Body'].read()

    logger.debug('end get_img_from_s3()')

    return body


# 画像をDropboxにアップロードする
# img:画像のバイナリデータ
# return:アップロード結果
def upload_to_dropbox(access_token, img):
    logger.debug('start upload_to_dropbox()')

    arg = '{"path": "' + '/' + FOLDER_PATH + FILE_NAME + '","mode": "add","autorename": true,"mute": false,"strict_conflict": false}'

    headers = {
        'Authorization': 'Bearer ' + access_token,
        'Dropbox-API-Arg': arg.encode("utf_8"),
        'Content-Type': 'application/octet-stream',
    }

    response = requests.post('https://content.dropboxapi.com/2/files/upload', headers=headers, data=img)
    logger.info('response:' + json.dumps(response.json(),default=str))

    logger.debug('end upload_to_dropbox()')

    return json.dumps(response,default=str)


def lambda_handler(event, context):
    logger.debug('start lambda_handler()')

    img = get_img_from_s3()
    access_token = get_token()
    result = upload_to_dropbox(access_token, img)

    logger.debug('end lambda_handler()')

    return result
```

# 感想
- 参考情報が少なかった
    - 2021年9月30日までは長期のアクセストークンが利用できるため  
      ほとんどがコンソールから手動でアクセストークンを取得していた
    - リダイレクトを含むアプリ開発はいくつかあった
- Dropbox Businessでも同様に動作することを確認
    - [LambdaでS3にある画像をDropbox Businessにアップロードする](https://qiita.com/nkmk1215/items/95e2d97e5bb89a8b904a)のように  
    「Dropbox-API-Path-Root」は必要になる