/
index.js
128 lines (117 loc) · 3.46 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const path = require('path');
const request = require('../utils/request');
const protobuf = require('protobufjs');
const Long = require('long');
const { waitFor } = require('../utils/timeout');
const fcmKey = require('../fcm/server-key');
const { toBase64 } = require('../utils/base64');
// Hack to fix PHONE_REGISTRATION_ERROR #17 when bundled with webpack
// https://github.com/dcodeIO/protobuf.js#browserify-integration
protobuf.util.Long = Long
protobuf.configure()
const serverKey = toBase64(Buffer.from(fcmKey));
const REGISTER_URL = 'https://android.clients.google.com/c2dm/register3';
const CHECKIN_URL = 'https://android.clients.google.com/checkin';
let root;
let AndroidCheckinResponse;
module.exports = {
register,
checkIn,
};
async function register(appId) {
const options = await checkIn();
const credentials = await doRegister(options, appId);
return credentials;
}
async function checkIn(androidId, securityToken) {
await loadProtoFile();
const buffer = getCheckinRequest(androidId, securityToken);
const body = await request({
url : CHECKIN_URL,
method : 'POST',
headers : {
'Content-Type' : 'application/x-protobuf',
},
body : buffer,
encoding : null,
});
const message = AndroidCheckinResponse.decode(body);
const object = AndroidCheckinResponse.toObject(message, {
longs : String,
enums : String,
bytes : String,
});
return object;
}
async function doRegister({ androidId, securityToken }, appId) {
const body = {
app : 'org.chromium.linux',
'X-subtype' : appId,
device : androidId,
sender : serverKey,
};
const response = await postRegister({ androidId, securityToken, body });
const token = response.split('=')[1];
return {
token,
androidId,
securityToken,
appId,
};
}
async function postRegister({ androidId, securityToken, body, retry = 0 }) {
const response = await request({
url : REGISTER_URL,
method : 'POST',
headers : {
Authorization : `AidLogin ${androidId}:${securityToken}`,
'Content-Type' : 'application/x-www-form-urlencoded',
},
form : body,
});
if (response.includes('Error')) {
console.warn(`Register request has failed with ${response}`);
if (retry >= 5) {
throw new Error('GCM register has failed');
}
console.warn(`Retry... ${retry + 1}`);
await waitFor(1000);
return postRegister({ androidId, securityToken, body, retry : retry + 1 });
}
return response;
}
async function loadProtoFile() {
if (root) {
return;
}
root = await protobuf.load(path.join(__dirname, 'checkin.proto'));
return root;
}
function getCheckinRequest(androidId, securityToken) {
const AndroidCheckinRequest = root.lookupType(
'checkin_proto.AndroidCheckinRequest'
);
AndroidCheckinResponse = root.lookupType(
'checkin_proto.AndroidCheckinResponse'
);
const payload = {
userSerialNumber : 0,
checkin : {
type : 3,
chromeBuild : {
platform : 2,
chromeVersion : '63.0.3234.0',
channel : 1,
},
},
version : 3,
id : androidId ? Long.fromString(androidId) : undefined,
securityToken : securityToken
? Long.fromString(securityToken, true)
: undefined,
};
const errMsg = AndroidCheckinRequest.verify(payload);
if (errMsg) throw Error(errMsg);
const message = AndroidCheckinRequest.create(payload);
return AndroidCheckinRequest.encode(message).finish();
}