mojang.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. const request = require('request')
  2. const minecraftAgent = {
  3. name: 'Minecraft',
  4. version: 1
  5. }
  6. const authpath = 'https://authserver.mojang.com'
  7. const statuses = [
  8. {
  9. service: 'minecraft.net',
  10. status: 'grey',
  11. name: 'Minecraft.net'
  12. },
  13. {
  14. service: 'api.mojang.com',
  15. status: 'grey',
  16. name: 'Public API'
  17. },
  18. {
  19. service: 'textures.minecraft.net',
  20. status: 'grey',
  21. name: 'Minecraft Skins'
  22. },
  23. {
  24. service: 'authserver.mojang.com',
  25. status: 'grey',
  26. name: 'Authentication Service'
  27. },
  28. {
  29. service: 'sessionserver.mojang.com',
  30. status: 'grey',
  31. name: 'Multiplayer Session Service'
  32. },
  33. {
  34. service: 'account.mojang.com',
  35. status: 'grey',
  36. name: 'Mojang accounts website'
  37. }
  38. ]
  39. /**
  40. * Converts a Mojang status color to a hex value. Valid statuses
  41. * are 'green', 'yellow', 'red', and 'grey'. Grey is a custom status
  42. * to our project which represents an unknown status.
  43. *
  44. * @param {string} status A valid status code.
  45. * @returns {string} The hex color of the status code.
  46. */
  47. exports.statusToHex = function(status){
  48. switch(status.toLowerCase()){
  49. case 'green':
  50. return '#a5c325'
  51. case 'yellow':
  52. return '#eac918'
  53. case 'red':
  54. return '#c32625'
  55. case 'grey':
  56. default:
  57. return '#848484'
  58. }
  59. }
  60. /**
  61. * Retrieves the status of Mojang's services.
  62. * The response is condensed into a single object. Each service is
  63. * a key, where the value is an object containing a status and name
  64. * property.
  65. *
  66. * @see http://wiki.vg/Mojang_API#API_Status
  67. */
  68. exports.status = function(){
  69. return new Promise(function(fulfill, reject) {
  70. request.get('https://status.mojang.com/check',
  71. {
  72. json: true
  73. },
  74. function(error, response, body){
  75. if(error || response.statusCode !== 200){
  76. console.warn('Unable to retrieve Mojang status.')
  77. console.debug('Error while retrieving Mojang statuses:', error)
  78. reject(error || response.statusCode)
  79. } else {
  80. for(let i=0; i<body.length; i++){
  81. const key = Object.keys(body[i])[0]
  82. inner:
  83. for(let j=0; j<statuses.length; j++){
  84. if(statuses[j].service === key) {
  85. statuses[j].status = body[i][key]
  86. break inner
  87. }
  88. }
  89. }
  90. fulfill(statuses)
  91. }
  92. })
  93. })
  94. }
  95. /**
  96. * Authenticate a user with their Mojang credentials.
  97. *
  98. * @param {string} username The user's username, this is often an email.
  99. * @param {string} password The user's password.
  100. * @param {string} clientToken The launcher's Client Token.
  101. * @param {boolean} requestUser Optional. Adds user object to the reponse.
  102. * @param {Object} agent Optional. Provided by default. Adds user info to the response.
  103. *
  104. * @see http://wiki.vg/Authentication#Authenticate
  105. */
  106. exports.authenticate = function(username, password, clientToken, requestUser = true, agent = minecraftAgent){
  107. return new Promise(function(fulfill, reject){
  108. request.post(authpath + '/authenticate',
  109. {
  110. json: true,
  111. body: {
  112. agent,
  113. username,
  114. password,
  115. clientToken,
  116. requestUser
  117. }
  118. },
  119. function(error, response, body){
  120. if(error){
  121. console.error('Error during authentication.', error)
  122. reject(error)
  123. } else {
  124. if(response.statusCode === 200){
  125. fulfill(body)
  126. } else {
  127. reject(body || {code: 'ENOTFOUND'})
  128. }
  129. }
  130. })
  131. })
  132. }
  133. /**
  134. * Validate an access token. This should always be done before launching.
  135. * The client token should match the one used to create the access token.
  136. *
  137. * @param {string} accessToken The access token to validate.
  138. * @param {string} clientToken The launcher's client token.
  139. *
  140. * @see http://wiki.vg/Authentication#Validate
  141. */
  142. exports.validate = function(accessToken, clientToken){
  143. return new Promise(function(fulfill, reject){
  144. request.post(authpath + '/validate',
  145. {
  146. json: true,
  147. body: {
  148. accessToken,
  149. clientToken
  150. }
  151. },
  152. function(error, response, body){
  153. if(error){
  154. console.error('Error during validation.', error)
  155. reject(error)
  156. } else {
  157. if(response.statusCode === 403){
  158. fulfill(false)
  159. } else {
  160. // 204 if valid
  161. fulfill(true)
  162. }
  163. }
  164. })
  165. })
  166. }
  167. /**
  168. * Invalidates an access token. The clientToken must match the
  169. * token used to create the provided accessToken.
  170. *
  171. * @param {string} accessToken The access token to invalidate.
  172. * @param {string} clientToken The launcher's client token.
  173. *
  174. * @see http://wiki.vg/Authentication#Invalidate
  175. */
  176. exports.invalidate = function(accessToken, clientToken){
  177. return new Promise(function(fulfill, reject){
  178. request.post(authpath + '/invalidate',
  179. {
  180. json: true,
  181. body: {
  182. accessToken,
  183. clientToken
  184. }
  185. },
  186. function(error, response, body){
  187. if(error){
  188. console.error('Error during invalidation.', error)
  189. reject(error)
  190. } else {
  191. if(response.statusCode === 204){
  192. fulfill()
  193. } else {
  194. reject(body)
  195. }
  196. }
  197. })
  198. })
  199. }
  200. /**
  201. * Refresh a user's authentication. This should be used to keep a user logged
  202. * in without asking them for their credentials again. A new access token will
  203. * be generated using a recent invalid access token. See Wiki for more info.
  204. *
  205. * @param {string} accessToken The old access token.
  206. * @param {string} clientToken The launcher's client token.
  207. * @param {boolean} requestUser Optional. Adds user object to the reponse.
  208. *
  209. * @see http://wiki.vg/Authentication#Refresh
  210. */
  211. exports.refresh = function(accessToken, clientToken, requestUser = true){
  212. return new Promise(function(fulfill, reject){
  213. request.post(authpath + '/refresh',
  214. {
  215. json: true,
  216. body: {
  217. accessToken,
  218. clientToken,
  219. requestUser
  220. }
  221. },
  222. function(error, response, body){
  223. if(error){
  224. console.error('Error during refresh.', error)
  225. reject(error)
  226. } else {
  227. if(response.statusCode === 200){
  228. fulfill(body)
  229. } else {
  230. reject(body)
  231. }
  232. }
  233. })
  234. })
  235. }