⛩️ Go library for protecting HTTP handlers with authorization bearer token.

Overview

g8

build Go Report Card codecov Go version Go Reference

G8, pronounced Gate, is a simple Go library for protecting HTTP handlers with tokens.

Tired of constantly re-implementing a security layer for each of applications? Me too, that's why I made G8.

Installation

go get -u github.com/TwinProduction/g8

Usage

Because the entire purpose of G8 is to NOT waste time configuring the layer of security, the primary emphasis is to keep it as simple as possible.

Simple

Just want a simple layer of security without the need for advanced permissions? This configuration is what you're looking for.

gate := g8.NewGate(g8.NewAuthorizationService().WithToken("mytoken"))
router := http.NewServeMux()
router.Handle("/unprotected", yourHandler)
router.Handle("/protected", gate.Protect(yourHandler))
http.ListenAndServe(":8080", router)

The endpoint /protected is now only accessible if you pass the header Authorization: Bearer mytoken.

If you use http.HandleFunc instead of http.Handle, you may use gate.ProtectFunc(yourHandler) instead.

Advanced permissions

If you have tokens with more permissions than others, g8's permission system will make managing authorization a breeze.

Rather than registering tokens, think of it as registering clients, the only difference being that clients may be configured with permissions while tokens cannot.

gate := g8.NewGate(g8.NewAuthorizationService().WithClient(g8.NewClient("mytoken").WithPermission("admin")))
router := http.NewServeMux()
router.Handle("/unprotected", yourHandler)
router.Handle("/protected-with-admin", gate.ProtectWithPermissions(yourHandler, []string{"admin"}))
http.ListenAndServe(":8080", router)

The endpoint /protected-with-admin is now only accessible if you pass the header Authorization: Bearer mytoken, because the client with the token mytoken has the permission admin. Note that the following handler would also be accessible with that token:

router.Handle("/protected", gate.Protect(yourHandler))

To clarify, both clients and tokens have access to handlers that aren't protected with extra permissions, and essentially, tokens are registered as clients with no extra permissions in the background.

Creating a token like so:

gate := g8.NewGate(g8.NewAuthorizationService().WithToken("mytoken"))

is the equivalent of creating the following client:

gate := g8.NewGate(g8.NewAuthorizationService().WithClient(g8.NewClient("mytoken")))

With client provider

A client provider's task is to retrieve a Client from an external source (e.g. a database) when provided with a token. You should use a client provider when you have a lot of tokens and it wouldn't make sense to register all of them using AuthorizationService's WithToken/WithTokens/WithClient/WithClients.

Note that the provider is used as a fallback source. As such, if a token is explicitly registered using one of the 4 aforementioned functions, the client provider will not be used.

clientProvider := g8.NewClientProvider(func(token string) *g8.Client {
    // We'll assume that the following function calls your database and returns a struct "User" that 
    // has the user's token as well as the permissions granted to said user
    user := database.GetUserByToken(token)
    if user != nil {
        return g8.NewClient(user.Token).WithPermissions(user.Permissions)
    }
    return nil
})
gate := g8.NewGate(g8.NewAuthorizationService().WithClientProvider(clientProvider))

You can also configure the client provider to cache the output of the function you provide to retrieve clients by token:

clientProvider := g8.NewClientProvider(...).WithCache(ttl, maxSize)

Since g8 leverages TwinProduction/gocache, you can also use gocache's constants for configuring the TTL and the maximum size:

  • Setting the TTL to gocache.NoExpiration (-1) will disable the TTL.
  • Setting the maximum size to gocache.NoMaxSize (0) will disable the maximum cache size

If you're using a TTL and have a lot of tokens (100k+), you may want to use clientProvider.StartJanitor() to allow the cache to passively delete expired entries. If you have to re-initialize the client provider after the janitor has been started, make sure to stop the janitor first (clientProvider.StopJanitor()). This is because the janitor runs on a separate goroutine, thus, if you were to re-create a client provider and re-assign it, the old client provider would still exist in memory with the old cache. I'm only specifying this for completeness, because for the overwhelming majority of people, the gate will be created on application start and never modified again until the application shuts down, in which case, you don't even need to worry about stopping the janitor.

To avoid any misunderstandings, using a client provider is not mandatory. If you only have a few tokens and you can load them on application start, you can just leverage AuthorizationService's WithToken/WithTokens/WithClient/WithClients.

AuthorizationService

As the previous examples may have hinted, there are several ways to create clients. The one thing they have in common is that they all go through AuthorizationService, which is in charge of both managing clients and determining whether a request should be blocked or allowed through.

Function Description
WithToken Creates a single static client with no extra permissions
WithTokens Creates a slice of static clients with no extra permissions
WithClient Creates a single static client
WithClients Creates a slice of static clients
WithClientProvider Creates a client provider which will allow a fallback to a dynamic source (e.g. to a database) when a static client is not found

Except for WithClientProvider, every functions listed above can be called more than once. As a result, you may safely perform actions like this:

authorizationService := g8.NewAuthorizationService().
    WithToken("123").
    WithToken("456").
    WithClient(g8.NewClient("789").WithPermission("admin"))
gate := g8.NewGate(authorizationService)

Be aware that g8.Client supports a list of permissions as well. You may call WithPermission several times, or call WithPermissions with a slice of permissions instead.

Permissions

Unlike client permissions, handler permissions are requirements.

A client may have as many permissions as you want, but for said client to have access to a handler protected by permissions, the client must have all permissions defined by said handler in order to have access to it.

In other words, a client with the permissions create, read, update and delete would have access to all of these handlers:

gate := g8.NewGate(g8.NewAuthorizationService().WithClient(g8.NewClient("mytoken").WithPermissions([]string{"create", "read", "update", "delete"})))
router := http.NewServeMux()
router.Handle("/", gate.Protect(homeHandler)) // equivalent of gate.ProtectWithPermissions(homeHandler, []string{})
router.Handle("/create", gate.ProtectWithPermissions(createHandler, []string{"create"}))
router.Handle("/read", gate.ProtectWithPermissions(readHandler, []string{"read"}))
router.Handle("/update", gate.ProtectWithPermissions(updateHandler, []string{"update"}))
router.Handle("/delete", gate.ProtectWithPermissions(deleteHandler, []string{"delete"}))
router.Handle("/crud", gate.ProtectWithPermissions(crudHandler, []string{"create", "read", "update", "delete"}))

But it would not have access to the following handler, because while mytoken has the read permission, it does not have the backup permission:

router.Handle("/backup", gate.ProtectWithPermissions(&testHandler{}, []string{"read", "backup"}))
Issues
  • Implement caching in the ClientProvider

    Implement caching in the ClientProvider

    Changelog

    Fixes #1

    • New fields in ClientProvider
    • Added a WithCache function to decorate over ClientProvider
    • Modified GetClientByToken function accordingly to use cache

    Some comments: We store the ttl property in the struct because we would need it in the WithCache function. The reason I didn't add a maxSize property to the struct is because we won't use it. I kept the cache property because, it is a direct indicator of whether or not there is a need to call gocache.Get(). I don't know if gocache is good property name.

    enhancement 
    opened by ynden 5
  • Implement caching in the ClientProvider

    Implement caching in the ClientProvider

    Implementing a cache with a configurable TTL as well as a configurable maximum size in ClientProvider could be very useful to help preventing unnecessary load on whatever service or infrastructure component ClientProvider's getClientByTokenFunc function is calling.

    The implementation:

    • Must be able to have a maximum cache size (e.g. 10000)
    • Must be able to have a configurable TTL (e.g. 1 hour)
    • Must be able to cache invalid tokens with a shorter entry TTL (e.g. 30 minutes)

    Being the self-promoting person that I am, I think TwinProduction/gocache would fit this role perfectly.

    enhancement good first issue 
    opened by TwinProduction 1
  • updates to main.go

    updates to main.go

    opened by Zynx64 1
  • Add support for rate limiting

    Add support for rate limiting

    It would be nice to have a configuration for rate limiting on Gate.

    Something like this:

    gate := g8.NewGate(nil).WithRateLimit(100)
    

    where 100 is the maximum number of requests per second.

    enhancement good first issue 
    opened by TwinProduction 0
Releases(v1.0.0)
Owner
Chris C.
I love programming
Chris C.
This package provides json web token (jwt) middleware for goLang http servers

jwt-auth jwt auth middleware in goLang. If you're interested in using sessions, checkout my sessions library! README Contents: Quickstart Performance

Adam Hanna 204 Jul 23, 2021
A fast and simple JWT implementation for Go

JWT Fast and simple JWT implementation written in Go. This package was designed with security, performance and simplicity in mind, it protects your to

Gerasimos (Makis) Maropoulos 102 Jul 22, 2021
The easiest JWT library to GO

JWT Go The easiest JWT Library that could be a starting point for your project. Installation go get github.com/supanadit/jwt-go Quick Start package ma

Supan Adit Pratama 16 Apr 21, 2021
⛩️ Go library for protecting HTTP handlers with authorization bearer token.

G8, pronounced Gate, is a simple Go library for protecting HTTP handlers with tokens. Tired of constantly re-implementing a security layer for each

Chris C. 28 Jun 17, 2021
Go login handlers for authentication providers (OAuth1, OAuth2)

gologin Package gologin provides chainable login http.Handler's for Google, Github, Twitter, Facebook, Bitbucket, Tumblr, or any OAuth1 or OAuth2 auth

Dalton Hubble 1.4k Jul 23, 2021
Golang implementation of JSON Web Tokens (JWT)

jwt-go A go (or 'golang' for search engine friendliness) implementation of JSON Web Tokens NEW VERSION COMING: There have been a lot of improvements s

Dave Grijalva 9.8k Jul 25, 2021
A go implementation of JSON Web Tokens

jwt-go A go (or 'golang' for search engine friendliness) implementation of JSON Web Tokens NEW VERSION COMING: There have been a lot of improvements s

null 462 Aug 2, 2021
JSON Web Token library

About … a JSON Web Token (JWT) library for the Go programming language. Feature complete Full test coverage Dependency free Key management The API enf

Pascal S. de Kloe 259 Jul 30, 2021
an SSO and OAuth / OIDC login solution for Nginx using the auth_request module

Vouch Proxy An SSO solution for Nginx using the auth_request module. Vouch Proxy can protect all of your websites at once. Vouch Proxy supports many O

Vouch 1.4k Jul 31, 2021
JWT login microservice with plugable backends such as OAuth2, Google, Github, htpasswd, osiam, ..

loginsrv loginsrv is a standalone minimalistic login server providing a JWT login for multiple login backends. ** Attention: Update to v1.3.0 for Goog

tarent 1.8k Jul 23, 2021
The boss of http auth.

Authboss Authboss is a modular authentication system for the web. It has several modules that represent authentication and authorization features that

Volatile Technologies Inc. 2.7k Jul 24, 2021
HTTP Session Management for Go

SCS: HTTP Session Management for Go Features Automatic loading and saving of session data via middleware. Choice of server-side session stores includi

Alex Edwards 901 Jul 22, 2021
Platform-Agnostic Security Tokens implementation in GO (Golang)

Golang implementation of PASETO: Platform-Agnostic Security Tokens This is a 100% compatible pure Go (Golang) implementation of PASETO tokens. PASETO

Oleg Lobanov 524 Jul 18, 2021
:closed_lock_with_key: Middleware for keeping track of users, login states and permissions

Permissions2 Middleware for keeping track of users, login states and permissions. Online API Documentation godoc.org Features and limitations Uses sec

Alexander F. Rødseth 432 Jul 9, 2021