README

Auth

Auth is a modular authentication system for web development in Golang, it provides different authentication backends to accelerate your development.

Currently Auth has database password, github, google, facebook, twitter authentication support, and it is fairly easy to add other support based on Auth's Provider interface

Quick Start

Auth aims to provide an easy to use authentication system that don't require much developer's effort.

To use it, basic flow is:

  • Initialize Auth with configuration
  • Register some providers
  • Register it into router

Here is an example:

package main

import (
  "github.com/qor/auth"
  "github.com/qor/auth/auth_identity"
  "github.com/qor/auth/providers/github"
  "github.com/qor/auth/providers/google"
  "github.com/qor/auth/providers/password"
  "github.com/qor/auth/providers/facebook"
  "github.com/qor/auth/providers/twitter"
  "github.com/qor/session/manager"

  _ "github.com/mattn/go-sqlite3"

  "net/http"
)

var (
  // Initialize gorm DB
  gormDB, _ = gorm.Open("sqlite3", "sample.db")

  // Initialize Auth with configuration
  Auth = auth.New(&auth.Config{
    DB: gormDB,
  })
)

func init() {
  // Migrate AuthIdentity model, AuthIdentity will be used to save auth info, like username/password, oauth token, you could change that.
  gormDB.AutoMigrate(&auth_identity.AuthIdentity{})

  // Register Auth providers
  // Allow use username/password
  Auth.RegisterProvider(password.New(&password.Config{}))

  // Allow use Github
  Auth.RegisterProvider(github.New(&github.Config{
    ClientID:     "github client id",
    ClientSecret: "github client secret",
  }))

  // Allow use Google
  Auth.RegisterProvider(google.New(&google.Config{
    ClientID:     "google client id",
    ClientSecret: "google client secret",
    AllowedDomains: []string{}, // Accept all domains, instead you can pass a whitelist of acceptable domains
  }))

  // Allow use Facebook
  Auth.RegisterProvider(facebook.New(&facebook.Config{
    ClientID:     "facebook client id",
    ClientSecret: "facebook client secret",
  }))

  // Allow use Twitter
  Auth.RegisterProvider(twitter.New(&twitter.Config{
    ClientID:     "twitter client id",
    ClientSecret: "twitter client secret",
  }))
}

func main() {
  mux := http.NewServeMux()

  // Mount Auth to Router
  mux.Handle("/auth/", Auth.NewServeMux())
  http.ListenAndServe(":9000", manager.SessionManager.Middleware(mux))
}

That's it, then you could goto http://127.0.0.1:9000/auth/login to try Auth features, like login, logout, register, forgot/change password...

And it could be even easier with Auth Themes, you could integrate Auth into your application with few line configurations.

Usage

Auth has many configurations that could be used to customize it for different usage, lets start from Auth's Config.

Models

Auth has two models, model AuthIdentityModel is used to save login information, model UserModel is used to save user information.

The reason we save auth and user info into two different models, as we want to be able to link a user to mutliple auth info records, so a user could have multiple ways to login.

If this is not required for you, you could just set those two models to same one or skip set UserModel.

  • AuthIdentityModel

Different provider usually use different information to login, like provider password use username/password, github use github user ID, so for each provider, it will save those information into its own record.

You are not necessary to set AuthIdentityModel, Auth has a default definition of AuthIdentityModel, in case of you want to change it, make sure you have auth_identity.Basic embedded, as Auth assume you have same data structure in your database, so it could query/create records with SQL.

  • UserModel

By default, there is no UserModel defined, even though, you still be able to use Auth features, Auth will return used auth info record as logged user.

But usually your application will have a User model, after you set its value, when you register a new account from any provider, Auth will create/get a user with UserStorer, and link its ID to the auth identity record.

Customize views

Auth using Render to render pages, you could refer it for how to register func maps, extend views paths, also be sure to refer BindataFS if you want to compile your application into a binary.

If you want to preprend view paths, you could add them to ViewPaths, which would be helpful if you want to overwrite the default (ugly) login/register pages or develop auth themes like https://github.com/qor/auth_themes

Sending Emails

Auth using Mailer to send emails, by default, Auth will print emails to console, please configure it to send real one.

User Storer

Auth created a default UserStorer to get/save user based on your AuthIdentityModel, UserModel's definition, in case of you want to change it, you could implement your own User Storer

Session Storer

Auth also has a default way to handle sessions, flash messages, which could be overwrited by implementing Session Storer Interface.

By default, Auth is using session's default manager to save data into cookies, but in order to save cookies correctly, you have to register session's Middleware into your router, e.g:

func main() {
	mux := http.NewServeMux()

	// Register Router
	mux.Handle("/auth/", Auth.NewServeMux())
	http.ListenAndServe(":9000", manager.SessionManager.Middleware(mux))
}
Redirector

After some Auth actions, like logged, registered or confirmed, Auth will redirect user to some URL, you could configure which page to redirect with Redirector, by default, will redirct to home page.

If you want to redirect to last visited page, redirect_back is for you, you could configure it and use it as the Redirector, like:

var RedirectBack = redirect_back.New(&redirect_back.Config{
	SessionManager:  manager.SessionManager,
	IgnoredPrefixes: []string{"/auth"},
}

var Auth = auth.New(&auth.Config{
	...
	Redirector: auth.Redirector{RedirectBack},
})

BTW, to make it works correctly, redirect_back need to save last visisted URL into session with session manager for each request, that's means, you need to mount redirect_back, and SessionManager's middleware into router.

http.ListenAndServe(":9000", manager.SessionManager.Middleware(RedirectBack.Middleware(mux)))

Advanced Usage

Auth Themes

In order to save more developer's effort, we have created some auth themes.

It usually has well designed pages, if you don't much custom requirements, you could just have few lines to make Auth system ready to use for your application, for example:

import "github.com/qor/auth_themes/clean"

var Auth = clean.New(&auth.Config{
	DB:         db.DB,
	Render:     config.View,
	Mailer:     config.Mailer,
	UserModel:  models.User{},
})

Check Auth Theme's document for How To use/create Auth themes

Authorization

Authentication is the process of verifying who you are, Authorization is the process of verifying that you have access to something.

Auth package not only provides Authentication, but also Authorization, please checkout authority for more details

Expand ▾ Collapse ▴

Documentation

Index

Constants

View Source
const CurrentUser utils.ContextKey = "current_user"

    CurrentUser context key to get current user from Request

    Variables

    View Source
    var (
    	// ErrInvalidPassword invalid password error
    	ErrInvalidPassword = errors.New("invalid password")
    	// ErrInvalidAccount invalid account error
    	ErrInvalidAccount = errors.New("invalid account")
    	// ErrUnauthorized unauthorized error
    	ErrUnauthorized = errors.New("Unauthorized")
    )
    View Source
    var DefaultAssetHandler = func(context *Context) {
    	asset := strings.TrimPrefix(context.Request.URL.Path, context.Auth.URLPrefix)
    
    	if context.Request.Header.Get("If-Modified-Since") == cacheSince {
    		context.Writer.WriteHeader(http.StatusNotModified)
    		return
    	}
    	context.Writer.Header().Set("Last-Modified", cacheSince)
    
    	if content, err := context.Config.Render.Asset(path.Join("/auth", asset)); err == nil {
    		etag := fmt.Sprintf("%x", md5.Sum(content))
    		if context.Request.Header.Get("If-None-Match") == etag {
    			context.Writer.WriteHeader(http.StatusNotModified)
    			return
    		}
    
    		if ctype := mime.TypeByExtension(filepath.Ext(asset)); ctype != "" {
    			context.Writer.Header().Set("Content-Type", ctype)
    		}
    
    		context.Writer.Header().Set("Cache-control", "private, must-revalidate, max-age=300")
    		context.Writer.Header().Set("ETag", etag)
    		context.Writer.Write(content)
    	} else {
    		http.NotFound(context.Writer, context.Request)
    	}
    }

      DefaultAssetHandler render auth asset file

      View Source
      var DefaultLoginHandler = func(context *Context, authorize func(*Context) (*claims.Claims, error)) {
      	var (
      		req         = context.Request
      		w           = context.Writer
      		claims, err = authorize(context)
      	)
      
      	if err == nil && claims != nil {
      		context.SessionStorer.Flash(w, req, session.Message{Message: "logged"})
      		respondAfterLogged(claims, context)
      		return
      	}
      
      	context.SessionStorer.Flash(w, req, session.Message{Message: template.HTML(err.Error()), Type: "error"})
      
      	responder.With("html", func() {
      		context.Auth.Config.Render.Execute("auth/login", context, req, w)
      	}).With([]string{"json"}, func() {
      
      	}).Respond(context.Request)
      }

        DefaultLoginHandler default login behaviour

        View Source
        var DefaultLogoutHandler = func(context *Context) {
        
        	context.SessionStorer.Delete(context.Writer, context.Request)
        	context.Auth.Redirector.Redirect(context.Writer, context.Request, "logout")
        }

          DefaultLogoutHandler default logout behaviour

          View Source
          var DefaultRegisterHandler = func(context *Context, register func(*Context) (*claims.Claims, error)) {
          	var (
          		req         = context.Request
          		w           = context.Writer
          		claims, err = register(context)
          	)
          
          	if err == nil && claims != nil {
          		respondAfterLogged(claims, context)
          		return
          	}
          
          	context.SessionStorer.Flash(w, req, session.Message{Message: template.HTML(err.Error()), Type: "error"})
          
          	responder.With("html", func() {
          		context.Auth.Config.Render.Execute("auth/register", context, req, w)
          	}).With([]string{"json"}, func() {
          
          	}).Respond(context.Request)
          }

            DefaultRegisterHandler default register behaviour

            Functions

            This section is empty.

            Types

            type Auth

            type Auth struct {
            	*Config
            	// Embed SessionStorer to match Authority's AuthInterface
            	SessionStorerInterface
            	// contains filtered or unexported fields
            }

              Auth auth struct

              func New

              func New(config *Config) *Auth

                New initialize Auth

                func (*Auth) AuthURL

                func (auth *Auth) AuthURL(pth string) string

                  AuthURL generate URL for auth

                  func (*Auth) GetCurrentUser

                  func (auth *Auth) GetCurrentUser(req *http.Request) interface{}

                    GetCurrentUser get current user from request

                    func (*Auth) GetDB

                    func (auth *Auth) GetDB(request *http.Request) *gorm.DB

                      GetDB get db from request

                      func (*Auth) GetProvider

                      func (auth *Auth) GetProvider(name string) Provider

                        GetProvider get provider with name

                        func (*Auth) GetProviders

                        func (auth *Auth) GetProviders() (providers []Provider)

                          GetProviders return registered providers

                          func (*Auth) Login

                          func (auth *Auth) Login(w http.ResponseWriter, req *http.Request, claimer claims.ClaimerInterface) error

                            Login sign user in

                            func (*Auth) Logout

                            func (auth *Auth) Logout(w http.ResponseWriter, req *http.Request)

                              Logout sign current user out

                              func (*Auth) NewServeMux

                              func (auth *Auth) NewServeMux() http.Handler

                                NewServeMux generate http.Handler for auth

                                func (*Auth) RegisterProvider

                                func (auth *Auth) RegisterProvider(provider Provider)

                                  RegisterProvider register auth provider

                                  type Config

                                  type Config struct {
                                  	// Default Database, which will be used in Auth when do CRUD, you can change a request's DB isntance by setting request Context's value, refer https://github.com/qor/auth/blob/master/utils.go#L32
                                  	DB *gorm.DB
                                  	// AuthIdentityModel a model used to save auth info, like email/password, OAuth token, linked user's ID, https://github.com/qor/auth/blob/master/auth_identity/auth_identity.go is the default implemention
                                  	AuthIdentityModel interface{}
                                  	// UserModel should be point of user struct's instance, it could be nil, then Auth will assume there is no user linked to auth info, and will return current auth info when get current user
                                  	UserModel interface{}
                                  	// Mount Auth into router with URLPrefix's value as prefix, default value is `/auth`.
                                  	URLPrefix string
                                  	// ViewPaths prepend views paths for auth
                                  	ViewPaths []string
                                  
                                  	// Auth is using [Render](https://github.com/qor/render) to render pages, you could configure it with your project's Render if you have advanced usage like [BindataFS](https://github.com/qor/bindatafs)
                                  	Render *render.Render
                                  	// Auth is using [Mailer](https://github.com/qor/mailer) to send email, by default, it will print email into console, you need to configure it to send real one
                                  	Mailer *mailer.Mailer
                                  	// UserStorer is an interface that defined how to get/save user, Auth provides a default one based on AuthIdentityModel, UserModel's definition
                                  	UserStorer UserStorerInterface
                                  	// SessionStorer is an interface that defined how to encode/validate/save/destroy session data and flash messages between requests, Auth provides a default method do the job, to use the default value, don't forgot to mount SessionManager's middleware into your router to save session data correctly. refer [session](https://github.com/qor/session) for more details
                                  	SessionStorer SessionStorerInterface
                                  	// Redirector redirect user to a new page after registered, logged, confirmed...
                                  	Redirector RedirectorInterface
                                  
                                  	// LoginHandler defined behaviour when request `{Auth Prefix}/login`, default behaviour defined in http://godoc.org/github.com/qor/auth#pkg-variables
                                  	LoginHandler func(*Context, func(*Context) (*claims.Claims, error))
                                  	// RegisterHandler defined behaviour when request `{Auth Prefix}/register`, default behaviour defined in http://godoc.org/github.com/qor/auth#pkg-variables
                                  	RegisterHandler func(*Context, func(*Context) (*claims.Claims, error))
                                  	// LogoutHandler defined behaviour when request `{Auth Prefix}/logout`, default behaviour defined in http://godoc.org/github.com/qor/auth#pkg-variables
                                  	LogoutHandler func(*Context)
                                  }

                                    Config auth config

                                    type Context

                                    type Context struct {
                                    	*Auth
                                    	Claims   *claims.Claims
                                    	Provider Provider
                                    	Request  *http.Request
                                    	Writer   http.ResponseWriter
                                    }

                                      Context context

                                      func (Context) Flashes

                                      func (context Context) Flashes() []session.Message

                                        Flashes get flash messages

                                        func (Context) FormValue

                                        func (context Context) FormValue(name string) string

                                          FormValue get form value with name

                                          type Provider

                                          type Provider interface {
                                          	GetName() string
                                          
                                          	ConfigAuth(*Auth)
                                          	Login(*Context)
                                          	Logout(*Context)
                                          	Register(*Context)
                                          	Callback(*Context)
                                          	ServeHTTP(*Context)
                                          }

                                            Provider define Provider interface

                                            type Redirector

                                            type Redirector struct {
                                            	*redirect_back.RedirectBack
                                            }

                                              Redirector default redirector

                                              func (Redirector) Redirect

                                              func (redirector Redirector) Redirect(w http.ResponseWriter, req *http.Request, action string)

                                                Redirect redirect back after action

                                                type RedirectorInterface

                                                type RedirectorInterface interface {
                                                	// Redirect redirect after action
                                                	Redirect(w http.ResponseWriter, req *http.Request, action string)
                                                }

                                                  RedirectorInterface redirector interface

                                                  type Schema

                                                  type Schema struct {
                                                  	Provider string
                                                  	UID      string
                                                  
                                                  	Name      string
                                                  	Email     string
                                                  	FirstName string
                                                  	LastName  string
                                                  	Location  string
                                                  	Image     string
                                                  	Phone     string
                                                  	URL       string
                                                  
                                                  	RawInfo interface{}
                                                  }

                                                    Schema auth schema

                                                    type SessionStorer

                                                    type SessionStorer struct {
                                                    	SessionName    string
                                                    	SigningMethod  jwt.SigningMethod
                                                    	SignedString   string
                                                    	SessionManager session.ManagerInterface
                                                    }

                                                      SessionStorer default session storer

                                                      func (*SessionStorer) Delete

                                                      func (sessionStorer *SessionStorer) Delete(w http.ResponseWriter, req *http.Request) error

                                                        Delete delete claims from session manager

                                                        func (*SessionStorer) Flash

                                                        func (sessionStorer *SessionStorer) Flash(w http.ResponseWriter, req *http.Request, message session.Message) error

                                                          Flash add flash message to session data

                                                          func (*SessionStorer) Flashes

                                                          func (sessionStorer *SessionStorer) Flashes(w http.ResponseWriter, req *http.Request) []session.Message

                                                            Flashes returns a slice of flash messages from session data

                                                            func (*SessionStorer) Get

                                                            func (sessionStorer *SessionStorer) Get(req *http.Request) (*claims.Claims, error)

                                                              Get get claims from request

                                                              func (*SessionStorer) SignedToken

                                                              func (sessionStorer *SessionStorer) SignedToken(claims *claims.Claims) string

                                                                SignedToken generate signed token with Claims

                                                                func (*SessionStorer) Update

                                                                func (sessionStorer *SessionStorer) Update(w http.ResponseWriter, req *http.Request, claims *claims.Claims) error

                                                                  Update update claims with session manager

                                                                  func (*SessionStorer) ValidateClaims

                                                                  func (sessionStorer *SessionStorer) ValidateClaims(tokenString string) (*claims.Claims, error)

                                                                    ValidateClaims validate auth token

                                                                    type SessionStorerInterface

                                                                    type SessionStorerInterface interface {
                                                                    	// Get get claims from request
                                                                    	Get(req *http.Request) (*claims.Claims, error)
                                                                    	// Update update claims with session manager
                                                                    	Update(w http.ResponseWriter, req *http.Request, claims *claims.Claims) error
                                                                    	// Delete delete session
                                                                    	Delete(w http.ResponseWriter, req *http.Request) error
                                                                    
                                                                    	// Flash add flash message to session data
                                                                    	Flash(w http.ResponseWriter, req *http.Request, message session.Message) error
                                                                    	// Flashes returns a slice of flash messages from session data
                                                                    	Flashes(w http.ResponseWriter, req *http.Request) []session.Message
                                                                    
                                                                    	// SignedToken generate signed token with Claims
                                                                    	SignedToken(claims *claims.Claims) string
                                                                    	// ValidateClaims validate auth token
                                                                    	ValidateClaims(tokenString string) (*claims.Claims, error)
                                                                    }

                                                                      SessionStorerInterface session storer interface for Auth

                                                                      type UserStorer

                                                                      type UserStorer struct {
                                                                      }

                                                                        UserStorer default user storer

                                                                        func (UserStorer) Get

                                                                        func (UserStorer) Get(Claims *claims.Claims, context *Context) (user interface{}, err error)

                                                                          Get defined how to get user with user id

                                                                          func (UserStorer) Save

                                                                          func (UserStorer) Save(schema *Schema, context *Context) (user interface{}, userID string, err error)

                                                                            Save defined how to save user

                                                                            type UserStorerInterface

                                                                            type UserStorerInterface interface {
                                                                            	Save(schema *Schema, context *Context) (user interface{}, userID string, err error)
                                                                            	Get(claims *claims.Claims, context *Context) (user interface{}, err error)
                                                                            }

                                                                              UserStorerInterface user storer interface