要約
DeviceCheckを使ってリセマラを防ぎます
概要
リセマラ(リセットマラソン)を防ぐために、DeviceCheckを使って端末チェックを行います。
ここでいうリセマラとは、アプリをアンインストール/インストールを繰り返して、初回限定の利益を何度も受ける行為を指します。
端末チェックの結果、「この端末ではまだ初回限定の利益を受けていない」と判断できた場合、初回限定の特典を与えます。
DeviceCheckを使った端末チェックはiOSアプリ単体で完結するわけではなく、さまざまな手順が必要になります。
また、仕組み上、中古端末や譲渡を受けた端末では初回限定の判断はできないかと思われます。
アプリの流れ
- iOSアプリがDeviceCheckを利用して、トークンを発行する
- iOSアプリは、サーバーに対してトークンを送信する
- サーバーは、JWTを生成して、トークンとともにApple APIへリクエストを送る
- サーバーは、Apple APIの結果を受け取り、iOSアプリへ返す
- サーバーのレスポンスを元に、初回限定の利益を与える/与えない
端末チェックの手順
p8編
iOS Developer centerで、Keysを作成することができる権限のアカウントが必要です。
iOS Developer centerでDeviceCheck用のP8ファイルを作成する必要があります。
下記は手順の一例です。iOS Developer centerに変更が行われている場合は適宜読み替えてください。
- Certificates, Identifiers & Profiles の Keysを選択
- Create Keyを押下
- Register a New Keyで、Key Nameを任意、DeviceCheckをチェック
- Downloadで、p8ファイルをダウンロードする。これはサーバーサイドで利用します。
サーバ編
先ほどダウンロードしたp8ファイルが必要です。
サーバーサイドでは、p8ファイル
、iOS Developer centerで生成したKeyのKey ID
、iOS Developer centerのチームID
が必要になり、組み合わせてJWTを生成し、iOSアプリからデバイストークン受信時、Authorizationヘッダーに Bearer <GeneratedJWT>
を付与して、bodyにPayloadを付与してAppleのAPIへリクエストを行います。
APIには query_two_bits
と update_two_bits
があり、まず query_two_bits
を確認して、200 Bit State Not Found
であれば初回限定とみなせます。
必要であればこの時点でupdate_two_bits
で初回特典を付与済みとすればよいでしょう。
AppleのDevice check用APIについての公式はこちらです。
JWTを生成するライブラリは各種環境で選定してください。
以下はJWT生成時のHEADERとPAYLOADの例です。
HEADER例
{
"alg": "ES256",
"kid": "(iOS Developer centerで生成したKeyのKey ID)"
}
PAYLOAD例
{
"iss": "(iOS Developer centerのチームID)",
"iat": 1573712797,
"exp": 1573716397
}
bodyの例
{
"device_token": "Base 64エンコードされた、iOSから送られてきたデバイストークン",
"transaction_id": "C94E26BD-714B-4D3F-8733-E86A790C0F8D",
"timestamp": 1573707063000
}
iOSアプリ編
DeviceCheckは実機じゃないと動作しません。プロビジョニングもDeviceCheckのp8ファイルを作ったチームIDと等しい必要があります。
iOSアプリでは DCDevice.current.generateToken
を実行し、生成したトークンをサーバーへ送信し、その結果を受け取って初回限定の特典を与えることになります。
import DeviceCheck
DCDevice.current.generateToken { (deviceToken, error) in
// TODO: サーバーにdeviceTokenを送信して、レスポンスを受け取って処理をする
}
iOS単体での実機デバッグ
DeviceCheckは、サーバーと連携して使うことが前提のものですが、iOSアプリ単体でテストとして使いたい場合、CupertinoJWT を使ってApple APIとの通信が可能です。
下記はquery_two_bits
の例です。
あくまでテストとしてお使いください。
import DeviceCheck
import CupertinoJWT
DCDevice.current.generateToken { (deviceToken, error) in
//p8ファイルの中身
let p8 = """
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
"""
let keyID = "iOS Developer centerで生成したKeyのKey ID"
let teamID = "iOS Developer centerのチームID"
let jwt = JWT(keyID: keyID, teamID: teamID, issueDate: Date(), expireDuration: 60 * 60)
do {
let generatedJWT = try jwt.sign(with: p8)
let url = URL(string: "https://api.development.devicecheck.apple.com/v1/query_two_bits")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let deviceTokenString = deviceToken!.base64EncodedString(options: Data.Base64EncodingOptions.init())
let timestamp:Int = Int(Date().timeIntervalSince1970)
let transaction = UUID().uuidString
let requestBody = """
{
"device_token" : "\(deviceTokenString)",
"transaction_id" : "\(transaction)",
"timestamp" : \(timestamp)000
}
"""
let data = requestBody.data(using: .utf8)
request.addValue("Bearer \(generatedJWT)", forHTTPHeaderField: "Authorization")
request.httpBody = data
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// NOTE:Apple Device Check APIの結果を判断して初回限定の付与などを判定
}
task.resume()
} catch {
// Handle error
}
}