@@ -17,21 +17,27 @@ import (
1717)
1818
1919const (
20- // JIRA OAuth2 endpoints
20+ // JIRA OAuth2 endpoints.
2121 jiraAuthURL = "https://auth.atlassian.com/authorize"
2222 jiraTokenURL = "https://auth.atlassian.com/oauth/token"
2323 accessibleResourcesURL = "https://api.atlassian.com/oauth/token/accessible-resources"
2424
25- // Default OAuth settings
25+ // Default OAuth settings.
2626 defaultRedirectURI = "http://localhost:9876/callback"
2727 defaultPort = ":9876"
2828 callbackPath = "/callback"
2929
30- // OAuth timeout
30+ // OAuth timeout.
3131 oauthTimeout = 5 * time .Minute
3232
33- // OAuth storage file name
33+ // OAuth storage file name.
3434 oauthSecretsFile = "oauth_secrets.json"
35+
36+ // Server shutdown timeout.
37+ serverShutdownTimeout = 5 * time .Second
38+
39+ // HTTP client timeout for API calls.
40+ httpClientTimeout = 30 * time .Second
3541)
3642
3743var defaultScopes = []string {
@@ -43,22 +49,22 @@ var defaultScopes = []string{
4349 "offline_access" , // This is required to get the refresh token from JIRA
4450}
4551
46- // OAuthConfig holds OAuth configuration
52+ // OAuthConfig holds OAuth configuration.
4753type OAuthConfig struct {
4854 ClientID string
4955 ClientSecret string
5056 RedirectURI string
5157 Scopes []string
5258}
5359
54- // ConfigureTokenResponse holds the OAuth token response
60+ // ConfigureTokenResponse holds the OAuth token response.
5561type ConfigureTokenResponse struct {
5662 AccessToken string
5763 RefreshToken string
5864 CloudID string
5965}
6066
61- // GetOAuth2Config creates an OAuth2 config for the given client credentials
67+ // GetOAuth2Config creates an OAuth2 config for the given client credentials.
6268func GetOAuth2Config (clientID , clientSecret , redirectURI string , scopes []string ) * oauth2.Config {
6369 if scopes == nil {
6470 scopes = defaultScopes
@@ -79,7 +85,7 @@ func GetOAuth2Config(clientID, clientSecret, redirectURI string, scopes []string
7985 }
8086}
8187
82- // Configure performs the complete OAuth flow and returns tokens
88+ // Configure performs the complete OAuth flow and returns tokens.
8389func Configure () (* ConfigureTokenResponse , error ) {
8490 // Collect OAuth credentials from user
8591 jiraDir , err := getJiraConfigDir ()
@@ -127,7 +133,7 @@ func Configure() (*ConfigureTokenResponse, error) {
127133 }, nil
128134}
129135
130- // LoadOAuthSecrets loads OAuth secrets from storage
136+ // LoadOAuthSecrets loads OAuth secrets from storage.
131137func LoadOAuthSecrets () (* OAuthSecrets , error ) {
132138 jiraDir , err := getJiraConfigDir ()
133139 if err != nil {
@@ -143,13 +149,13 @@ func LoadOAuthSecrets() (*OAuthSecrets, error) {
143149 return & secrets , nil
144150}
145151
146- // HasOAuthCredentials checks if OAuth credentials are present
152+ // HasOAuthCredentials checks if OAuth credentials are present.
147153func HasOAuthCredentials () bool {
148154 _ , err := LoadOAuthSecrets ()
149155 return err == nil
150156}
151157
152- // collectOAuthCredentials collects OAuth credentials from the user
158+ // collectOAuthCredentials collects OAuth credentials from the user.
153159func collectOAuthCredentials () (* OAuthConfig , error ) {
154160 var questions []* survey.Question
155161 answers := struct {
@@ -195,7 +201,7 @@ func collectOAuthCredentials() (*OAuthConfig, error) {
195201 }, nil
196202}
197203
198- // performOAuthFlow executes the OAuth authorization flow
204+ // performOAuthFlow executes the OAuth authorization flow.
199205func performOAuthFlow (config * OAuthConfig ) (* oauth2.Token , error ) {
200206 s := cmdutil .Info ("Starting OAuth flow..." )
201207 defer s .Stop ()
@@ -223,15 +229,18 @@ func performOAuthFlow(config *OAuthConfig) (*oauth2.Token, error) {
223229
224230 // Send success response to browser
225231 w .Header ().Set ("Content-Type" , "text/html" )
226- w .Write ([]byte (`
232+ if _ , err := w .Write ([]byte (`
227233 <html>
228234 <body>
229235 <h2>Authorization successful!</h2>
230236 <p>You can close this window and return to the terminal.</p>
231237 <script>window.close();</script>
232238 </body>
233239 </html>
234- ` ))
240+ ` )); err != nil {
241+ errChan <- fmt .Errorf ("failed to write response: %w" , err )
242+ return
243+ }
235244
236245 codeChan <- code
237246 } else {
@@ -261,9 +270,11 @@ func performOAuthFlow(config *OAuthConfig) (*oauth2.Token, error) {
261270 select {
262271 case code := <- codeChan :
263272 // Shutdown server
264- ctx , cancel := context .WithTimeout (context .Background (), 5 * time . Second )
273+ ctx , cancel := context .WithTimeout (context .Background (), serverShutdownTimeout )
265274 defer cancel ()
266- server .Shutdown (ctx )
275+ if err := server .Shutdown (ctx ); err != nil {
276+ fmt .Printf ("Warning: failed to shutdown server: %v\n " , err )
277+ }
267278
268279 // Exchange code for token
269280 s .Stop ()
@@ -279,27 +290,31 @@ func performOAuthFlow(config *OAuthConfig) (*oauth2.Token, error) {
279290
280291 case err := <- errChan :
281292 // Shutdown server
282- ctx , cancel := context .WithTimeout (context .Background (), 5 * time . Second )
293+ ctx , cancel := context .WithTimeout (context .Background (), serverShutdownTimeout )
283294 defer cancel ()
284- server .Shutdown (ctx )
295+ if shutdownErr := server .Shutdown (ctx ); shutdownErr != nil {
296+ fmt .Printf ("Warning: failed to shutdown server: %v\n " , shutdownErr )
297+ }
285298 return nil , fmt .Errorf ("OAuth flow failed: %w" , err )
286299
287300 case <- time .After (oauthTimeout ):
288301 // Shutdown server
289- ctx , cancel := context .WithTimeout (context .Background (), 5 * time . Second )
302+ ctx , cancel := context .WithTimeout (context .Background (), serverShutdownTimeout )
290303 defer cancel ()
291- server .Shutdown (ctx )
304+ if err := server .Shutdown (ctx ); err != nil {
305+ fmt .Printf ("Warning: failed to shutdown server: %v\n " , err )
306+ }
292307 return nil , fmt .Errorf ("OAuth flow timed out after %v" , oauthTimeout )
293308 }
294309}
295310
296- // getCloudID retrieves the Cloud ID for the authenticated user
311+ // getCloudID retrieves the Cloud ID for the authenticated user.
297312func getCloudID (url string , accessToken string ) (string , error ) {
298313 s := cmdutil .Info ("Fetching cloud ID..." )
299314 defer s .Stop ()
300315
301316 // Create HTTP client with bearer token
302- client := & http.Client {Timeout : 30 * time . Second }
317+ client := & http.Client {Timeout : httpClientTimeout }
303318
304319 req , err := http .NewRequest ("GET" , url , nil )
305320 if err != nil {
@@ -313,7 +328,11 @@ func getCloudID(url string, accessToken string) (string, error) {
313328 if err != nil {
314329 return "" , err
315330 }
316- defer resp .Body .Close ()
331+ defer func () {
332+ if closeErr := resp .Body .Close (); closeErr != nil {
333+ fmt .Printf ("Warning: failed to close response body: %v\n " , closeErr )
334+ }
335+ }()
317336
318337 if resp .StatusCode != http .StatusOK {
319338 return "" , fmt .Errorf ("failed to get accessible resources: status %d" , resp .StatusCode )
0 commit comments