├── .gitignore
├── README.md
├── bird-auth.js
├── index.js
├── lib
├── baidu
│ ├── passport.js
│ └── uuap.js
├── httpClient.js
└── netease
│ ├── crypto.js
│ └── music.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | test/
3 | .idea/
4 | /code.png
5 |
6 | package-lock.json
7 | yarn.lock
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bird-auth
2 |
3 | [![NPM Version][npm-image]][npm-url]
4 | ![NODE Version][node-image]
5 | ![OSX Build][osx-image]
6 | ![LINUX Build][liunx-image]
7 |
8 | 解决 [birdv1](https://github.com/weger/bird) 版本手动取cookie问题, 支持网易云音乐、百度认证。
9 |
10 | ## Install
11 |
12 | `npm install --save-dev bird-auth`
13 |
14 | ## API
15 |
16 | ### birdAuth
17 |
18 | | name | api | instance function |
19 | | :----- | :----- | :----- |
20 | | `client` | `client[fn]` | not support |
21 | | `baidu.uuap` | `birdAuth.baidu.uuap(options[, callback])` | `retry`, `getCookie` |
22 | | `baidu.passport`| `birdAuth.baidu.passport(options[, callback])` | `getCookie` |
23 | | `netease.music`| `birdAuth.netease.music(options[, callback])` | `retry`, `getCookie`, `aesEncrypt` |
24 |
25 |
26 | ### client
27 |
28 | | method | demo | detail |
29 | | :----- | :-- | :----- |
30 | | `url_get` | `client.url_get(url, callback)` | get method(no cookie) |
31 | | `get` | `client.get(url, callback)` | get method(with cookie) |
32 | | `post` | `client.post(options, callback)` | post method(with cookie) |
33 | | `get_cookies_string` | `client.get_cookies_string()` | get all cookies |
34 | | `update_cookies` | `client.update_cookies(cookies, true)` | update new cookies |
35 | | `set_cookies` | `client.set_cookies(cookies, true)` | set new cookies |
36 | | `clear_cookies` | `client.clear_cookies()` | remove all cookies |
37 |
38 |
39 | ## Command Line
40 |
41 | usage: `bird -u xxx -p xxx -t netseae_music`
42 |
43 | ```bash
44 | Options:
45 | -h, --help Show help [boolean]
46 | -t, --type baidu_uuap, baidu_passport, netease_music [default: "baidu_uuap"]
47 | -u, --username username [required]
48 | -p, --password password [required]
49 | -s, --server server(baidu_uuap need it), if you don't know this, you can logout you system and get url.
50 | ```
51 |
52 | ## Examples
53 |
54 | #### baidu uuap auth
55 |
56 | ```javascript
57 | const birdAuth = require('bird-auth')
58 | const uuap = new birdAuth.baidu.uuap({
59 | username: 'xxx',
60 | password: 'xxx',
61 | type: 3, // default 1 is username and password; 3 is username and verification code.
62 | uuapServer: 'http://xxx.baidu.com/login', // CAS auth url
63 | service: 'http://xxx.baidu.com/' // service address, if you don't know this url, you can logout you system, and get `service` parameters
64 | }, function(cookie) {
65 | console.log(cookie)
66 | });
67 |
68 | uuap.retry({
69 | username: 'xxx',
70 | password: 'xxx',
71 | uuapServer: 'http://xxx.baidu.com/login',
72 | service: 'http://xxx.baidu.com/'
73 | });
74 | ```
75 |
76 | #### baidu passport auth
77 |
78 | ```javascript
79 | const birdAuth = require('bird-auth')
80 | const passport = new birdAuth.baidu.passport({
81 | username: 'xxx',
82 | password: 'xxx',
83 | service: 'https://passport.baidu.com/v2/?login' //default passport.baidu.com
84 | }, function(cookie) {
85 | console.log(cookie)
86 | });
87 | ```
88 |
89 | #### netease music auth
90 |
91 | ```javascript
92 | const birdAuth = require('bird-auth')
93 | const music = new birdAuth.netease.music({
94 | username: 'xxx', // phone number or mail
95 | password: 'xxx'
96 | }, function(cookie) {
97 | console.log(cookie)
98 | });
99 | ```
100 |
101 | ## History
102 |
103 | - [2.5.0] remove `querystring` package.
104 | - [2.4.1] fix url check method.
105 | - [2.4.0] switch login to authorize.
106 | - [2.3.1] add type option to auth parameter.
107 | - [2.3.0] fix token verification mechanism.
108 | - [2.2.1] modify parameter naming.
109 | - [2.2.0] change login to authorize.
110 | - [2.1.0] remove service params.
111 | - [2.0.0] refactor & update auth.
112 | - [1.2.8] add auth rsa check.
113 | - [1.2.7] add `client.set_cookies & client.clear_cookies` method.
114 | - [1.2.5] add `client.update_cookies` method.
115 | - [1.2.4] fix passport test(qatest/rdtest) auth bug.
116 | - [1.2.0] group auth and add netease music auth.
117 | - [1.1.10] add httpClient Content-Type adjust.
118 | - [1.1.9] fix passport agent.
119 | - [1.1.6] Fixup 302 response location is not a normal url 😂
120 | - [1.1.3] Custom agent to fix https authorized bug. :(
121 | - [1.1.0] Add `bird-auth` command, you can use `bird-auth -h` to see more :)
122 | - [1.0.6] Fixed get_cookies_string bug
123 | - [1.0.5] Fixed Syntax Error
124 | - [1.0.4] Fixed passport auth bugfix
125 | - [1.0.3] Project init
126 |
127 | [npm-image]: https://img.shields.io/badge/npm-v5.3.6-blue.svg
128 | [npm-url]: https://npmjs.org/package/bird-auth
129 | [node-image]: https://img.shields.io/badge/node-v10.0.0%2B-yellow.svg
130 | [osx-image]: https://img.shields.io/badge/OSX-passing-brightgreen.svg
131 | [liunx-image]: https://img.shields.io/badge/Liunx-passing-brightgreen.svg
132 |
133 | ## Future
134 | - refactor with typescript.
135 | - optimize function, remove useless code.
136 | - Change account and get cookie afresh
137 | - Support Https
138 | - Support online Passport auth
139 | - Set rejectUnauthorized false and fix uuap auth bug. [detail](http://stackoverflow.com/questions/20082893/unable-to-verify-leaf-signature)
140 | - Add bprouting support
141 | - statusCode === 302 judgment
--------------------------------------------------------------------------------
/bird-auth.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const argv = require('yargs')
4 | .help('h')
5 | .alias('h', 'help')
6 | .alias('t', 'type')
7 | .default('t', 'baidu_uuap')
8 | .describe('t', 'baidu_uuap, baidu_passport, netease_music')
9 | .alias('u', 'username')
10 | .demand('u', true)
11 | .describe('u', 'username')
12 | .alias('p', 'password')
13 | .demand('p', true)
14 | .describe('p', 'password')
15 | .alias('s', 'server')
16 | .describe('s', "server(baidu_uuap need it), if you don't know this, you can logout you system and get url.")
17 | .argv;
18 |
19 | const baiduUuap = require('./lib/baidu/uuap.js');
20 | const baiduPassport = require('./lib/baidu/passport.js');
21 | const neteaseMusic = require('./lib/netease/music.js');
22 |
23 | const CALLBACK = function (cookie) {
24 | console.log(cookie);
25 | return cookie;
26 | };
27 |
28 | switch (argv.t) {
29 | case 'baidu_uuap':
30 | baiduUuap({
31 | username: argv.u,
32 | password: argv.p,
33 | server: argv.s
34 | }, CALLBACK);
35 | break;
36 | case 'baidu_passport':
37 | baiduPassport({
38 | username: argv.u,
39 | password: argv.p,
40 | server: argv.s
41 | }, CALLBACK);
42 | break;
43 | case 'netease_music':
44 | neteaseMusic({
45 | username: argv.u,
46 | password: argv.p
47 | }, CALLBACK);
48 | break;
49 | default:
50 | break;
51 | }
52 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file bird-auth > index
3 | */
4 |
5 | const uuap = require('./lib/baidu/uuap.js');
6 | const passport = require('./lib/baidu/passport.js');
7 | const birdNetease = require('./lib/netease/music.js');
8 | const client = require('./lib/httpClient.js');
9 |
10 | module.exports = {
11 | uuap,
12 | passport,
13 | baidu: {
14 | uuap,
15 | passport
16 | },
17 | netease: {
18 | music: birdNetease
19 | },
20 | client
21 | };
22 |
--------------------------------------------------------------------------------
/lib/baidu/passport.js:
--------------------------------------------------------------------------------
1 | /**
2 | * baidu passport auth
3 | * get token
4 | * first post
5 | * if need to verify, to get verify code and repost
6 | */
7 |
8 | 'use strict'
9 |
10 | var fs = require('fs');
11 | var URL = require('url');
12 | var readline = require('readline');
13 |
14 | var client = require('./../httpClient');
15 |
16 | var rl;
17 |
18 | var options = {
19 | // sign_url: options.server + '/v2/api/?login'
20 | };
21 |
22 | var birdPassport = module.exports = function (params, cb) {
23 | if (typeof params !== 'object') {
24 | throw new Error('Where are you params?');
25 | }
26 | options.callback = cb;
27 | options.username = params.username;
28 | options.password = params.password;
29 | options.service = params.service || 'https://passport.baidu.com/v2/?login';
30 |
31 | if (!params.server) {
32 | var tmp = URL.parse(decodeURIComponent(options.service));
33 | options.server = tmp.protocol + '//' + tmp.hostname + (~~tmp.port ? ':' + tmp.port : '');
34 | }
35 | else {
36 | options.server = params.server;
37 | }
38 | options.sign_url = options.server + '/v2/api/?login';
39 |
40 | // GET BAIDUID
41 | client.url_get(options.server + '/v2/?login', function (res, data) {
42 | client.get(options.server + '/v2/api/?getapi&tpl=pp&apiver=v3&tt=' + Date.now() + '&class=login&logintype=dialogLogin&callback=bd__cbs__w5lrwn', function (res, data) {
43 | // console.log(data)
44 | options.token = data.match(/"token" : "(.*?)"/i)[1];
45 | Login();
46 | })
47 | });
48 | };
49 |
50 | function Login(data) {
51 | var form = data
52 | if (!data) {
53 | form = {
54 | 'username': options.username,
55 | 'password': options.password,
56 | 'u': 'https://www.baidu.com/',
57 | 'tpl': 'pp',
58 | 'token': options.token,
59 | 'codestring': '',
60 | 'verifycode': '',
61 | 'staticpage': options.server + '/static/passpc-account/html/v3Jump.html',
62 | 'isPhone': 'false',
63 | 'mem_pass': 'on'
64 | }
65 | }
66 | var tmp = URL.parse(options.sign_url)
67 | client.post({
68 | protocol: tmp.protocol,
69 | host: tmp.hostname,
70 | port: tmp.port || (tmp.protocol == 'https:' ? 443 : 80),
71 | path: tmp.path,
72 | method: 'POST',
73 | headers: {
74 | 'Refer': 'https://www.baidu.com/'
75 | }
76 | }, form, function (res, data) {
77 | // console.log(data)
78 | if (data.match(/error=(\d+)/i)) {
79 | if (+data.match(/error=(\d+)/i)[1] === 257) {
80 | options.codestring = data.match(/codestring=(.+?)&username/i)[1];
81 | getVerifyCode();
82 | }
83 | else if (+data.match(/error=(\d+)/i)[1] === 0) {
84 | options.callback && options.callback(client.get_cookies_string());
85 | }
86 | }
87 | else if (data.match(/err_no=(\d+)&callback/i)) {
88 | if (+data.match(/err_no=(\d+)&callback/i)[1] === 0) {
89 | options.callback && options.callback(client.get_cookies_string());
90 | }
91 | else if (+data.match(/err_no=(\d+)&callback/i)[1] === 6) {
92 | options.codestring = data.match(/codestring=(.+?)&username/i)[1];
93 | getVerifyCode();
94 | }
95 | }
96 | })
97 | }
98 |
99 | function getVerifyCode() {
100 | var url = options.server + "/cgi-bin/genimage?" + options.codestring;
101 | client.url_get(url, function (res, data) {
102 | if (url.match(/rdtest|qatest/)) {
103 | Login({
104 | 'apiver': 'v3',
105 | 'codestring': options.codestring,
106 | 'isPhone': '',
107 | 'logLoginType': ' pc_loginDialog',
108 | 'loginmerge': 'true',
109 | 'logintype': 'dialogLogin',
110 | 'mem_pass': 'on',
111 | 'password': options.password,
112 | 'ppui_logintime': '5452',
113 | 'quick_user': '0',
114 | 'safeflg': '0',
115 | 'splogin': 'newuser',
116 | 'staticpage': 'https://www.baidu.com/cache/user/html/v3Jump.html',
117 | 'token': options.token,
118 | 'tpl': 'mn',
119 | 'tt': Date.now(),
120 | 'u': options.server + '/',
121 | 'username': options.username,
122 | 'verifycode': 'aaaa'
123 | });
124 | return
125 | }
126 | fs.writeFile('./code.png', data, 'binary', function (err) {
127 | if (err) {
128 | console.log(err);
129 | }
130 | else {
131 | rl = readline.createInterface({
132 | input: process.stdin,
133 | output: process.stdout
134 | });
135 | require('child_process').exec('open ./code.png');
136 | rl.question('Please input the verify code : ', function (code) {
137 | options.code = code;
138 | rl.close();
139 | require('child_process').exec('rm -rf ./code.png');
140 | Login({
141 | 'apiver': 'v3',
142 | 'codestring': options.codestring,
143 | 'isPhone': '',
144 | 'logLoginType': ' pc_loginDialog',
145 | 'loginmerge': 'true',
146 | 'logintype': 'dialogLogin',
147 | 'mem_pass': 'on',
148 | 'password': options.password,
149 | 'ppui_logintime': '5452',
150 | 'quick_user': '0',
151 | 'safeflg': '0',
152 | 'splogin': 'newuser',
153 | 'staticpage': 'https://www.baidu.com/cache/user/html/v3Jump.html',
154 | 'token': options.token,
155 | 'tpl': 'mn',
156 | 'tt': Date.now(),
157 | 'u': options.server + '/',
158 | 'username': options.username,
159 | 'verifycode': options.code
160 | });
161 | });
162 | }
163 | });
164 | }, function (res) {
165 | res.setEncoding('binary');
166 | });
167 | }
--------------------------------------------------------------------------------
/lib/baidu/uuap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file baidu > uuap > auth
3 | */
4 |
5 | const JSEncrypt = require('node-jsencrypt');
6 | const {URL} = require('url');
7 | const client = require('./../httpClient');
8 |
9 | const UUAP_SERVER = 'https://uuap.baidu.com/login';
10 | let loginUrl = '';
11 |
12 | const baiduUuap = module.exports = function (params, cb) {
13 | if (typeof params !== 'object') {
14 | throw new Error('Where are you params?');
15 | }
16 |
17 | const self = this;
18 | self._options = params;
19 | self._cb = cb || function () {};
20 |
21 | const authorizeUrl = self._options.server || UUAP_SERVER;
22 |
23 | const METHOD = 'login';
24 | loginUrl = authorizeUrl.replace('authorize', METHOD);
25 |
26 | client.url_get(authorizeUrl, {}, function (response) {
27 | if (response.statusCode === 200) {
28 | client.url_get(loginUrl, {
29 | headers: {
30 | 'originalUrl': authorizeUrl
31 | }
32 | }, function (res, data) {
33 | const {result} = JSON.parse(data);
34 | const {lt, execution, rsaPublicKey, _eventId, loginPath} = result;
35 | const loginOptions = {
36 | lt,
37 | execution,
38 | rsaPublicKey,
39 | _eventId,
40 | loginPath
41 | };
42 |
43 | _login(self._options, loginOptions, function (cookie) {
44 | self._cookie = cookie;
45 | self._cb(cookie);
46 | })
47 | });
48 | } else {
49 | throw new Error('UUAP Server Errors.');
50 | }
51 | });
52 | };
53 |
54 | /**
55 | * 获取cookie
56 | * @type {get_cookies_string}
57 | */
58 | baiduUuap.prototype.getCookie = client.get_cookies_string;
59 |
60 | /**
61 | * 重试
62 | */
63 | baiduUuap.prototype.retry = function (options) {
64 | baiduUuap(options || this._options, this._cb);
65 | };
66 |
67 | /**
68 | * Post登录
69 | * @param options
70 | * @param loginOptions
71 | * @param callback
72 | * @private
73 | */
74 | function _login(options, loginOptions, callback) {
75 | const {username, password, server, type} = options;
76 | const {lt, execution, rsaPublicKey, _eventId, loginPath} = loginOptions;
77 | const form = {
78 | username,
79 | password,
80 | rememberMe: true,
81 | lt,
82 | execution,
83 | _eventId,
84 | type: type || 1
85 | };
86 |
87 | if (rsaPublicKey) {
88 | const timestamp = new Date().getTime();
89 | const arr = [lt, timestamp, password];
90 |
91 | const encrypt = new JSEncrypt();
92 | encrypt.setPublicKey(rsaPublicKey);
93 | const encryptedPassword = encrypt.encrypt(arr.join(','));
94 |
95 | form.password = encryptedPassword;
96 | }
97 |
98 | const tmpUrl = loginUrl.replace('/login', loginPath);
99 | const tmpObj = new URL(tmpUrl);
100 |
101 | const {protocol, hostname, port, pathname, search} = tmpObj;
102 |
103 | client.post({
104 | protocol,
105 | host: hostname,
106 | port: port || (protocol == 'https:' ? 443 : 80),
107 | path: pathname + search,
108 | method: 'POST',
109 | headers: {
110 | 'Referer': server || UUAP_SERVER,
111 | }
112 | }, form, function (res, data) {
113 | const {code, result} = JSON.parse(data);
114 | if (code === 302 && result.redirectUrl) {
115 | client.get(result.redirectUrl, function (res, data) {
116 | const tokenMatch = data.match(/window.location.replace\("(.*)"\)/);
117 | const sTokenUrl = tokenMatch ? tokenMatch[1] : '';
118 | if (sTokenUrl) {
119 | client.get(sTokenUrl, function(res, data) {
120 | callback(client.get_cookies_string());
121 | });
122 | } else {
123 | callback(client.get_cookies_string());
124 | }
125 | });
126 | } else if (server) {
127 | client.get(loginUrl, function (res, data) {
128 | callback(client.get_cookies_string());
129 | });
130 | } else {
131 | callback(client.get_cookies_string());
132 | }
133 | });
134 | };
135 |
--------------------------------------------------------------------------------
/lib/httpClient.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file http client
3 | */
4 |
5 | const http = require('http');
6 | const https = require('https');
7 | const {URL} = require('url');
8 | const querystring = require('querystring');
9 | const union = require('lodash.union');
10 |
11 | const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36';
12 |
13 | let all_cookies = [];
14 |
15 | let agent;
16 |
17 | const _getNet = url => {
18 | let protocol = http;
19 |
20 | if (url.indexOf('https:') === 0) {
21 | protocol = https;
22 | }
23 | return protocol;
24 | }
25 |
26 | const get_cookies_string = function () {
27 | const cookie_map = {};
28 |
29 | all_cookies.forEach(function (ck) {
30 | const v = ck.split(';');
31 | v.forEach(function (item) {
32 | if (!item.match('path')) {
33 | const kv = item.trim().split('=');
34 | if (kv[1]) {
35 | cookie_map[kv[0]] = kv[1];
36 | }
37 | }
38 | })
39 | });
40 |
41 | const cks = [];
42 | for (let k in cookie_map) {
43 | cks.push(k + '=' + cookie_map[k]);
44 | }
45 |
46 | return cks.join(';')
47 | }
48 |
49 | const update_cookies = function (cks) {
50 | if (cks) {
51 | all_cookies = union(all_cookies, cks);
52 | }
53 | }
54 |
55 | const set_cookies = function (cks, single) {
56 | if (cks) {
57 | all_cookies = union(all_cookies, single ? cks : cks.split(' '));
58 | }
59 | }
60 |
61 | const clear_cookies = function() {
62 | all_cookies = [];
63 | }
64 |
65 | // No Cookie Get
66 | const url_get = function (url, options, callback) {
67 | const http_or_https = _getNet(url);
68 |
69 | if (url.match('passport')) {
70 | options.rejectUnauthorized = false;
71 | options.port = options.port || (options.protocol == 'https:' ? 443 : 80);
72 | agent = new https.Agent(options);
73 | options.agent = agent;
74 | }
75 |
76 | http_or_https.get(url, options, function (res) {
77 | all_cookies = [];
78 | set_cookies(res.headers['set-cookie'], true);
79 |
80 | let body = '';
81 | res.on('data', function (chunk) {
82 | return body += chunk;
83 | })
84 | res.on('end', function () {
85 | return callback(res, body);
86 | })
87 | }).on('error', function (e) {
88 | return console.error(e);
89 | });
90 | }
91 |
92 | // Cookie Post
93 | const post = function (options, data, callback) {
94 | const http_or_https = _getNet(options.protocol);
95 |
96 | if (options.protocol === 'https:') {
97 | options.agent = agent;
98 | }
99 |
100 | if (typeof options.headers !== 'object') {
101 | options.headers = {};
102 | }
103 |
104 | const postData = options.headers['Content-Type'] ? JSON.stringify(data) : querystring.stringify(data);
105 |
106 | options.headers['Content-Type'] = options.headers['Content-Type'] || 'application/x-www-form-urlencoded';
107 | options.headers['Content-Length'] = Buffer.byteLength(postData);
108 | options.headers['Cookie'] = get_cookies_string();
109 | options.headers['User-Agent'] = USER_AGENT;
110 |
111 | const req = http_or_https.request(options, function (resp) {
112 | const res = resp;
113 | let body = '';
114 | resp.on('data', function (chunk) {
115 | body += chunk;
116 | })
117 |
118 | set_cookies(resp.headers['set-cookie'], true);
119 |
120 | resp.on('end', function (argument) {
121 | callback(res, body);
122 | })
123 | }).on('error', function (e) {
124 | return console.error(e);
125 | });
126 |
127 | req.write(postData);
128 |
129 | return req.end();
130 | }
131 |
132 | // Cookie Get
133 | const get = function (url, callback) {
134 | const http_or_https = _getNet(url);
135 |
136 | const {protocol, hostname, port, pathname, search} = new URL(url);
137 |
138 | const options = {
139 | protocol,
140 | host: hostname,
141 | port: port || (protocol == 'https:' ? 443 : 80),
142 | path: pathname + search,
143 | method: 'GET',
144 | rejectUnauthorized: false,
145 | headers: {}
146 | }
147 |
148 | options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
149 | options.headers['Cookie'] = get_cookies_string();
150 | options.headers['User-Agent'] = USER_AGENT;
151 |
152 | const req = http_or_https.request(options, function (res, data) {
153 | let body = '';
154 | res.on('data', function (chunk) {
155 | body += chunk;
156 | });
157 |
158 | set_cookies(res.headers['set-cookie'], true);
159 |
160 | return res.on('end', function (argument) {
161 | if (res.statusCode === 302 && res.headers.location.match(/^http/)) {
162 | get(res.headers.location, callback);
163 | } else {
164 | callback(res, body);
165 | }
166 | })
167 | }).on('error', function (e) {
168 | console.error(e, options);
169 | });
170 |
171 | return req.end();
172 | }
173 |
174 | module.exports = {
175 | url_get,
176 | get,
177 | post,
178 | get_cookies_string,
179 | update_cookies,
180 | set_cookies,
181 | clear_cookies
182 | };
183 |
--------------------------------------------------------------------------------
/lib/netease/crypto.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto');
2 | var bigInt = require('big-integer');
3 |
4 | function addPadding(encText, modulus) {
5 | var ml = modulus.length;
6 | for (i = 0; ml > 0 && modulus[i] == '0'; i++)ml--;
7 | var num = ml - encText.length, prefix = '';
8 | for (var i = 0; i < num; i++) {
9 | prefix += '0';
10 | }
11 | return prefix + encText;
12 | }
13 |
14 |
15 | function aesEncrypt(text, secKey) {
16 | var cipher = crypto.createCipheriv('AES-128-CBC', secKey, '0102030405060708');
17 | return cipher.update(text, 'utf-8', 'base64') + cipher.final('base64');
18 | }
19 |
20 | /**
21 | * RSA Encryption algorithm.
22 | * @param text {string} - raw data to encrypt
23 | * @param exponent {string} - public exponent
24 | * @param modulus {string} - modulus
25 | * @returns {string} - encrypted data: reverseText^pubKey%modulus
26 | */
27 | function rsaEncrypt(text, exponent, modulus) {
28 | var rText = '', radix = 16;
29 | for (var i = text.length - 1; i >= 0; i--) rText += text[i];//reverse text
30 | var biText = bigInt(new Buffer(rText).toString('hex'), radix),
31 | biEx = bigInt(exponent, radix),
32 | biMod = bigInt(modulus, radix),
33 | biRet = biText.modPow(biEx, biMod);
34 | return addPadding(biRet.toString(radix), modulus);
35 | }
36 |
37 | function createSecretKey(size) {
38 | var keys = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
39 | var key = "";
40 | for (var i = 0; i < size; i++) {
41 | var pos = Math.random() * keys.length;
42 | pos = Math.floor(pos);
43 | key = key + keys.charAt(pos)
44 | }
45 | return key;
46 | }
47 | var modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';
48 | var nonce = '0CoJUm6Qyw8W8jud';
49 | var pubKey = '010001';
50 | var Crypto = {
51 | MD5: function (text) {
52 | return crypto.createHash('md5').update(text).digest('hex');
53 | },
54 | aesEncrypt: function(text) {
55 | var secKey = createSecretKey(16);
56 | return aesEncrypt(aesEncrypt(text, nonce), secKey);
57 | },
58 | aesRsaEncrypt: function (text) {
59 | var secKey = createSecretKey(16);
60 | return {
61 | params: aesEncrypt(aesEncrypt(text, nonce), secKey),
62 | encSecKey: rsaEncrypt(secKey, pubKey, modulus)
63 | }
64 | }
65 | };
66 | module.exports = Crypto;
--------------------------------------------------------------------------------
/lib/netease/music.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file netease > music > auth
3 | */
4 |
5 | var URL = require('url');
6 |
7 | var client = require('./../httpClient');
8 | var crypto = require('./crypto');
9 |
10 | var neteaseMusic = module.exports = function (params, cb) {
11 | if (typeof params !== 'object') {
12 | throw new Error('Where are you params?');
13 | }
14 |
15 | var self = this;
16 |
17 | self._options = params;
18 | self._cb = cb || function () {
19 | };
20 | // console.log(params);
21 | var url,
22 | pattern = /^0\d{2,3}\d{7,8}$|^1[34578]\d{9}$/,
23 | body = {
24 | password: crypto.MD5(params.password),
25 | rememberLogin: 'false'
26 | };
27 |
28 | if (pattern.test(params.username)) {
29 | body.phone = params.username;
30 | url = 'http://music.163.com/weapi/login/cellphone/';
31 | } else {
32 | body.username = params.username;
33 | url = 'http://music.163.com/weapi/login';
34 | }
35 |
36 | _login(url, crypto.aesRsaEncrypt(JSON.stringify(body)), function (cookie) {
37 | self._cookie = cookie;
38 | self._cb(cookie);
39 | });
40 | };
41 |
42 | /**
43 | * 获取cookie
44 | * @type {get_cookies_string}
45 | */
46 | neteaseMusic.prototype.getCookie = client.get_cookies_string;
47 |
48 | /**
49 | * 加密参数
50 | * @type {Crypto.aesEncrypt}
51 | */
52 | neteaseMusic.prototype.aesEncrypt = crypto.aesEncrypt;
53 |
54 | /**
55 | * 重试
56 | */
57 | neteaseMusic.prototype.retry = function (options) {
58 | neteaseMusic(options || this._options, this._cb);
59 | };
60 |
61 | function _login(url, encBody, callback) {
62 |
63 | var tmp = URL.parse(url);
64 |
65 | client.post({
66 | protocol: tmp.protocol,
67 | host: tmp.hostname,
68 | port: tmp.port || (tmp.protocol == 'https:' ? 443 : 80),
69 | path: tmp.path,
70 | method: 'POST',
71 | headers: {
72 | 'Referer': 'http://music.163.com/'
73 | }
74 | }, encBody, function (res, data) {
75 | callback(client.get_cookies_string());
76 | });
77 | };
78 |
79 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bird-auth",
3 | "version": "2.5.1",
4 | "description": "get cookie and then you can use it with birdv1",
5 | "main": "index.js",
6 | "dependencies": {
7 | "big-integer": "^1.6.22",
8 | "lodash.union": "^4.6.0",
9 | "node-jsencrypt": "^1.0.0",
10 | "yargs": "^4.8.1"
11 | },
12 | "scripts": {
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "bin": {
16 | "bird-auth": "bird-auth.js"
17 | },
18 | "author": "gismanli@163.com",
19 | "license": "MIT",
20 | "engines": {
21 | "node": ">= 10.0.0",
22 | "npm": ">= 5.6.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------