diff --git a/oauth2/oauth2_test.go b/oauth2/oauth2_test.go index 5b72167..44c0d94 100644 --- a/oauth2/oauth2_test.go +++ b/oauth2/oauth2_test.go @@ -13,6 +13,7 @@ import ( "github.com/aarondl/authboss/v3/mocks" "golang.org/x/oauth2" "golang.org/x/oauth2/facebook" + "golang.org/x/oauth2/github" "golang.org/x/oauth2/google" ) @@ -46,6 +47,20 @@ var testProviders = map[string]authboss.OAuth2Provider{ }, FindUserDetails: FacebookUserDetails, }, + + "github": { + + OAuth2Config: &oauth2.Config{ + ClientID: `jazz`, + ClientSecret: `hands`, + Scopes: []string{`email`}, + Endpoint: github.Endpoint, + // This is typically set by Init() but some tests rely on it's existence + RedirectURL: "https://www.example.com/auth/oauth2/callback/github", + }, + + FindUserDetails: GithubUserDetails, + }, } var testToken = &oauth2.Token{ @@ -75,6 +90,7 @@ func TestInit(t *testing.T) { gets := []string{ "/oauth2/facebook", "/oauth2/callback/facebook", + "/oauth2/github", "/oauth2/callback/github", "/oauth2/google", "/oauth2/callback/google", } if err := router.HasGets(gets...); err != nil { diff --git a/oauth2/providers.go b/oauth2/providers.go index e56b93e..7d3bd1d 100644 --- a/oauth2/providers.go +++ b/oauth2/providers.go @@ -20,6 +20,7 @@ const ( const ( googleInfoEndpoint = `https://www.googleapis.com/userinfo/v2/me` facebookInfoEndpoint = `https://graph.facebook.com/me?fields=name,email` + githubInfoEndpoint = `https://api.github.com/user` ) type googleMeResponse struct { @@ -88,3 +89,37 @@ func FacebookUserDetails(ctx context.Context, cfg oauth2.Config, token *oauth2.T OAuth2Name: response.Name, }, nil } + +type githubMeResponse struct { + ID string `json:"id"` + Email string `json:"email"` + Name string `json:"name"` +} + +// GithubUserDetails can be used as a FindUserDetails function +// for an authboss.OAuth2Provider +func GithubUserDetails(ctx context.Context, cfg oauth2.Config, token *oauth2.Token) (map[string]string, error) { + client := cfg.Client(ctx, token) + resp, err := clientGet(client, githubInfoEndpoint) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + byt, err := io.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrap(err, "failed to read body from github oauth2 endpoint") + } + + var response githubMeResponse + if err = json.Unmarshal(byt, &response); err != nil { + return nil, errors.Wrap(err, "failed to parse json from github oauth2 endpoint") + } + + return map[string]string{ + OAuth2UID: response.ID, + OAuth2Email: response.Email, + OAuth2Name: response.Name, + }, nil + +} diff --git a/oauth2/providers_test.go b/oauth2/providers_test.go index 9cfc02e..250c3ed 100644 --- a/oauth2/providers_test.go +++ b/oauth2/providers_test.go @@ -71,3 +71,36 @@ func TestFacebook(t *testing.T) { t.Error("Name wrong:", name) } } + +func TestGithub(t *testing.T) { + + t.Parallel() + + cfg := *testProviders["github"].OAuth2Config + + tok := &oauth2.Token{ + + AccessToken: "token", + TokenType: "Bearer", + RefreshToken: "refresh", + Expiry: time.Now().Add(60 * time.Minute), + } + + details, err := GithubUserDetails(context.Background(), cfg, tok) + + if err != nil { + + t.Error(err) + } + + if uid, ok := details[OAuth2UID]; !ok || uid != "id" { + t.Error("UID wrong:", uid) + } + if email, ok := details[OAuth2Email]; !ok || email != "email" { + t.Error("Email wrong:", email) + } + if name, ok := details[OAuth2Name]; !ok || name != "name" { + t.Error("Name wrong:", name) + } + +}