authproxy

package module
v0.0.0-...-17dd32f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2017 License: MIT Imports: 16 Imported by: 1

README

micro-auth-proxy

Transparent auth reverse proxy designed for micro-apps with static client and an API server.

This project is built to serve internal projects at your company. Those can include dashboard, internal tools and others.

It is built with ACL and Docker deployment in mind and fits a very broad use case that can really serve any company that requires authentication for internal tools without messing with auth for every single project.

Overall architecture

Auth proxy architecture

This is built to protext "upstreams" behind an auth backend (Currently only supports github).

Say you have 2 upstreams configured:

// REDACTED
  "upstreams": [
    {
      "type": "http",
      "prefix": "/",
      "location": "https://bdf682c0.ngrok.io"
    },
    {
      "type": "http",
      "prefix": "/api/",
      "location": "https://6cc1b022.ngrok.io"
    }
  ]
// REDACTED

Any request starting with /api/ will be routed through to your backend server. Anything else / will go to the client.

Because request are no longer routed directly from the client to the server, you will not have to define CORS or any sort of absolute URL on the client. You simply call /api if the client is behind the proxy and it will do the rest.

Features
  • Limiting users by username on Github
  • Restrict to a single http method. Some users will only be able to do GET and the rest can do anything. This comes in REALLY handy when you want to limit view vs edit without worrying about it on your backend.
  • Memorize the tokens, we don't DDOs gihub. Once a token has been verified it will be memorized and will not be checked.
  • Save the token in the cookie for the user (See limitations regarding this feature).
  • Docker friendly: Upstreams can be only visible to the main docker container and not accessible to the public. This makes the proxy the only gate to the code and you have to authenticate first.

Configuration

Config file
{
  "authContext": "github",
  "users": [
    {
      "username": "KensoDev"
    },
    {
      "username": "KensoDev2",
      "restrict": "GET"
    }
  ],
  "upstreams": [
    {
      "type": "http",
      "prefix": "/",
      "location": "https://bdf682c0.ngrok.io"
    },
    {
      "type": "http",
      "prefix": "/api/",
      "location": "https://6cc1b022.ngrok.io"
    }
  ]
}

  • users - Users that are allowed to access your app.
  • upstreams - HTTP supported upstreams that the proxy will call
  • authContext - Authentication context you want to use. Currently only supports github, see design docs for the interface implemented.
Env Vars
For Github

ENV vars you'll need to config in order for the proxy to work

  CLIENT_ID
  CLIENT_SECRET
For Auth0

// TODO

These are your Github Client ID and Client secret from the Oauth page.

Usage

usage: authproxy --listen-port=LISTEN-PORT --config-location=CONFIG-LOCATION [<flags>]

Flags:
  --help                     Show context-sensitive help (also try --help-long and --help-man).
  --listen=LISTEN-PORT  Which port should the proxy listen on
  --config=CONFIG-LOCATION
                             Proxy Config Location

Limitations

  • Currently, for V1, this only supports HTTP backend. So for example if you have a frontend and a backend, you can host your frontend on some service that support HTTP endpoint. eg: S3 with static hosting. Some heroku or anything that will give you a valid web address.

  • If you have a client side app calling to a server side through the proxy (which is what this is designed for), you will need to pass in the cookie for all server requests.

    If you are using fetch, you will need to use { credentials: "include" }. This will make sure that the github token will get passed through to the proxy and it will direct the request to the server and not redirect to Github all over again.

  • From your client side, you can either call the PROXY url as the backend or simply use the prefix /api you defined in your configuration.

Design Decisions and architecture

The authentication context

The authentication context is the class used in order to check whether a user is valid, whether the action they try to do is allowed or not.

The interface supports a token and a username and returns simple objects such as strings and booleans.

This was done by design in order to allow you to extend it and add many other contexts beyond Github such as Auth0, Google and so on.

IsAccessTokenValidAndUserAuthorized(accessToken string) bool

This method is used to validate the token and check whether the user is authorized. Checking whether they're included in the list of users or not

GetUserName(accessToken sting) string

Passing in an access token, this should return the username as a string

GetHTTPEndpointPrefix() string

What endpoint does the external service used to authentication needs exposing.

For example, Github wants you to expose a /callback which they send a code to. You can read more about it here in the docs.

ServeHTTP(w http.ResponseWriter, req *http.Request)

Your auth context needs to expose an HTTP handler in order to handle the callback from the service.

Github Auth Context Token Memoization Process

Once you have a Github Token in the http cookie, the proxy will validate that token against Github. Make sure the user is authorized (from the user list) and will not validate again.

The cookie expiration is set for 24 hours, when you authenticate again, you will get revalidated.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Asset

func Asset(name string) ([]byte, error)

Asset loads and returns the asset for the given name. It returns an error if the asset could not be found or could not be loaded.

func AssetDir

func AssetDir(name string) ([]string, error)

AssetDir returns the file names below a certain directory embedded in the file by go-bindata. For example if you run go-bindata on data/... and data contains the following hierarchy:

data/
  foo.txt
  img/
    a.png
    b.png

then AssetDir("data") would return []string{"foo.txt", "img"} AssetDir("data/img") would return []string{"a.png", "b.png"} AssetDir("foo.txt") and AssetDir("notexist") would return an error AssetDir("") will return []string{"data"}.

func AssetInfo

func AssetInfo(name string) (os.FileInfo, error)

AssetInfo loads and returns the asset info for the given name. It returns an error if the asset could not be found or could not be loaded.

func AssetNames

func AssetNames() []string

AssetNames returns the names of the assets.

func GetenvOrDie

func GetenvOrDie(k string) string

GetenvOrDie is a safety wrapper around os.Getenv. This fatals if the key is unset or empty.

func MapUserNames

func MapUserNames(in []User, f mapf) []string

func MustAsset

func MustAsset(name string) []byte

MustAsset is like Asset but panics when Asset would return an error. It simplifies safe initialization of global variables.

func NewHttpListeners

func NewHttpListeners(config *Configuration)

func RenderTemplate

func RenderTemplate(tpl string, data interface{}) (b []byte, err error)

RenderTemplate is a generic text/template render wrapper.

func RestoreAsset

func RestoreAsset(dir, name string) error

RestoreAsset restores an asset under the given directory

func RestoreAssets

func RestoreAssets(dir, name string) error

RestoreAssets restores an asset under the given directory recursively

Types

type Auth0AuthContext

type Auth0AuthContext struct {
	Config            *Configuration
	AuthDomain        string
	ClientID          string
	ClientSecret      string
	CallbackURL       string
	ValidAccessTokens map[string]string
	HTMLFile          []byte
}

func NewAuth0AuthContext

func NewAuth0AuthContext(config *Configuration) *Auth0AuthContext

func (*Auth0AuthContext) GetCookieName

func (c *Auth0AuthContext) GetCookieName() string

func (*Auth0AuthContext) GetHTTPEndpointPrefix

func (c *Auth0AuthContext) GetHTTPEndpointPrefix() string

func (*Auth0AuthContext) GetLoginPage

func (c *Auth0AuthContext) GetLoginPage() ([]byte, error)

func (*Auth0AuthContext) GetUserName

func (c *Auth0AuthContext) GetUserName(accessToken string) string

func (*Auth0AuthContext) IsAccessTokenValidAndUserAuthorized

func (c *Auth0AuthContext) IsAccessTokenValidAndUserAuthorized(accessToken string) bool

func (*Auth0AuthContext) RenderHTMLFile

func (c *Auth0AuthContext) RenderHTMLFile() error

func (*Auth0AuthContext) ServeHTTP

func (c *Auth0AuthContext) ServeHTTP(w http.ResponseWriter, req *http.Request)

type AuthenticationContext

type AuthenticationContext interface {
	IsAccessTokenValidAndUserAuthorized(accessToken string) bool
	GetUserName(accessToken string) string
	GetHTTPEndpointPrefix() string
	GetCookieName() string
	GetLoginPage() ([]byte, error)
	ServeHTTP(w http.ResponseWriter, req *http.Request)
	RenderHTMLFile() error
}

type Configuration

type Configuration struct {
	AuthenticationContextName string     `json:"authContext"`
	Upstreams                 []Upstream `json:"upstreams"`
	Users                     []User     `json:"users"`
}

func NewConfiguration

func NewConfiguration(data []byte) (*Configuration, error)

func (*Configuration) GetAuthenticationContext

func (c *Configuration) GetAuthenticationContext() (cx AuthenticationContext, err error)

func (*Configuration) GetRestrictionsForUsername

func (c *Configuration) GetRestrictionsForUsername(username string) string

func (*Configuration) ShouldRestrictUser

func (c *Configuration) ShouldRestrictUser(username string, method string) bool

type ConfigurationReader

type ConfigurationReader struct {
	FileLocation string
}

func NewConfigurationReader

func NewConfigurationReader(location string) *ConfigurationReader

func (*ConfigurationReader) ReadConfigurationFile

func (r *ConfigurationReader) ReadConfigurationFile() ([]byte, error)

type GithubAuthContext

type GithubAuthContext struct {
	ClientID          string
	ClientSecret      string
	Config            *Configuration
	CookirName        string
	ValidAccessTokens map[string]string
	HTMLFile          []byte
}

func NewGithubAuthContext

func NewGithubAuthContext(config *Configuration) *GithubAuthContext

func (*GithubAuthContext) GetCookieName

func (c *GithubAuthContext) GetCookieName() string

func (*GithubAuthContext) GetHTTPEndpointPrefix

func (c *GithubAuthContext) GetHTTPEndpointPrefix() string

func (*GithubAuthContext) GetLoginPage

func (c *GithubAuthContext) GetLoginPage() ([]byte, error)

func (*GithubAuthContext) GetUserDetailsFromGithub

func (c *GithubAuthContext) GetUserDetailsFromGithub(accessToken string) ([]byte, error)

func (*GithubAuthContext) GetUserName

func (c *GithubAuthContext) GetUserName(accessToken string) string

func (*GithubAuthContext) IsAccessTokenValidAndUserAuthorized

func (c *GithubAuthContext) IsAccessTokenValidAndUserAuthorized(accessToken string) bool

func (*GithubAuthContext) ParseUserResponse

func (c *GithubAuthContext) ParseUserResponse(response []byte) (*GithubUser, error)

func (*GithubAuthContext) RenderHTMLFile

func (c *GithubAuthContext) RenderHTMLFile() error

func (*GithubAuthContext) ServeHTTP

func (c *GithubAuthContext) ServeHTTP(w http.ResponseWriter, req *http.Request)

type GithubAuthRequest

type GithubAuthRequest struct {
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
	Code         string `json:"code"`
}

type GithubAuthResponse

type GithubAuthResponse struct {
	AccessToken string `json:"access_token"`
	Scope       string `json:"scope"`
}

type GithubUser

type GithubUser struct {
	ID       int    `json:"id"`
	UserName string `json:"login"`
}

type Listener

type Listener struct {
	Prefix      string
	Location    string
	Proxy       *httputil.ReverseProxy
	Hostname    string
	AuthContext AuthenticationContext
	Config      *Configuration
}

func (*Listener) ServeAuthenticatedRequest

func (l *Listener) ServeAuthenticatedRequest(w http.ResponseWriter, req *http.Request, accessToken string)

func (*Listener) ServeHTTP

func (l *Listener) ServeHTTP(w http.ResponseWriter, req *http.Request)

type Upstream

type Upstream struct {
	Prefix   string `json:"prefix"`
	Location string `json:"location"`
	Type     string `json:"type"`
}

type User

type User struct {
	Username string `json:"username"`
	Restrict string `json:"restrict"`
}

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL