server.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package login
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "time"
  10. "github.com/porter-dev/porter/cli/cmd/utils"
  11. )
  12. func redirect(
  13. codechan chan string,
  14. ) func(http.ResponseWriter, *http.Request) {
  15. return func(w http.ResponseWriter, r *http.Request) {
  16. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  17. fmt.Fprint(w, successScreen)
  18. queryParams, err := url.ParseQuery(r.URL.RawQuery)
  19. if err != nil {
  20. return
  21. }
  22. if codeParam, exists := queryParams["code"]; exists && len(codeParam) > 0 {
  23. codechan <- queryParams["code"][0]
  24. }
  25. }
  26. }
  27. func Login(
  28. host string,
  29. ) (string, error) {
  30. listener, err := net.Listen("tcp", ":0")
  31. if err != nil {
  32. panic(err)
  33. }
  34. port := listener.Addr().(*net.TCPAddr).Port
  35. errorchan := make(chan error)
  36. codechan := make(chan string)
  37. go func() {
  38. http.HandleFunc("/", redirect(
  39. codechan,
  40. ))
  41. err := http.Serve(listener, nil)
  42. errorchan <- err
  43. }()
  44. // open browser for host login
  45. var redirectHost string
  46. if utils.CheckIfWsl() {
  47. fmt.Println("Got till here")
  48. redirectHost = fmt.Sprintf("http://%s:%d", utils.GetWslHostName(), port)
  49. } else {
  50. redirectHost = fmt.Sprintf("http://localhost:%d", port)
  51. }
  52. loginURL := fmt.Sprintf("%s/api/cli/login?redirect=%s", host, url.QueryEscape(redirectHost))
  53. err = utils.OpenBrowser(loginURL)
  54. if err != nil {
  55. return "", fmt.Errorf("Could not open browser: %v", err)
  56. }
  57. for {
  58. select {
  59. case err = <-errorchan:
  60. return "", err
  61. case code := <-codechan:
  62. return ExchangeToken(host, code)
  63. }
  64. }
  65. }
  66. type ExchangeResponse struct {
  67. Token string `json:"token"`
  68. }
  69. func ExchangeToken(host, code string) (string, error) {
  70. req, err := http.NewRequest(
  71. "GET",
  72. fmt.Sprintf("%s/api/cli/login/exchange", host),
  73. strings.NewReader(fmt.Sprintf(`{"authorization_code": "%s"}`, code)),
  74. )
  75. if err != nil {
  76. return "", err
  77. }
  78. // create a request with the authorization code
  79. req.Header.Set("Content-Type", "application/json; charset=utf-8")
  80. req.Header.Set("Accept", "application/json; charset=utf-8")
  81. client := &http.Client{
  82. Timeout: time.Minute,
  83. }
  84. res, err := client.Do(req)
  85. if err != nil {
  86. return "", err
  87. }
  88. defer res.Body.Close()
  89. resp := &ExchangeResponse{}
  90. if err = json.NewDecoder(res.Body).Decode(resp); err != nil {
  91. return "", err
  92. }
  93. return resp.Token, nil
  94. }
  95. const successScreen = `
  96. <!DOCTYPE html>
  97. <html lang="en">
  98. <head>
  99. <meta charset='UTF-8'>
  100. <title>Porter | Login</title>
  101. <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  102. <link href="https://fonts.googleapis.com/css?family=Assistant:400,700|Noto+Sans:400,600,700|Work+Sans:400,500,600|Source+Sans+Pro:400,600,700|Hind+Siliguri:500|Cabin:400,600" rel="stylesheet">
  103. <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  104. <link href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css" rel="stylesheet" />
  105. <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap" rel="stylesheet">
  106. <style>
  107. #logo {
  108. width: 80px;
  109. margin-top: -30px;
  110. margin-bottom: 40px;
  111. }
  112. #success {
  113. font-family: 'Open Sans', sans-serif;
  114. font-size: 18px;
  115. color: #CBCBD8;
  116. margin-bottom: 17px;
  117. }
  118. #subtitle {
  119. font-family: 'Open Sans', sans-serif;
  120. font-size: 14px;
  121. color: #CBCBD8;
  122. }
  123. body{
  124. margin: 0;
  125. padding: 0;
  126. width: 100%;
  127. height: 100vh;
  128. background: #f1f3f5;
  129. display: flex;
  130. flex-direction: column;
  131. align-items: center;
  132. justify-content: center;
  133. }
  134. #text {
  135. display: flex;
  136. height: 100vh;
  137. align-items: center;
  138. justify-content: center;
  139. text-align: center;
  140. }
  141. h2{
  142. color: #fff;
  143. font-size: 47px;
  144. line-height: 40px;
  145. }
  146. #container {
  147. left: 0px;
  148. top: -100px;
  149. height: calc(100vh + 100px);
  150. overflow: hidden;
  151. position: relative;
  152. }
  153. #animate{
  154. margin: 0 auto;
  155. width: 20px;
  156. overflow: visible;
  157. position: relative;
  158. }
  159. #all{
  160. overflow: hidden;
  161. height: 100vh;
  162. width: 100%;
  163. position: fixed;
  164. }
  165. #footer{
  166. color: #808080;
  167. text-decoration: none;
  168. position: fixed;
  169. width: 752px;
  170. bottom: 20px;
  171. align-content: center;
  172. float: none;
  173. margin-left: calc(50% - 376px);
  174. }
  175. a, p{
  176. text-decoration: none;
  177. color: #808080;
  178. letter-spacing: 6px;
  179. transition: all 0.5s ease-in-out;
  180. width: auto;
  181. float: left;
  182. margin: 0;
  183. margin-right: 9px;
  184. }
  185. a:hover{
  186. color: #fff;
  187. letter-spacing: 2px;
  188. transition: all 0.5s ease-in-out;
  189. }
  190. </style>
  191. </head>
  192. <body>
  193. <link href="https://fonts.googleapis.com/css?family=Oswald:600,700" rel="stylesheet">
  194. <div id="all">
  195. <div id="container">
  196. <div id="animate">
  197. </div>
  198. </div>
  199. </div>
  200. <noscript>You need to enable JavaScript to run this app.</noscript>
  201. <img id='logo' src='https://i.ibb.co/y64zfm5/porter.png'>
  202. <div id='success'>Authentication successful!</div>
  203. <div id='subtitle'>You can now close this window.</div>
  204. <script>
  205. /* setTimeout(function () {
  206. window.close();
  207. }, 1000)
  208. */
  209. var container = document.getElementById('animate');
  210. var emoji = ['🎉'];
  211. var circles = [];
  212. for (var i = 0; i < 15; i++) {
  213. addCircle(i * 550, [10 + 0, 300], emoji[Math.floor(Math.random() * emoji.length)]);
  214. addCircle(i * 550, [10 + 0, -300], emoji[Math.floor(Math.random() * emoji.length)]);
  215. addCircle(i * 550, [10 - 200, -300], emoji[Math.floor(Math.random() * emoji.length)]);
  216. addCircle(i * 550, [10 + 200, 300], emoji[Math.floor(Math.random() * emoji.length)]);
  217. addCircle(i * 550, [10 - 400, -300], emoji[Math.floor(Math.random() * emoji.length)]);
  218. addCircle(i * 550, [10 + 400, 300], emoji[Math.floor(Math.random() * emoji.length)]);
  219. addCircle(i * 550, [10 - 600, -300], emoji[Math.floor(Math.random() * emoji.length)]);
  220. addCircle(i * 550, [10 + 600, 300], emoji[Math.floor(Math.random() * emoji.length)]);
  221. }
  222. function addCircle(delay, range, color) {
  223. setTimeout(function() {
  224. var c = new Circle(range[0] + Math.random() * range[1], 80 + Math.random() * 4, color, {
  225. x: -0.15 + Math.random() * 0.3,
  226. y: 1 + Math.random() * 1
  227. }, range);
  228. circles.push(c);
  229. }, delay);
  230. }
  231. function Circle(x, y, c, v, range) {
  232. var _this = this;
  233. this.x = x;
  234. this.y = y;
  235. this.color = c;
  236. this.v = v;
  237. this.range = range;
  238. this.element = document.createElement('span');
  239. /*this.element.style.display = 'block';*/
  240. this.element.style.opacity = 0;
  241. this.element.style.position = 'absolute';
  242. this.element.style.fontSize = '26px';
  243. this.element.style.color = 'hsl('+(Math.random()*360|0)+',80%,50%)';
  244. this.element.innerHTML = c;
  245. container.appendChild(this.element);
  246. this.update = function() {
  247. if (_this.y > 800) {
  248. _this.y = 80 + Math.random() * 4;
  249. _this.x = _this.range[0] + Math.random() * _this.range[1];
  250. }
  251. _this.y += _this.v.y;
  252. _this.x += _this.v.x;
  253. this.element.style.opacity = 1;
  254. this.element.style.transform = 'translate3d(' + _this.x + 'px, ' + _this.y + 'px, 0px)';
  255. this.element.style.webkitTransform = 'translate3d(' + _this.x + 'px, ' + _this.y + 'px, 0px)';
  256. this.element.style.mozTransform = 'translate3d(' + _this.x + 'px, ' + _this.y + 'px, 0px)';
  257. };
  258. }
  259. function animate() {
  260. for (var i in circles) {
  261. circles[i].update();
  262. }
  263. requestAnimationFrame(animate);
  264. }
  265. if (Math.random() < 0.001) {
  266. animate();
  267. }
  268. </script>
  269. </body>
  270. </html>
  271. `