--Based on the device token that can be obtained from the terminal, two bit values can be recorded and referenced in Apple's API for each terminal. --The saved bit value is not reset even if the terminal is initialized and the application is uninstalled and installed.
-(At the time of initial installation, you can determine that it has been reinstalled by recording it in the device check API.)
--Record that the terminal is account banned by the API of device check. --Even after reinstalling the app and initializing the device, make it unavailable if the device was previously banned.
--Even if the terminal is passed to another person in the second-hand market, etc., it is a value that is retained, so careful operation is required. --It seems that the device token obtained from the terminal has a time expiration date. (I'm not sure about the memory of this part, so please test it.) If you use it with account ban, send a device token to the server side when the next application starts after ban, and immediately there It is necessary to devise such as updating with API.
--Obtain the p8 file of the authentication key. --Get KEY_ID, TEAM_ID --Obtain DeviceToken on the terminal and pass it to the server side.
--About Device Check added in iOS 11 - https://qiita.com/owen/items/85dff1e45083d2805140
--Apple Official: - https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data
https://api.development.devicecheck.apple.com/v1/
https://api.devicecheck.apple.com/v1/
https://api.devicecheck.apple.com/v1/query_two_bits
https://api.devicecheck.apple.com/v1/update_two_bits
https://api.devicecheck.apple.com/v1/validate_device_token
import DeviceCheck
DCDevice.current.generateToken {
(data, error) in
guard let data = data else {
return
}
let token = data.base64EncodedString()
print(token)
}
//Get
private void queryIOSDeviceCheckSample() {
//With this value you can get two bool values and the last modified date.
Response response = postRequest(DEVELOPMENT_BASE_API_URL + "query_two_bits", "Device token obtained from the terminal", null, null);
}
//update
private void updateIOSDeviceCheckSample(Boolean bit0, Boolean bit1) {
Response response = postRequest(DEVELOPMENT_BASE_API_URL + "update_two_bits", "Device token obtained from the terminal", null, null);
}
--Set the JWT token in the "Authorization" header. --Set each parameter in json format in request body. - device_token, transaction_id, timestamp, bit0, bit1
private static Response postRequest(String url, String deviceToken, Boolean bit0, Boolean bit1) throws IOException {
MediaType JSON = MediaType.get("application/json; charset=utf-8");
JSONObject jsonObject = new JSONObject();
jsonObject.put("device_token", deviceToken);
jsonObject.put("transaction_id", UUID.randomUUID().toString());
jsonObject.put("timestamp", new Date().getTime());
if (bit0 != null) {
jsonObject.put("bit0", bit0);
}
if (bit1 != null) {
jsonObject.put("bit1", bit1);
}
String json = jsonObject.toJSONString();
RequestBody body = RequestBody.create(JSON, json);
String jwt = getJWTStr();
if (jwt == null) {
return null;
}
Request request = new Request.Builder()
.url(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Content-Length", String.valueOf(json.length()))
.header("Authorization", "Bearer " + jwt)
.post(body)
.build();
OkHttpClient client = new OkHttpClient();
return client.newCall(request).execute();
}
--There is a library for iOS push notifications called pushy, There is a process to get a JWT token from a p8 file. --Implemented with reference to that part. https://github.com/jchambers/pushy/blob/master/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java#L124-L170
JTW(=JSON Web Token) https://ja.wikipedia.org/wiki/JSON_Web_Token
private static String getJWTStr() {
try {
ECPrivateKey privateKey = getECPrivateKey(P8_SECRET_KEY_PATH);
return Jwts.builder()
.setHeaderParam("kid", KEY_ID)
.setIssuer(TEAM_ID)
.setIssuedAt(new Date())
.signWith(privateKey, SignatureAlgorithm.ES256)
.compact();
} catch (Exception e) {
return null;
}
}
private static ECPrivateKey getECPrivateKey(String p8FilePath) throws Exception {
final FileInputStream fileInputStream = new FileInputStream(new File(p8FilePath));
final ECPrivateKey signingKey;
{
final String base64EncodedPrivateKey;
{
final StringBuilder privateKeyBuilder = new StringBuilder();
final BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));
boolean haveReadHeader = false;
boolean haveReadFooter = false;
for (String line; (line = reader.readLine()) != null; ) {
if (!haveReadHeader) {
if (line.contains("BEGIN PRIVATE KEY")) {
haveReadHeader = true;
}
} else {
if (line.contains("END PRIVATE KEY")) {
haveReadFooter = true;
break;
} else {
privateKeyBuilder.append(line);
}
}
}
if (!(haveReadHeader && haveReadFooter)) {
throw new IOException("Could not find private key header/footer");
}
base64EncodedPrivateKey = privateKeyBuilder.toString();
}
final byte[] keyBytes = Base64.getDecoder().decode(base64EncodedPrivateKey.getBytes(StandardCharsets.US_ASCII));
final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("EC");
try {
signingKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException(e);
}
}
return signingKey;
}