Gitlab 2FA Token Auto Fill

Bros

  1. // ==UserScript==
  2. // @name Gitlab 2FA Token Auto Fill
  3. // @namespace https://github.com/JZ6/20220202
  4. // @version 1.0
  5. // @description Bros
  6. // @author JZ6
  7. // @match *://gitlab.com/*
  8. // @include *://git.*.com/*
  9. // @icon https://www.google.com/s2/favicons?domain=gitlab.com
  10. // @grant none
  11. // @require https://cdnjs.cloudflare.com/ajax/libs/jsSHA/3.2.0/sha1.min.js
  12. // ==/UserScript==
  13.  
  14. const config = {
  15. entry: init,
  16. TOTPKey: '',
  17. autoSubmit: false,
  18. tokenRefreshPeriod: 30,
  19. localStorageKey: 'bros',
  20. defaultInput: 'Get the key from your profile/account page!',
  21. }
  22.  
  23. config.entry()
  24.  
  25. function init() {
  26.  
  27. const TOTPInput = document.getElementById('user_otp_attempt')
  28. if (!TOTPInput) return
  29.  
  30. loadTOTPKey()
  31.  
  32. const TOTPToken = get2FAToken(config.TOTPKey)
  33. console.log(TOTPToken)
  34. TOTPInput.value = TOTPToken
  35.  
  36. if (config.autoSubmit) {
  37. const submitButton = document.querySelector("div.prepend-top-20 input[name='commit']")
  38. submitButton.click()
  39. }
  40.  
  41. // data-qa-selector="otp_secret_content"
  42.  
  43. }
  44.  
  45. function loadTOTPKey() {
  46. config.TOTPKey = localStorage.getItem(config.localStorageKey) || ''
  47. if (!config.TOTPKey || config.TOTPKey == 'null') {
  48. promptKeyInput()
  49. loadTOTPKey()
  50. return
  51. }
  52.  
  53. if (config.TOTPKey == config.defaultInput) {
  54. alert('Please enter your 2FA Key from from your profile/account page!')
  55. promptKeyInput()
  56. loadTOTPKey()
  57. }
  58. }
  59.  
  60. function saveTOTPKey() {
  61. localStorage.setItem(config.localStorageKey, config.TOTPKey)
  62. }
  63.  
  64. function promptKeyInput() {
  65. let key = prompt('Enter your Gitlab Two Factor Key', config.defaultInput)
  66. config.TOTPKey = key
  67. saveTOTPKey()
  68. }
  69.  
  70. function get2FAToken(TOTPKey) {
  71.  
  72. const HEX = 'HEX'
  73. const hexSHA = new jsSHA('SHA-1', HEX)
  74.  
  75. const hexadecimalKey = getHexadecimalKey(TOTPKey)
  76. hexSHA.setHMACKey(hexadecimalKey, HEX)
  77.  
  78. const hexTime = getHexTime()
  79. hexSHA.update(hexTime)
  80.  
  81.  
  82. const HMACKey = hexSHA.getHMAC(HEX)
  83. return getTOTPToken(HMACKey)
  84. }
  85.  
  86. function getHexadecimalKey(TOTPKey) {
  87.  
  88. const base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
  89. cleanKey = TOTPKey.replace(/\s+/g, '').replace(/=+$/, '')
  90.  
  91. let binary = ''
  92. for (const c of cleanKey) {
  93. let val = base32chars.indexOf(c.toUpperCase())
  94. binary += paddingFill(val.toString(2), 5)
  95. }
  96.  
  97. let hexadecimalKey = ''
  98. for (let i = 0; i + 8 <= binary.length; i += 8) {
  99. const byte = binary.slice(i, i + 8)
  100. const hexByte = parseInt(byte, 2).toString(16)
  101. hexadecimalKey += paddingFill(hexByte, 2)
  102. }
  103.  
  104. return hexadecimalKey
  105. }
  106.  
  107. function getHexTime() {
  108. const currentSecond = Math.round(Date.now() / 1000.0)
  109. const tokenRefreshTime = Math.floor(currentSecond / config.tokenRefreshPeriod)
  110.  
  111.  
  112. let hexTime = Math.round(tokenRefreshTime).toString(16)
  113.  
  114. if (tokenRefreshTime < 15.5) {
  115. hexTime = `0${hexTime}`
  116. }
  117.  
  118. return paddingFill(hexTime, 16)
  119. }
  120.  
  121. function getTOTPToken(HMACKey) {
  122. const offset = parseInt(HMACKey.slice(-1), 16)
  123. const hexOffset = parseInt(HMACKey.substr(offset * 2, 8), 16)
  124.  
  125. const mask = parseInt('7fffffff', 16)
  126.  
  127. const TOTPToken = String(hexOffset & mask)
  128.  
  129. return TOTPToken.slice(TOTPToken.length - 6)
  130. }
  131.  
  132. function paddingFill(str, len) {
  133. if (len < str.length - 1) {
  134. return str
  135. }
  136. return Array(len - str.length + 1).join('0') + str
  137. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址