Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: refactor OAuth providers and add Microsoft support#69

Merged
appleboy merged 2 commits intomainfrom
auth
Mar 3, 2026
Merged

feat: refactor OAuth providers and add Microsoft support#69
appleboy merged 2 commits intomainfrom
auth

Conversation

@appleboy
Copy link
Member

@appleboy appleboy commented Mar 3, 2026

  • Refactor HTTP API auth tests to use time.Second and testify require and assert helpers for clearer assertions.
  • Add OAuth user info implementations for GitHub, Gitea, and Microsoft as separate provider-specific files.
  • Introduce a shared fetchJSON helper to centralize HTTP GET and JSON decoding logic.
  • Move provider-specific OAuth logic out of the main provider file and remove duplicated request handling code.
  • Extend OAuth provider support to include Microsoft accounts.
  • Simplify OAuth provider display name capitalization logic.

- Refactor HTTP API auth tests to use time.Second and testify require and assert helpers for clearer assertions.
- Add OAuth user info implementations for GitHub, Gitea, and Microsoft as separate provider-specific files.
- Introduce a shared fetchJSON helper to centralize HTTP GET and JSON decoding logic.
- Move provider-specific OAuth logic out of the main provider file and remove duplicated request handling code.
- Extend OAuth provider support to include Microsoft accounts.
- Simplify OAuth provider display name capitalization logic.

Signed-off-by: Bo-Yi Wu <[email protected]>
Copilot AI review requested due to automatic review settings March 3, 2026 04:12
- Reformat the construction of the Gitea user API URL to improve readability without changing behavior

Signed-off-by: Bo-Yi Wu <[email protected]>
@codecov
Copy link

codecov bot commented Mar 3, 2026

Codecov Report

❌ Patch coverage is 0% with 74 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/auth/oauth_github.go 0.00% 30 Missing ⚠️
internal/auth/oauth_microsoft.go 0.00% 20 Missing ⚠️
internal/auth/oauth_gitea.go 0.00% 15 Missing ⚠️
internal/auth/oauth_provider.go 0.00% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

@appleboy appleboy merged commit 59f597c into main Mar 3, 2026
11 of 15 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors OAuth provider user-info fetching into provider-specific implementations, adds Microsoft Entra ID (Graph) user info support, and cleans up HTTP API auth tests for readability and correctness.

Changes:

  • Introduces a shared fetchJSON helper to centralize GET + JSON decoding for provider user-info calls.
  • Moves GitHub, Gitea, and Microsoft user-info logic into separate provider files and adds Microsoft support.
  • Updates internal/auth/http_api_test.go to use time.Second and testify’s require/assert helpers.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/auth/oauth_provider.go Removes inlined provider user-info logic, simplifies display-name formatting, and adds shared fetchJSON helper.
internal/auth/oauth_microsoft.go Adds Microsoft Graph /me user-info implementation using shared fetchJSON.
internal/auth/oauth_github.go Adds GitHub user + primary-email fetching using shared fetchJSON.
internal/auth/oauth_gitea.go Adds Gitea user-info fetching using shared fetchJSON.
internal/auth/http_api_test.go Refactors tests to use time.Second and testify assertions for clearer test failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 146 to +148
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("GitHub API error: %s - %s", resp.Status, string(body))
}

var user githubUser
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
return nil, fmt.Errorf("failed to decode user info: %w", err)
}

// If email is not public, fetch from emails endpoint
if user.Email == "" {
email, err := p.getGitHubPrimaryEmail(ctx, client)
if err != nil {
return nil, fmt.Errorf("failed to get user email: %w", err)
}
user.Email = email
}

// GitHub requires email for our integration
if user.Email == "" {
return nil, errors.New("GitHub account has no email address")
}

return &OAuthUserInfo{
ProviderUserID: strconv.FormatInt(user.ID, 10),
Username: user.Login,
Email: user.Email,
FullName: user.Name,
AvatarURL: user.AvatarURL,
}, nil
}

// getGitHubPrimaryEmail fetches primary email from GitHub emails endpoint
func (p *OAuthProvider) getGitHubPrimaryEmail(
ctx context.Context,
client *http.Client,
) (string, error) {
req, err := http.NewRequestWithContext(
ctx, http.MethodGet, "https://api.github.com/user/emails", nil,
)
if err != nil {
return "", fmt.Errorf("failed to create request: %w", err)
return fmt.Errorf("unexpected status %s: %s", resp.Status, body)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchJSON reads the entire non-200 response body via io.ReadAll. Since this is an HTTP helper that may be used against external services, an unexpectedly large body can cause excessive memory use (and it’s also echoed into the error). Consider limiting/truncating the body (e.g., via io.LimitReader) before formatting it into the error, and possibly omitting the body entirely for very large responses.

Copilot uses AI. Check for mistakes.
Comment on lines 142 to +148
if err != nil {
return nil, fmt.Errorf("failed to get user info: %w", err)
return fmt.Errorf("failed to fetch user info: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("GitHub API error: %s - %s", resp.Status, string(body))
}

var user githubUser
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
return nil, fmt.Errorf("failed to decode user info: %w", err)
}

// If email is not public, fetch from emails endpoint
if user.Email == "" {
email, err := p.getGitHubPrimaryEmail(ctx, client)
if err != nil {
return nil, fmt.Errorf("failed to get user email: %w", err)
}
user.Email = email
}

// GitHub requires email for our integration
if user.Email == "" {
return nil, errors.New("GitHub account has no email address")
}

return &OAuthUserInfo{
ProviderUserID: strconv.FormatInt(user.ID, 10),
Username: user.Login,
Email: user.Email,
FullName: user.Name,
AvatarURL: user.AvatarURL,
}, nil
}

// getGitHubPrimaryEmail fetches primary email from GitHub emails endpoint
func (p *OAuthProvider) getGitHubPrimaryEmail(
ctx context.Context,
client *http.Client,
) (string, error) {
req, err := http.NewRequestWithContext(
ctx, http.MethodGet, "https://api.github.com/user/emails", nil,
)
if err != nil {
return "", fmt.Errorf("failed to create request: %w", err)
return fmt.Errorf("unexpected status %s: %s", resp.Status, body)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchJSON returns errors like "failed to fetch user info" / "unexpected status ..." without including the URL being fetched. Since this helper is shared by multiple providers/endpoints (user profile vs emails), the current wording is also a bit misleading. Consider including the request URL in the error and using a more generic message (e.g., "failed to fetch JSON") so failures are actionable when surfaced in logs.

Copilot uses AI. Check for mistakes.
email = user.UserPrincipalName
}
if email == "" {
return nil, errors.New("microsoft account has no email address")
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returned error message uses a lowercase provider name ("microsoft account has no email address"). Since this is likely user-facing and other providers use capitalized names (e.g., "GitHub ..."), consider capitalizing it for consistency ("Microsoft account ...").

Suggested change
return nil, errors.New("microsoft account has no email address")
return nil, errors.New("Microsoft account has no email address")

Copilot uses AI. Check for mistakes.
}

if user.Email == "" {
return nil, errors.New("gitea account has no email address")
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returned error message uses a lowercase provider name ("gitea account has no email address"). For consistency with other providers and display names, consider capitalizing it ("Gitea account ...").

Suggested change
return nil, errors.New("gitea account has no email address")
return nil, errors.New("Gitea account has no email address")

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants