autho

package module
v0.0.0-...-e4d3a07 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2022 License: MIT Imports: 4 Imported by: 0

README ¶

Autho

Autho is a customisable and clean implementation of all OAuth and OAuth2.0 web providers with full implementation for popular providers such as twitter, facebook, github and google. Autho helps you focus on the logic side of your application whilst providing an idiomatic api to handle the boilerplate.

Autho was inspired by: gologin and goth

Quickstart 🚀

Install autho:

go get github.com/Lambels/autho

Github Provider:

import (
    "github.com/Lambels/autho"
    gh "github.com/Lambels/autho/github"
	"github.com/google/go-github/v32/github"
    "golang.org/x/oauth2"
	oauthGh "golang.org/x/oauth2/github"
)

func main() {
    ckCfg := autho.NewDebugCookieConfig("my-cookie")
    ghCfg := &oauth2.Config{
        ClientID: "client-ID",
        ClientSecret: "client-secret",
        Endpoint: oauthGh.Endpoint,
    }
    app := autho.NewApp(
        autho.NewProviderApp(
            "github/login",
            "github/callback",
            gh.NewCallbackHandler(ghCfg, ckCfg, nil, terminalHandler),
            gh.NewLoginHandler(ghCfg, ckCfg),
        ),
    )
    mux := http.NewServeMux()
    app.Register(mux)
    srv := &http.Server{
		Handler: mux,
	}
	if err := srv.ListenAndServe(); err != nil {
		panic(err)
	}
}

// this is what gets called at the end of the OAuth process if everything goes right.
// use the request context to pull the *github.User .
func terminalHandler(w http.ResponseWriter, r *http.Request) {
    // at this point we for sure have a user object under the ctx
    // but still check for ok to avoid any panics.
    user, ok := autho.UserFromContext(r.Context()).(*github.User)
    if !ok {
        return
    }

    fmt.Println(user)
}

Docs

GoDoc

Overview (Callback and Login Phases)

Simillarly to gologin, autho chains http.Handlers to build handlers for the two main stages of OAuth flows: "The Login Phase" and "The Callback Phase", in autho the login phase has its own handlers in the autho/oauth1 and autho/oauth2 packages which are at the core of autho. autho/oauth1.NewLoginHandler() and autho/oauth2.NewLoginHandler() are the two core login handlers, then packages such as autho/facebook.NewLoginHandler() or autho/twitter.NewLoginHandler() provide abstractions of those handlers respectivly for top level use. Simillarly the callback phase has its own handler which is constructed by wrapping the providers specific user handler with the protocols token handler.

Facebook CallbackHandler example: Oauth2 Token Handler + Facebook User Handler = Facebook Callback Handler

LoginHandler OAuth1.0 And OAuth2.0

The login handler is in both OAuth1.0 and OAuth2.0 responsible for redirecting the user to the provider for obtaining the users grant. However there are differences:

OAuth1.0:

  • The OAuth1.0 Login Handler is responsible for, if the provider requires it, setting a cookie with the request secret for later reading in the Token Handler. This behaviour is flagged by passing nil to the cookie config parameter, if its nil the provider doesent require it, if it isnt the provider requires and the cookie must be set accordingly.

OAuth2.0:

  • The OAuth2.0 Login Handler is responsible for setting the state value in a short lived cookie to be validated in the token handler step.

CallbackHandler

The callback handler is specific to each provider and can be built either by steps or by using the providers helper method.

autho/twitter.NewCallbackHandler() or autho/github.NewCallbackHandler() are examples of building callback handlers using the provided helper methods in each provider package. In essence what these helper functions are doing is using the default token and user handlers for each provider to build the callback handler.

Basically a callback handler is just an http.Handler but obtained through chaining multiple http.Handlers.

The flow looks something like this:

OAuth2.0:

Provider (redirects to your server) -> TokenHandler (validates state + exchanges auth code for tokens) -> UserHandler (uses obtained tokens to exchange for user. unique to each provider) -> TerminalHandler (has access to tokens and user resource. end logic)

OAuth1.0:

Provider (redirects to your server) -> TokenHandler (exchanges request token + request secret + verifier for tokens) -> UserHandler (uses obtained tokens to exchange for user. unique to each provider) -> TerminalHandler (has access to tokens and user resource. end logic)

The main difference between OAuth2.0 and OAuth1.0 is in the token hanlder.

TokenHandler OAuth1.0 And OAuth2.0

The token handler is in both OAuth1.0 and OAuth2.0 responsible for, as the name says, obtaining the tokens used in exchange for the users resource. However there are some differences:

OAuth1.0:

  • The OAuth1.0 Token Handler is responsible for grabbing the request secret from the short lived cookie if the provider requires so. Some providers require that the request secret is persisted throughout the exchange, some dont. This behaviour is flagged by passing nil to the cookie config parameter, if its nil the provider doesent require it, if it isnt the provider requires and the cookie must be read accordingly.

OAuth2.0:

  • The OAuth2.0 Token Handler is responsible for validating the state from the short lived cookie and compare it with the state from the request.

Finally both token handlers add the tokens to the request context to be used down the line by the user handler using the autho/oauth1.ContextWithToken() or autho/oauth2.ContextWithToken() respectively.

UserHandler

The user handler is specific to each provider and comes in chain right after the token handler, it is responsible for exchanging the tokens obtained from the request context using the autho/oauth1.TokenFromContext() or autho/oauth2.TokenFromContext() for the user resource.

Finally after obtaining the user resource the user handler adds the user object to the request context using the autho.UserWithContext() method, it then calls the terminal handler which is last in chain.

TerminalHandler

The terminal handler is the "end logic" and must be implemented by you. To access the tokens if your provider uses OAuth1.0 use autho/oauth1.TokenFromContext() else use autho/oauth2.TokenFromContext(). To access the user resource use autho.UserFromContext() which returns interface{} so it is up to you to parse the interface{} to your own type. To know what type the user is of check your providers user handler docs which specifies the user type.

ErrorHandler

Obviously throughout the whole OAuth1.0 or OAuth2.0 flow errors can occur, the error handler gets called by handlers when an error occurs.

This is githubs callback handler signature:

autho/github.NewCallbackHandler(cfg *oauth2.Config, ckCfg *autho.CookieConfig, errHandler, terminalHandler http.Handler)

The errHandler parameter is a http.Handler, if nil is passed the autho.DefaultFailureHandler is used, else you can implement your own. The error inside the handler is obtainable by the request context using the autho.ErrorFromContext() and its up to you how you handle it.

Customising The Handlers

There are essentially 5 http.Handlers in the whole exchange, but you can chain as many as you want by chaining n http.Handlers.

The 5 handlers used by autho defaultly are:

  • LoginHandler
  • TokenHandler
  • UserHandler
  • TerminalHandler
  • ErrorHandler

The flow is: LoginHandler -> Provider Provider -> TokenHandler -> UserHandler -> TerminalHandler (any errors -> ErrorHandler)

Obviously you can provide your own LoginHandler or CallbackHandler as long as it is an http.Handler to autho.NewProviderApp()

When you call autho/someProvider.NewCallbackHandler() the helper function chains:

TokenHandler(
    ErrorHandler,
    UserHandler(
        ErrorHandler,
        TerminalHandler,
    )
)

Code Examples

Github OAuth2.0

This is a full github provider implemented, the ghCfg is a *golang.org/x/oauth2.Config and the ckCfg is a *autho.CookieConfig. The terminal handler is the end logic which must be implemented by the user. The user object is obtainable from the request context with the method autho.UserFromContext(), to know what type to parse the user object to, check the providers user handler docs, in our case its the Github User Handler.

app := autho.NewApp(
    autho.NewProviderApp(
        "github/login",
        "github/callback",
        gh.NewCallbackHandler(ghCfg, ckCfg, nil, terminalHandler),
        gh.NewLoginHandler(ghCfg, ckCfg),
    ),
)

func terminalHandler(w http.ResponseWriter, r *http.Request) {
    // at this point we for sure have a user object under the ctx
    // but still check for ok to avoid any panics.
    user, ok := autho.UserFromContext(r.Context()).(*github.User)
    if !ok {
        return
    }

    fmt.Println(user)
}

Twitter OAuth1.0

Simillarly to the Github implementation, we will use the autho.NewProviderApp() method to create our twitter provider. Only difference here is the twCfg and the source of the handlers. The twCfg is a *github.com/dghubble/oauth1.Config. The Login and Callback hanlder come from the autho/twitter package. A complete abstraction is made between OAuth1.0 and OAuth2.0 .

app := autho.NewApp(
    autho.NewProviderApp(
        "twitter/login",
        "twitter/callback",
        tw.NewCallbackHandler(twCfg, ckCfg, nil, terminalHandler),
        tw.NewLoginHandler(twCfg, ckCfg),
    ),
)

func terminalHandler(w http.ResponseWriter, r *http.Request) {
    // at this point we for sure have a user object under the ctx
    // but still check for ok to avoid any panics.
    user, ok := autho.UserFromContext(r.Context()).(*twitter.User)
    if !ok {
        return
    }

    fmt.Println(user)
}

Documentation ¶

Index ¶

Constants ¶

This section is empty.

Variables ¶

View Source
var DefaultFailureHandle http.HandlerFunc = failureHandler

DefaultFailureHandle sends a response with error code: 400 (Bad Request) and the error text.

View Source
var ErrNoUser error = errors.New("autho: unable to get user from provider")

ErrNoUser represents the user handler not being able to reach the user resoursce.

Functions ¶

func ContextWithError ¶

func ContextWithError(ctx context.Context, err error) context.Context

ContextWithError adds err to the context to be used by the err handler.

func ContextWithUser ¶

func ContextWithUser(ctx context.Context, user interface{}) context.Context

ContextWithUser adds the user to the context to be used by the terminal handler.

func ErrorFromContext ¶

func ErrorFromContext(ctx context.Context) error

ErrorFromContext returns any error from ctx. If an error is present then its parsed and returned else nil is returned.

func GetCookie ¶

func GetCookie(conf *CookieConfig, r *http.Request) *http.Cookie

GetCookie gets a cookie from the request if possible or returns a new cookie structured after the config.

func PassError ¶

func PassError(err error, errHandler http.Handler, w http.ResponseWriter, r *http.Request)

PassError is a helper function which passes err to the error handler via the ContextWithError method.

func UserFromContext ¶

func UserFromContext(ctx context.Context) interface{}

UserFromContext harvests the user struct from the request context. Parse the return value to the specific user you are expecting from the user handler with the , ok idiom. If !ok then no user is set. (check your providers user handler for user type)

Types ¶

type CookieConfig ¶

type CookieConfig struct {
	// Name sets the name of the cookie.
	Name string
	// Path Sets the path of the cookie which defaults to the path of the current responding
	// URL.
	Path string
	// Domain Sets the domain of the cookie which defaults to the domain of the current app.
	Domain string
	// Expires sets the expiry date of the cookie.
	Expires time.Time
	// MaxAge sets TTL for the cookie in seconds.
	MaxAge int
	// Secure indicates if the cookie will be sent through an HTTPS secure connection.
	Secure bool
	// HttpOnly indicates to the browser if the cookie is accessable by client-side scripts.
	HttpOnly bool
}

CookieConfig represents a config used by handlers to create cookies throughout the oauth flow.

func NewDebugCookieConfig ¶

func NewDebugCookieConfig(name string) *CookieConfig

func NewProductionCookieConfig ¶

func NewProductionCookieConfig(name string) *CookieConfig

type Registerer ¶

type Registerer interface {
	Register(multiplexer)
}

func NewApp ¶

func NewApp(apps ...Registerer) Registerer

NewApp creates a new autho app which consists of multiple providers (OAuth 1 or 2), the new app is used to register the providers' callback and login URL to the provided multiplexer.

mux := http.NewServeMux()
app := autho.NewApp()
app.Register(mux)

func NewProviderApp ¶

func NewProviderApp(
	callbackURL, loginURL string,
	callbackHandler, loginHandler http.Handler,
) Registerer

NewProviderApp creates a new provider application.

Directories ¶

Path Synopsis

Jump to

Keyboard shortcuts

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