client.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package remoteauth
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/sha256"
  7. "encoding/base64"
  8. "encoding/json"
  9. "net/http"
  10. "sync"
  11. "github.com/gorilla/websocket"
  12. "github.com/bwmarrin/discordgo"
  13. )
  14. type Client struct {
  15. sync.Mutex
  16. URL string
  17. conn *websocket.Conn
  18. qrChan chan string
  19. doneChan chan struct{}
  20. user User
  21. err error
  22. heartbeats int
  23. closed bool
  24. privateKey *rsa.PrivateKey
  25. }
  26. // New creates a new Discord remote auth client. qrChan is a channel that will
  27. // receive the qrcode once it is available.
  28. func New() (*Client, error) {
  29. privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
  30. if err != nil {
  31. return nil, err
  32. }
  33. return &Client{
  34. URL: "wss://remote-auth-gateway.discord.gg/?v=2",
  35. privateKey: privateKey,
  36. }, nil
  37. }
  38. // Dial will start the QRCode login process. ctx may be used to abandon the
  39. // process.
  40. func (c *Client) Dial(ctx context.Context, qrChan chan string, doneChan chan struct{}) error {
  41. c.Lock()
  42. defer c.Unlock()
  43. header := http.Header{}
  44. for key, value := range discordgo.DroidWSHeaders {
  45. header.Set(key, value)
  46. }
  47. c.qrChan = qrChan
  48. c.doneChan = doneChan
  49. conn, _, err := websocket.DefaultDialer.DialContext(ctx, c.URL, header)
  50. if err != nil {
  51. return err
  52. }
  53. c.conn = conn
  54. go c.processMessages()
  55. return nil
  56. }
  57. func (c *Client) Result() (User, error) {
  58. c.Lock()
  59. defer c.Unlock()
  60. return c.user, c.err
  61. }
  62. func (c *Client) close() error {
  63. c.Lock()
  64. defer c.Unlock()
  65. if c.closed {
  66. return nil
  67. }
  68. c.conn.WriteMessage(
  69. websocket.CloseMessage,
  70. websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""),
  71. )
  72. c.closed = true
  73. defer close(c.doneChan)
  74. return c.conn.Close()
  75. }
  76. func (c *Client) write(p clientPacket) error {
  77. c.Lock()
  78. defer c.Unlock()
  79. payload, err := json.Marshal(p)
  80. if err != nil {
  81. return err
  82. }
  83. return c.conn.WriteMessage(websocket.TextMessage, payload)
  84. }
  85. func (c *Client) decrypt(payload string) ([]byte, error) {
  86. // Decode the base64 string.
  87. raw, err := base64.StdEncoding.DecodeString(payload)
  88. if err != nil {
  89. return []byte{}, err
  90. }
  91. // Decrypt the data.
  92. return rsa.DecryptOAEP(sha256.New(), nil, c.privateKey, raw, nil)
  93. }