newCallbackServer starts a callback server on listener that validates state and reports the result on a buffered channel. The redirect URI always uses localhost so it matches the value registered on the OAuth/GitHub App.
(listener net.Listener, expectedState string)
| 59 | // and reports the result on a buffered channel. The redirect URI always uses |
| 60 | // localhost so it matches the value registered on the OAuth/GitHub App. |
| 61 | func newCallbackServer(listener net.Listener, expectedState string) *callbackServer { |
| 62 | cs := &callbackServer{ |
| 63 | server: &http.Server{ReadHeaderTimeout: 10 * time.Second}, // ReadHeaderTimeout guards against Slowloris. |
| 64 | listener: listener, |
| 65 | redirect: fmt.Sprintf("http://localhost:%d/callback", listener.Addr().(*net.TCPAddr).Port), |
| 66 | results: make(chan callbackResult, 1), |
| 67 | } |
| 68 | cs.server.Handler = cs.handler(expectedState) |
| 69 | |
| 70 | go func() { |
| 71 | if err := cs.server.Serve(listener); err != nil && err != http.ErrServerClosed { |
| 72 | cs.report(callbackResult{err: fmt.Errorf("callback server: %w", err)}) |
| 73 | } |
| 74 | }() |
| 75 | |
| 76 | return cs |
| 77 | } |
| 78 | |
| 79 | // handler renders the callback endpoint. It reports the outcome exactly once and |
| 80 | // always shows the user a friendly page. |