Skip to content
This repository has been archived by the owner on May 29, 2018. It is now read-only.

Commit

Permalink
add oauth
Browse files Browse the repository at this point in the history
  • Loading branch information
hiendv committed Nov 20, 2017
1 parent b09d184 commit bc03573
Show file tree
Hide file tree
Showing 167 changed files with 32,032 additions and 9 deletions.
32 changes: 31 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
[[constraint]]
name = "github.com/satori/go.uuid"
version = "1.1.0"

[[constraint]]
branch = "master"
name = "golang.org/x/oauth2"
1 change: 1 addition & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Auth interface {
type UserService interface {
FindOneByID(string) (User, error)
FindOrCreateOneByEmail(string) (User, error)
FindOneByEmail(string) (User, error)
}

// RoleService is the contract which offers queries on the role entity
Expand Down
9 changes: 9 additions & 0 deletions internal/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package internal

import (
"net/http"
)

type HTTPClient interface {
Get(url string) (resp *http.Response, err error)
}
71 changes: 71 additions & 0 deletions internal/test/fixtures/oauth_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package fixtures

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/hiendv/gate"
"github.com/hiendv/gate/internal"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)

type OAuthClient struct {
token *oauth2.Token
responses map[string]gate.HasEmail
}

var handler = func(w http.ResponseWriter, r *http.Request, user gate.HasEmail) {
result, err := json.Marshal(user)
if err != nil {
http.Error(w, err.Error(), 500)
return
}

fmt.Fprint(w, string(result))
}

func (client OAuthClient) Get(url string) (resp *http.Response, err error) {
if client.token == nil || client.token.AccessToken == "" {
err = errors.New("invalid token")
return
}

user := client.responses[client.token.AccessToken]

result, err := json.Marshal(user)
if err != nil {
return
}

return &http.Response{
Body: ioutil.NopCloser(bytes.NewBuffer(result)),
}, nil
}

type OAuthProvider struct {
Responses map[string]gate.HasEmail
}

func (config OAuthProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
return ""
}

func (config OAuthProvider) Exchange(ctx context.Context, code string) (*oauth2.Token, error) {
if code == "" {
return nil, nil
}

token := &oauth2.Token{}
token.AccessToken = fmt.Sprintf("%s-token", code)

return token, nil
}

func (config OAuthProvider) Client(ctx context.Context, token *oauth2.Token) internal.HTTPClient {
return OAuthClient{token, config.Responses}
}
21 changes: 13 additions & 8 deletions internal/test/fixtures/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,18 @@ func (service MyUserService) FindOneByEmail(email string) (u gate.User, err erro
return
}

// FindOrCreateOneByEmail fetches the user with the given email or create a new one if the user doesn't exist
// CreateOneByEmail creates the user with the given email
func (service *MyUserService) CreateOneByEmail(email string) (u gate.User, err error) {
record := User{
ID: service.GenerateMyUserID(),
Email: email,
}
service.records = append(service.records, record)
u = record
return
}

// FindOrCreateOneByEmail fetches the user with the given email or creates a new one if the user doesn't exist
func (service *MyUserService) FindOrCreateOneByEmail(email string) (u gate.User, err error) {
u, err = service.FindOneByEmail(email)
if err == nil {
Expand All @@ -83,12 +94,6 @@ func (service *MyUserService) FindOrCreateOneByEmail(email string) (u gate.User,
return
}

err = nil
record := User{
ID: service.GenerateMyUserID(),
Email: email,
}
service.records = append(service.records, record)
u = record
u, err = service.CreateOneByEmail(email)
return
}
43 changes: 43 additions & 0 deletions oauth/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package oauth

import (
"github.com/hiendv/gate"
"golang.org/x/oauth2"
"golang.org/x/oauth2/facebook"
"golang.org/x/oauth2/google"
)

// Config is the configuration for OAuth authentication
type Config struct {
gate.Config
ClientID string
ClientSecret string
Scopes []string
Endpoint oauth2.Endpoint
RedirectURI string
UserAPI string
}

func NewGoogleConfig(base gate.Config, id, secret, redirectURI string) Config {
return Config{
base,
id,
secret,
[]string{"https://www.googleapis.com/auth/userinfo.email"},
google.Endpoint,
redirectURI,
"https://www.googleapis.com/oauth2/v3/userinfo",
}
}

func NewFacebookConfig(base gate.Config, id, secret, redirectURI string) Config {
return Config{
base,
id,
secret,
[]string{"email"},
facebook.Endpoint,
redirectURI,
"https://graph.facebook.com/v2.11/me?fields=id,name,email",
}
}
2 changes: 2 additions & 0 deletions oauth/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package oauth is the OAuth2 authentication driver for github.com/hiendv/gate. It uses client implementations, not OAuth servers e.g. Google, Facebook, etc.
package oauth
56 changes: 56 additions & 0 deletions oauth/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package oauth

import (
"encoding/json"

"github.com/hiendv/gate"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)

type GoogleUser struct {
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
}

func (user GoogleUser) GetEmail() string {
if !user.EmailVerified {
return ""
}

return user.Email
}

var GoogleStatelessHandler LoginFunc = func(driver Driver, code, state string) (account gate.HasEmail, err error) {
// State is ignored

token, err := driver.provider.Exchange(oauth2.NoContext, code)
if err != nil {
return
}

client := driver.provider.Client(oauth2.NoContext, token)
if client == nil {
err = errors.New("invalid API client")
return
}

response, err := client.Get(driver.config.UserAPI)
if err != nil {
return
}
if response == nil {
err = errors.New("invalid API response")
return
}
defer response.Body.Close()

var user GoogleUser
err = json.NewDecoder(response.Body).Decode(&user)
if err != nil {
return
}

account = user
return
}

0 comments on commit bc03573

Please sign in to comment.