⛩️ 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
  • Support using cookie as a form of identification

    Support using cookie as a form of identification

    Rather than only supporting the Authorization header as a way to identify a user, it would be nice if we could use the client provider to retrieve the user through a session cookie.

    enhancement 
    opened by TwiN 1
  • 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 TwiN 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 TwiN 0
  • feat: ClientProvider.cache as an interface to support external cache (e.g. redis)

    feat: ClientProvider.cache as an interface to support external cache (e.g. redis)

    Describe the feature request

    At present it looks like the client provider cache field is tightly coupled to the gocache. If a user wants to also use an external/shared/distributed cache then this isn't possible. It would be nice to allow more flexibility to the user in which caching solution they wish to use.

    Why do you personally want this feature to be implemented?

    to allow flexibility in choice of cache solution.

    How long have you been using this project?

    No response

    Additional information

    No response

    enhancement 
    opened by c-nv-s 0
Releases(v1.3.0)
Owner
Chris C.
I love programming
Chris C.
Authorization and authentication. Learning go by writing a simple authentication and authorization service.

Authorization and authentication. Learning go by writing a simple authentication and authorization service.

Dinesh Bhattarai 0 Aug 5, 2022
Backend Development Rest Api Project for book management system. Used Features like redis, jwt token,validation and authorization.

Golang-restapi-project Simple Rest Api Project with Authentication, Autherization,Validation and Connection with redis File Structure ├── cache │ ├──

Srijan Chakraborty 1 May 25, 2022
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.5k Aug 4, 2022
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 218 Jul 29, 2022
An authorization library that supports access control models like ACL, RBAC, ABAC in Golang

Casbin News: still worry about how to write the correct Casbin policy? Casbin online editor is coming to help! Try it at: https://casbin.org/editor/ C

Casbin 12.5k Aug 4, 2022
An authorization library that supports access control models like ACL, RBAC, ABAC in Golang

Casbin News: still worry about how to write the correct Casbin policy? Casbin online editor is coming to help! Try it at: https://casbin.org/editor/ C

Casbin 12.5k Aug 5, 2022
Go library providing in-memory implementation of an OAuth2 Authorization Server / OpenID Provider

dispans Go library providing in-memory implementation of an OAuth2 Authorization Server / OpenID Provider. The name comes from the Swedish word dispen

Xenit AB 3 Dec 22, 2021
A library for Go client applications that need to perform OAuth authorization against a server

oauth-0.8.0.zip oauth A library for Go client applications that need to perform OAuth authorization against a server, typically GitHub.com. Traditiona

tigressma 1 Oct 13, 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 287 Jul 27, 2022
ACL, RBAC, ABAC authorization middleware for KubeSphere

casbin-kubesphere-auth Casbin-kubesphere-auth is a plugin which apply several security authentication check on kubesphere via casbin. This plugin supp

Casbin 4 Jun 9, 2022
an stateless OpenID Connect authorization server that mints ID Tokens from Webauthn challenges

Webauthn-oidc Webauthn-oidc is a very minimal OIDC authorization server that only supports webauthn for authentication. This can be used to bootstrap

Arian van Putten 13 May 16, 2022
policy - the CLI for managing authorization policies

policy - the CLI for managing authorization policies The policy CLI is a tool for building, versioning and publishing your authorization policies. It

Open Policy Registry 87 Aug 3, 2022
Authorization As A Service

a3s NOTE: this is a work in progress and this software is not usable yet a3s (stands for Auth As A Service) is an authentication and ABAC authorizatio

Palo Alto Networks 4 Feb 11, 2022
telegram authorization in telegram without using a widget

TGAH - telegram Authorization Example of authorization in telegram without using a widget Installation go get -d github.com/tioffs/[email protected] Setti

tioffs 2 Jun 6, 2022
A demo of authentication and authorization using jwt

Nogopy Hi, this a demo of how to use jwt for authentication in microservices Keep in mind that this is a demo of how to authenticate using jwt, we don

null 2 Nov 1, 2021
Mini-framework for multiple authentication and authorization schemes

Go authorization pattern This repository demonstrates an authorization pattern that allows multiple schemes. Demo To start the demo run the following

Tim van Osch 0 Dec 30, 2021
Example of a simple application which is powered by a third-party oAuth 2.0 server for it's authentication / authorization. Written in Golang.

go mod init github.com/bartmika/osin-thirdparty-example go get github.com/spf13/cobra go get github.com/openshift/osin go get github.com/openshift/osi

Bartlomiej Mika 0 Jan 4, 2022
Authelia: an open-source authentication and authorization server providing two-factor authentication

Authelia is an open-source authentication and authorization server providing two

Streato 0 Jan 5, 2022
🔑 Authz0 is an automated authorization test tool. Unauthorized access can be identified based on URL and Role.

Authz0 is an automated authorization test tool. Unauthorized access can be identified based on URL and Role. URLs and Roles are managed as YAML-based

HAHWUL 288 Jul 16, 2022