update auth to v0.5.0

This commit is contained in:
Umputun
2019-03-24 16:35:23 -05:00
parent b534991770
commit 2a03afddc6
13 changed files with 77 additions and 42 deletions
+1
View File
@@ -20,3 +20,4 @@ debug.test
*.prof
*.test
remark42
/backend/var/
+2 -1
View File
@@ -17,4 +17,5 @@ debug.test
.DS_Store
.mongo
remark42
/bin/
/bin/
/backend/var/
+1 -1
View File
@@ -13,7 +13,7 @@ require (
github.com/go-chi/chi v3.3.2+incompatible
github.com/go-chi/cors v1.0.0
github.com/go-chi/render v1.0.0
github.com/go-pkgz/auth v0.4.2
github.com/go-pkgz/auth v0.5.0
github.com/go-pkgz/lcw v0.2.0
github.com/go-pkgz/lgr v0.4.0
github.com/go-pkgz/mongo v1.1.2
+2
View File
@@ -28,6 +28,8 @@ github.com/go-chi/render v1.0.0 h1:cLJlkaTB4xfx5rWhtoB0BSXsXVJKWFqv08Y3cR1bZKA=
github.com/go-chi/render v1.0.0/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
github.com/go-pkgz/auth v0.4.2 h1:WY3XzjUieUGxJSjXDU0rLrKt8RcPEaGdtLVRS7M56wU=
github.com/go-pkgz/auth v0.4.2/go.mod h1:CWtB8dHmOv+TfF3MUzKwk/YwTLepC2TaDL05A+pFVBM=
github.com/go-pkgz/auth v0.5.0 h1:+wqppq35x83PchZNZ7SHHYLI/e8WeETFouujDLsklac=
github.com/go-pkgz/auth v0.5.0/go.mod h1:CWtB8dHmOv+TfF3MUzKwk/YwTLepC2TaDL05A+pFVBM=
github.com/go-pkgz/lcw v0.2.0 h1:aFoKUG8q0YybId+ThVRQpDMjjuSG4hkLL1EA2xUtruc=
github.com/go-pkgz/lcw v0.2.0/go.mod h1:k+PY1CkCMTLXILtFoJOyK65Qqi9rkoTYunFH1vE/C0I=
github.com/go-pkgz/lgr v0.2.2/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
+4 -1
View File
@@ -10,7 +10,8 @@ This library provides "social login" with Github, Google, Facebook and Yandex as
- 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
- Support of user-defined storages for avatars
- Support of user-defined storage for avatars
- Identicon for default avatars
- Black list with user-defined validator
- Multiple aud (audience) supported
- Secure key with customizable `SecretReader`
@@ -144,6 +145,8 @@ 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._
### Customization
There are several ways to adjust functionality of the library:
+2
View File
@@ -1,3 +1,4 @@
// Package auth provides "social login" with Github, Google, Facebook and Yandex as well as custom auth providers.
package auth
import (
@@ -226,6 +227,7 @@ func (s *Service) AddDirectProvider(name string, credChecker provider.CredChecke
Issuer: s.issuer,
TokenService: s.jwtService,
CredChecker: credChecker,
AvatarSaver: s.avatarProxy,
}
s.providers = append(s.providers, provider.NewService(dh))
s.authMiddleware.Providers = s.providers
+32 -2
View File
@@ -13,6 +13,7 @@ import (
"time"
"github.com/go-pkgz/rest"
"github.com/nullrocks/identicon"
"github.com/pkg/errors"
"golang.org/x/image/draw"
@@ -33,9 +34,19 @@ type Proxy struct {
// Put stores retrieved avatar to avatar.Store. Gets image from user info. Returns proxied url
func (p *Proxy) Put(u token.User) (avatarURL string, err error) {
// no picture for user, try default avatar
// no picture for user, try to generate identicon avatar
if u.Picture == "" {
return "", errors.Errorf("no picture for %s", u.ID)
b, err := GenerateAvatar(u.ID)
if err != nil {
return "", errors.Errorf("no picture for %s", u.ID)
}
avatarID, err := p.Store.Put(u.ID, p.resize(bytes.NewBuffer(b), p.ResizeLimit)) // put returns avatar base name, like 123456.image
if err != nil {
return "", err
}
p.Logf("[DEBUG] saved identicon avatar to %s, user %q", avatarID, u.Name)
return p.URL + p.RoutePath + "/" + avatarID, nil
}
// load avatar from remote location
@@ -152,6 +163,25 @@ func (p *Proxy) resize(reader io.Reader, limit int) io.Reader {
}
return &out
}
// GenerateAvatar for give user with identicon
func GenerateAvatar(user string) ([]byte, error) {
iconGen, err := identicon.New("pkgz/auth", 5, 5)
if err != nil {
return nil, errors.Wrap(err, "can't create identicon service")
}
ii, err := iconGen.Draw(user) // generate an IdentIcon
if err != nil {
return nil, errors.Wrapf(err, "failed to draw avatar for %s", user)
}
buf := &bytes.Buffer{}
err = ii.Png(300, buf)
return buf.Bytes(), err
}
func retry(retries int, delay time.Duration, fn func() error) (err error) {
for i := 0; i < retries; i++ {
if err = fn(); err == nil {
+2
View File
@@ -1,3 +1,5 @@
// Package logger defines interface for logging. Implementation should be passed by user.
// Also provides NoOp (do-nothing) and Std (redirect to std log) predefined loggers.
package logger
import "log"
+6 -1
View File
@@ -1,4 +1,7 @@
// Package middleware provides oauth2 support as well as related middlewares.
// Package middleware provides login middlewares:
// - Auth: adds auth from session and populates user info
// - Trace: populates user info if token presented
// - AdminOnly: restrict access to admin users only
package middleware
import (
@@ -35,6 +38,7 @@ type TokenService interface {
Reset(w http.ResponseWriter)
}
// adminUser sets claims for an optional basic auth
var adminUser = token.User{
ID: "admin",
Name: "admin",
@@ -120,6 +124,7 @@ func (a *Authenticator) auth(reqAuth bool) func(http.Handler) http.Handler {
// refreshExpiredToken makes a new token with passed claims
func (a *Authenticator) refreshExpiredToken(w http.ResponseWriter, claims token.Claims, tkn string) (token.Claims, error) {
// cache refreshed claims for given token in order to eliminate multiple refreshes for concurrent requests
if a.RefreshCache != nil {
if c, ok := a.RefreshCache.Get(tkn); ok {
// already in cache
+2 -24
View File
@@ -1,7 +1,6 @@
package provider
import (
"bytes"
"context"
"fmt"
"net/http"
@@ -10,9 +9,8 @@ import (
"text/template"
"time"
"github.com/go-pkgz/auth/avatar"
"github.com/go-pkgz/auth/logger"
"github.com/nullrocks/identicon"
"github.com/pkg/errors"
"golang.org/x/oauth2"
"github.com/go-pkgz/auth/token"
@@ -31,7 +29,6 @@ type DevAuthServer struct {
Automatic bool
username string // unsafe, but fine for dev
iconGen *identicon.Generator
httpServer *http.Server
lock sync.Mutex
}
@@ -42,10 +39,6 @@ func (d *DevAuthServer) Run(ctx context.Context) {
d.Logf("[INFO] run local oauth2 dev server on %d, redir url=%s", devAuthPort, d.Provider.redirectURL)
d.lock.Lock()
var err error
d.iconGen, err = identicon.New("github", 5, 3)
if err != nil {
d.Logf("[WARN] can't create identicon, %s", err)
}
userFormTmpl, err := template.New("page").Parse(devUserFormTmpl)
if err != nil {
@@ -113,7 +106,7 @@ func (d *DevAuthServer) Run(ctx context.Context) {
case strings.HasPrefix(r.URL.Path, "/avatar"):
user := r.URL.Query().Get("user")
b, e := d.genAvatar(user)
b, e := avatar.GenerateAvatar(user)
if e != nil {
w.WriteHeader(http.StatusNotFound)
return
@@ -177,21 +170,6 @@ func NewDev(p Params) Oauth2Handler {
})
}
func (d *DevAuthServer) genAvatar(user string) ([]byte, error) {
if d.iconGen == nil {
return nil, errors.Errorf("no iconGen, skip avatar generation for %s", user)
}
ii, err := d.iconGen.Draw(user) // Generate an IdentIcon
if err != nil {
return nil, errors.Wrapf(err, "failed to draw avatar for %s", user)
}
buf := &bytes.Buffer{}
err = ii.Png(300, buf)
return buf.Bytes(), err
}
var devUserFormTmpl = `
<html>
<head>
+15 -6
View File
@@ -20,6 +20,7 @@ type DirectHandler struct {
ProviderName string
TokenService TokenService
Issuer string
AvatarSaver AvatarSaver
}
// CredChecker defines interface to check credentials
@@ -45,23 +46,31 @@ func (p DirectHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
aud := r.URL.Query().Get("aud")
sessOnly := r.URL.Query().Get("sess") == "1"
if p.CredChecker == nil {
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, errors.New("empty credential store"), "no credential store")
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError,
errors.New("no credential checker"), "no credential checker")
return
}
ok, err := p.CredChecker.Check(user, password)
if err != nil {
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to access creds store")
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to check user credentials")
return
}
if !ok {
rest.SendErrorJSON(w, r, p.L, http.StatusForbidden, nil, "incorrect user or password")
return
}
u := token.User{
Name: user,
ID: p.ProviderName + "_" + token.HashID(sha1.New(), user),
}
u, err = setAvatar(p.AvatarSaver, u)
if err != nil {
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to save avatar to proxy")
return
}
claims := token.Claims{
User: &token.User{
Name: user,
ID: p.ProviderName + "_" + token.HashID(sha1.New(), user),
},
User: &u,
StandardClaims: jwt.StandardClaims{
Issuer: p.Issuer,
Audience: aud,
+7 -5
View File
@@ -20,12 +20,14 @@ const adminAttr = "admin" // predefined attribute key for bool isAdmin status
// User is the basic part of oauth data provided by service
type User struct {
// set by service
Name string `json:"name"`
ID string `json:"id"`
Picture string `json:"picture"`
IP string `json:"ip,omitempty"`
Email string `json:"email,omitempty"`
// set by client
IP string `json:"ip,omitempty"`
Email string `json:"email,omitempty"`
Attributes map[string]interface{} `json:"attrs,omitempty"`
}
@@ -82,7 +84,7 @@ func (u *User) SliceAttr(key string) []string {
return r
}
// SetSliceAttr sets boolean attribute
// SetSliceAttr sets slice attribute for given key
func (u *User) SetSliceAttr(key string, val []string) {
if u.Attributes == nil {
u.Attributes = map[string]interface{}{}
@@ -112,7 +114,7 @@ func HashID(h hash.Hash, val string) string {
type contextKey string
// MustGetUserInfo fails if can't extract user data from the request.
// MustGetUserInfo gets user info and panics if can't extract it from the request.
// should be called from authenticated controllers only
func MustGetUserInfo(r *http.Request) User {
user, err := GetUserInfo(r)
@@ -122,7 +124,7 @@ func MustGetUserInfo(r *http.Request) User {
return user
}
// GetUserInfo returns user from request context
// GetUserInfo returns user info from request context
func GetUserInfo(r *http.Request) (user User, err error) {
ctx := r.Context()
+1 -1
View File
@@ -30,7 +30,7 @@ github.com/go-chi/chi/middleware
github.com/go-chi/cors
# github.com/go-chi/render v1.0.0
github.com/go-chi/render
# github.com/go-pkgz/auth v0.4.2
# github.com/go-pkgz/auth v0.5.0
github.com/go-pkgz/auth
github.com/go-pkgz/auth/avatar
github.com/go-pkgz/auth/provider