Skip to content

Commit

Permalink
Verified (confirmed) provider support (#24)
Browse files Browse the repository at this point in the history
* fix missing claim id and empty xsrf token

* add confirmed provider #23

* add minimal docks for confirmed provider

* add redirect to from param in confirmed provider

* more docks about confirmed

* rename confirm provider to verify

* update deps

* use gravatar for email in verif provider

* simplify email hasher

* update linter version

* switch to github source for linter

* switch to travis to go v1.12

* test with failed avatar saver
  • Loading branch information
umputun authored Jun 13, 2019
1 parent 7c577fe commit e4139c5
Show file tree
Hide file tree
Showing 14 changed files with 833 additions and 37 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ services:
- mongodb

go:
- "1.11.x"
- "1.12.x"

install: true

before_install:
- export TZ=America/Chicago
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.13.2
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.17.1
- golangci-lint --version
- go get github.com/mattn/goveralls
- export MONGO_TEST=mongodb://127.0.0.1:27017
- export PATH=$(pwd)/bin:$PATH
Expand Down
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ This library provides "social login" with Github, Google, Facebook and Yandex as
- JWT stored in a secure cookie with XSRF protection. Cookies can be session-only
- Minimal scopes with user name, id and picture (avatar) only
- Direct authentication with user's provided credential checker
- Integrated avatar proxy with FS, boltdb and gridfs storages
- Confirmed authentication with user's provided sender (email, im, etc)
- Integrated avatar proxy with FS, boltdb and gridfs storage
- Support of user-defined storage for avatars
- Identicon for default avatars
- Black list with user-defined validator
Expand Down Expand Up @@ -146,6 +147,40 @@ Such provider acts like any other, i.e. will be registered as `/auth/local/login
The API for this provider - `GET /auth/<name>/login?user=<user>&passwd=<password>&aud=<site_id>&session=[1|0]`

_note: password parameter doesn't have to be naked/real password and can be any kind of password hash prepared by caller._

### Verified authentication

Another non-oauth2 provider allowing user-confirmed authentication, for example by email or slack or telegram. This is
done by adding confirmed provider with `auth.AddVerifProvider`.

```go
msgTemplate := "Confirmation email, token: {{.Token}}"
service.AddVerifProvider("email", msgTemplate, sender)
```

Message template may use the follow elements:

- `{{.Address}}` - user address, for example email
- `{{.User}}` - user name
- `{{.Token}}` - confirmation token
- `{{.Site}}` - site ID

Sender should be provided by end-user and implements a single function interface

```go
type Sender interface {
Send(address string, text string) error
}
```

For convenience a functional wrapper `SenderFunc` provided.

The API for this provider:

- `GET /auth/<name>/login?user=<user>&address=<adsress>&aud=<site_id>&from=<url>` - send confirmation request to user
- `GET /auth/<name>/login?token=<conf.token>&sess=[1|0]` - authorize with confirmation token

The provider acts like any other, i.e. will be registered as `/auth/email/login`.

### Customization

Expand Down
6 changes: 2 additions & 4 deletions _example/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ replace github.com/go-pkgz/auth => ../
require (
github.com/go-chi/chi v4.0.1+incompatible
github.com/go-pkgz/auth v0.4.1
github.com/go-pkgz/lgr v0.4.0
github.com/go-pkgz/rest v1.4.0
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/go-pkgz/lgr v0.6.2
github.com/go-pkgz/rest v1.4.1
)
113 changes: 113 additions & 0 deletions _example/go.sum

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,21 @@ func (s *Service) AddDirectProvider(name string, credChecker provider.CredChecke
s.authMiddleware.Providers = s.providers
}

// AddVerifProvider adds provider user's verification sent by sender
func (s *Service) AddVerifProvider(name string, msgTmpl string, sender provider.Sender) {
dh := provider.VerifyHandler{
L: s.logger,
ProviderName: name,
Issuer: s.issuer,
TokenService: s.jwtService,
AvatarSaver: s.avatarProxy,
Sender: sender,
Template: msgTmpl,
}
s.providers = append(s.providers, provider.NewService(dh))
s.authMiddleware.Providers = s.providers
}

// DevAuth makes dev oauth2 server, for testing and development only!
func (s *Service) DevAuth() (*provider.DevAuthServer, error) {
p, err := s.Provider("dev") // peak dev provider
Expand Down
68 changes: 66 additions & 2 deletions auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func TestIntegrationList(t *testing.T) {

b, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
assert.Equal(t, `["dev","github","direct"]`+"\n", string(b))
assert.Equal(t, `["dev","github","direct","email"]`+"\n", string(b))
}

func TestIntegrationUserInfo(t *testing.T) {
Expand All @@ -192,7 +192,7 @@ func TestIntegrationUserInfo(t *testing.T) {
assert.Equal(t, 200, resp.StatusCode)
defer resp.Body.Close()

//get user info
// get user info
req, err := http.NewRequest("GET", "http://127.0.0.1:8080/auth/user", nil)
require.NoError(t, err)
t.Log(resp.Cookies())
Expand Down Expand Up @@ -285,12 +285,56 @@ func TestDirectProvider(t *testing.T) {
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)

body, err := ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
t.Logf("resp %s", string(body))
t.Logf("headers: %+v", resp.Header)
require.Equal(t, 2, len(resp.Cookies()))
assert.Equal(t, "JWT", resp.Cookies()[0].Name)
assert.NotEqual(t, "", resp.Cookies()[0].Value, "token set")
assert.Equal(t, 86400, resp.Cookies()[0].MaxAge)
assert.Equal(t, "XSRF-TOKEN", resp.Cookies()[1].Name)
assert.NotEqual(t, "", resp.Cookies()[1].Value, "xsrf cookie set")

resp, err = client.Get("http://127.0.0.1:8080/private")
require.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)
defer resp.Body.Close()
}

func TestVerifProvider(t *testing.T) {
teardown := prepService(t)
defer teardown()

// login
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get("http://127.0.0.1:8080/auth/email/login?user=dev&address=email")
require.Nil(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)

tkn := sender.text
jar, err := cookiejar.New(nil)
require.Nil(t, err)
client = &http.Client{Jar: jar, Timeout: 5 * time.Second}
resp, err = client.Get("http://127.0.0.1:8080/auth/email/login?token=" + tkn)
require.Nil(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)

body, err := ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
t.Logf("resp %s", string(body))
t.Logf("headers: %+v", resp.Header)
require.Equal(t, 2, len(resp.Cookies()))
assert.Equal(t, "JWT", resp.Cookies()[0].Name)
assert.NotEqual(t, "", resp.Cookies()[0].Value, "token set")
assert.Equal(t, 86400, resp.Cookies()[0].MaxAge)
assert.Equal(t, "XSRF-TOKEN", resp.Cookies()[1].Name)
assert.NotEqual(t, "", resp.Cookies()[1].Value, "xsrf cookie set")

}

func prepService(t *testing.T) (teardown func()) {

options := Opts{
Expand Down Expand Up @@ -320,6 +364,8 @@ func prepService(t *testing.T) (teardown func()) {
return user == "dev_direct" && password == "password", nil
}))

svc.AddVerifProvider("email", "{{.Token}}", &sender)

// run dev/test oauth2 server on :8084
devAuth, err := svc.DevAuth()
require.NoError(t, err)
Expand Down Expand Up @@ -354,3 +400,21 @@ func prepService(t *testing.T) (teardown func()) {
os.RemoveAll("/tmp/auth-pkgz")
}
}

var sender = mockSender{}

type mockSender struct {
err error

to string
text string
}

func (m *mockSender) Send(to string, text string) error {
if m.err != nil {
return m.err
}
m.to = to
m.text = text
return nil
}
22 changes: 11 additions & 11 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
module github.com/go-pkgz/auth

require (
cloud.google.com/go v0.34.0 // indirect
github.com/boltdb/bolt v1.3.1 // indirect
github.com/coreos/bbolt v1.3.0
cloud.google.com/go v0.40.0 // indirect
github.com/coreos/bbolt v1.3.3
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
github.com/go-pkgz/mongo v1.0.0
github.com/go-pkgz/rest v1.2.0
github.com/go-pkgz/lgr v0.6.2 // indirect
github.com/go-pkgz/mongo v1.1.2
github.com/go-pkgz/rest v1.4.1
github.com/kr/pretty v0.1.0 // indirect
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.3.0
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 // indirect
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb // indirect
google.golang.org/appengine v1.4.0 // indirect
go.etcd.io/bbolt v1.3.3 // indirect
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae // indirect
google.golang.org/appengine v1.6.1 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
Loading

0 comments on commit e4139c5

Please sign in to comment.