update auth to v0.5.0
This commit is contained in:
@@ -20,3 +20,4 @@ debug.test
|
||||
*.prof
|
||||
*.test
|
||||
remark42
|
||||
/backend/var/
|
||||
+2
-1
@@ -17,4 +17,5 @@ debug.test
|
||||
.DS_Store
|
||||
.mongo
|
||||
remark42
|
||||
/bin/
|
||||
/bin/
|
||||
/backend/var/
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user