client.go 2.0 KB

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