aaa

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 7, 2021 License: MIT Imports: 8 Imported by: 0

README

aaa GoDoc Build Status Coverage Status Go Report Card

AAA - Authentication, Authorization & Accounting

Motivation

The package provides AAA features for http router github.com/axkit/vatel using JWT. AAA plays a proxy between users/roles/permission storage and vatel.

Concepts

  • JTW is used.
  • AAA is independent of users, roles and permissions storage structure.
  • AAA does not know password encryption approach.
  • Developer can extend token payload.

Endpoints

Sign in

POST /auth/sign-in Input

    {
        "login" : "user1"
        "password" : "plain-or-encrypted-password",
    }

Output Successfull: HTTP 200

{
    "access_token" : "abc.."
    "refresh_token": "zyx..."
    "permissions" : ["PermissionCode1","PermissionCode2", "PermissionCode3"...]
}

Access token holds following user specified payload inside:

{
  "user": 42,
  "login": "user1",
  "role": 1,
  "perms": "ZTY1ZmZmN2YyZmVlYzNlZmJjN2RmZmJmZGNmM2Y3ZmYzZjlmZmRmZmZmN2Y3NWJkMDE="
}

Refresh token holds user specified payload inside:

{
  "user": 42
}

Failed: HTTP 401

{
    "message" : "invalid cridentials"
}
Access token validation

POST /auth/is-token-valid Output Successfull: HTTP 200

{
   "result" : "ok"
}

Failed: HTTP 401

{
    "message" : "invalid token"
}
Refresh token

POST /auth/refresh-token Input

    {
        "refresh_token" : "xyz.."
    }

Output Successfull: HTTP 200

{
    "access_token" : "abc.."
    "refresh_token": "zyx..."
    "permissions" : ["PermissionCode1","PermissionCode2", "PermissionCode3"...]
}

Failed: HTTP 401

{
    "message" : "invalid token"
}
  • Application functionality can be limited by using permissions.
  • Permission (access right) represented by unique string code.
  • Application can have many permissions.
  • A user has a role.
  • A role is set of allowed permission, it's subset of all permissions supported by application.
  • As a result of succesfull sign in backend provides access and resresh tokens.
  • Payload of access token shall have list of allowed permissions.
  • A single permission code looks like "Customers.Create", "Customer.AttachDocuments", "Customer.Edit", etc.
  • Store allowed permission codes could increase token size.
  • Bitset comes here.
  • Every permission shall be accociated with a single bit in the set.
  • Bitset adds to the token as hexadecimal string.

Usage Examples

Sign In

    var perms bitset.Bitset
    perms.Set(1)                    // 0000_0010
    perms.Set(2)                    // 0000_0110
    perms.Set(8, 10)                // 0000_0110 0000_0101
    tokenPerms := perms.String()    // returns "0605"

Check allowed permission in auth middleware

    ...
    tokenPerms := accessToken.Payload.Perms     // "0605
    bs := bitset.Parse(tokenPerms)              // returns 0000_0110 0000_0101
    if bs.AreSet(2,8) {
        // the permission allowed
    }

Further Improvements

  • Finalize integration BitSet with database/sql
  • Add benchmarks
  • Reduce memory allocations

Prague 2020

curl examples

curl 127.0.0.1:8083/api/auth/sign-in -d '{"login" : "testadmin", "password":"test"}'

{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlbGVwaGFudHNvZnQiLCJzdWIiOiJ0YXVkaXQiLCJhdWQiOiJodHRwczovL2VsZXBoYW50c29mdC5ydSIsImV4cCI6MTU4NjI0ODQ3MywiaWF0IjoxNTg2MjQ2NjczLCJqdGkiOiJ0ZXN0IiwidXNlcl9pZCI6MTEsImxvZ2luIjoidGVzdGFkbWluIiwicm9sZV9pZCI6MSwicGVybV9iaXRzZXQiOjcsImV4dHJhIjpudWxsfQ.1u_UbBAPHIg819JqJjzDHKsaW2wZBMVcEYjt92FRRWw","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlbGVwaGFudHNvZnQiLCJzdWIiOiJ0YXVkaXQiLCJhdWQiOiJodHRwczovL2VsZXBoYW50c29mdC5ydSIsImV4cCI6MTU4ODgzODY3MywiaWF0IjoxNTg2MjQ2NjczLCJqdGkiOiJ0ZXN0IiwidXNlcl9pZCI6MTF9.x7383jbhlhk2VhABF8YfgjUY3SNp5_GFqA3lcctupjs","allowed_permissions":{"TestCreateEntity","TestDeleteEntity","TestUpdateEntity"}}

curl -X POST 127.0.0.1:8083/api/auth/is-token-valid -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlbGVwaGFudHNvZnQiLCJzdWIiOiJ0YXVkaXQiLCJhdWQiOiJodHRwczovL2VsZXBoYW50c29mdC5ydSIsImV4cCI6MTU4NjI0ODQ3MywiaWF0IjoxNTg2MjQ2NjczLCJqdGkiOiJ0ZXN0IiwidXNlcl9pZCI6MTEsImxvZ2luIjoidGVzdGFkbWluIiwicm9sZV9pZCI6MSwicGVybV9iaXRzZXQiOjcsImV4dHJhIjpudWxsfQ.1u_UbBAPHIg819JqJjzDHKsaW2wZBMVcEYjt92FRRWw"

{"result" : "ok"}

curl 127.0.0.1:8083/api/auth/refresh-token -d '{"refresh_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlbGVwaGFudHNvZnQiLCJzdWIiOiJ0YXVkaXQiLCJhdWQiOiJodHRwczovL2VsZXBoYW50c29mdC5ydSIsImV4cCI6MTU4ODgzODY3MywiaWF0IjoxNTg2MjQ2NjczLCJqdGkiOiJ0ZXN0IiwidXNlcl9pZCI6MTF9.x7383jbhlhk2VhABF8YfgjUY3SNp5_GFqA3lcctupjs"}'

{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlbGVwaGFudHNvZnQiLCJzdWIiOiJ0YXVkaXQiLCJhdWQiOiJodHRwczovL2VsZXBoYW50c29mdC5ydSIsImV4cCI6MTU4NjI0ODY1NiwiaWF0IjoxNTg2MjQ2ODU2LCJqdGkiOiJ0ZXN0IiwidXNlcl9pZCI6MTEsImxvZ2luIjoidGVzdGFkbWluIiwicm9sZV9pZCI6MSwicGVybV9iaXRzZXQiOjcsImV4dHJhIjpudWxsfQ.3wD4cfhOFFu_ZTV1jgPz_PcMPvt4MVoHLacUW2QCxG4","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJlbGVwaGFudHNvZnQiLCJzdWIiOiJ0YXVkaXQiLCJhdWQiOiJodHRwczovL2VsZXBoYW50c29mdC5ydSIsImV4cCI6MTU4ODgzODg1NiwiaWF0IjoxNTg2MjQ2ODU2LCJqdGkiOiJ0ZXN0IiwidXNlcl9pZCI6MTF9.wIh1VpkmDKkGFAY5c0IMO0SC3TVwXNsl1NufNzdITUI","allowed_permissions":["TestCreateEntity","TestDeleteEntity", "TestUpdateEntity"]}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// EPSignIn holds endpoint path to sign in.
	EPSignIn = "/auth/sign-in"

	// EPIsTokenValid holds endpoint path to is token valid.
	EPIsTokenValid = "/auth/is-token-valid"

	// EPRefreshToken holds endpoint path to refresh token.
	EPRefreshToken = "/auth/refresh-token"
)
View Source
var DefaultConfig = Config{
	AccessTokenDuration:       time.Minute * 30,
	RefreshTokenDuration:      time.Hour * 24 * 30,
	IsRefreshNotBeforeEnabled: false,
	Issuer:                    "",
	Subject:                   "",
	Audience:                  []string{""},
	EncryptionKey:             "default",
}

DefaultConfig holds default JWT configuration.

Functions

This section is empty.

Types

type AAA

type AAA interface {

	// SignIn предоставляет метод для аутентификации пользователя.
	SignIn(login, password string) (*TokenSet, error)

	// ForceSignIn генерирует JWT токены для пользователя.
	// может использоваться для принудительной аутентификации пользователя, при
	// переходе по ссылки из письма активации адреса email.
	ForceSignIn(Userer) (*TokenSet, error)

	// RefreshToken принимает токен в виде base64 строки, проверяет на валидность,
	// обновляет и возвращает новый токен.
	RefreshToken(encodedToken []byte) (*TokenSet, error)

	SetExtraAssigner(func(userID int) map[string]interface{})
}

type ApplicationPayload

type ApplicationPayload struct {
	UserID           int                    `json:"user"`
	UserLogin        string                 `json:"login"`
	RoleID           int                    `json:"role"`
	PermissionBitSet json.RawMessage        `json:"perms,omitempty"`
	IsDebug          bool                   `json:"debug,omitempty"`
	ExtraPayload     map[string]interface{} `json:"extra,omitempty"`
}

ApplicationPayload defines attributes what will be injected into JWT access token.

func (*ApplicationPayload) Debug added in v0.1.0

func (t *ApplicationPayload) Debug() bool

func (*ApplicationPayload) Extra

func (t *ApplicationPayload) Extra() interface{}

func (*ApplicationPayload) Login

func (t *ApplicationPayload) Login() string

func (*ApplicationPayload) Perms

func (t *ApplicationPayload) Perms() []byte

func (*ApplicationPayload) Role

func (t *ApplicationPayload) Role() int

func (*ApplicationPayload) User

func (t *ApplicationPayload) User() int

type BasicAAA

type BasicAAA struct {
	// contains filtered or unexported fields
}

BasicAAA holds data required for implementation AAA interface and axkit/vatel interfaces Authorizer, TokenDecoder.

func New

func New(cfg Config, u UserStorer, r RoleStorer) *BasicAAA

New returns default implementation of AAA based on JWT.

func (*BasicAAA) Decode

func (s *BasicAAA) Decode(encodedToken []byte) (vatel.Tokener, error)

Decode

func (*BasicAAA) Endpoints

func (a *BasicAAA) Endpoints() []vatel.Endpoint

Endpoints returnts lists of endpoints serving by BasicAAA.

func (*BasicAAA) GenerateToken

func (a *BasicAAA) GenerateToken(u Userer) (*TokenSet, error)

GenerateToken generates JWT token without credentials.

func (*BasicAAA) Init

func (a *BasicAAA) Init(ctx context.Context) error

Init ...

func (*BasicAAA) IsAllowed

func (a *BasicAAA) IsAllowed(requestPerms []byte, bitpos ...uint) (bool, error)

IsAllowed implements interface axkit/vatel Autorizer. Method receives perms from JTW token and endpointPemrs. Return true if all endpointPerms are inside requestPerms.

func (*BasicAAA) Refresh

func (a *BasicAAA) Refresh(encodedToken []byte) (*TokenSet, error)

Refresh refreshes JWT token.

func (*BasicAAA) SetExtraAssigner

func (a *BasicAAA) SetExtraAssigner(f func(userID int) (map[string]interface{}, error))

SetExtraAssigner receives a funcion what will be called in /sign-in and /refresh-token endpoints. Data returned by the function will be assigned to JWT payload attribute "app->extra".

func (*BasicAAA) SignIn

func (a *BasicAAA) SignIn(login, password string) (*TokenSet, error)

SignIn implements sign in logic. In case of succesfull result returns

func (*BasicAAA) Start

func (a *BasicAAA) Start(ctx context.Context) error

Start ...

type Config

type Config struct {
	AccessTokenDuration       time.Duration
	RefreshTokenDuration      time.Duration
	IsRefreshNotBeforeEnabled bool
	Issuer                    string
	Subject                   string
	Audience                  []string
	EncryptionKey             string
}

Config describes JWT configuration.

type IsTokenValidController

type IsTokenValidController struct {
	// contains filtered or unexported fields
}

IsTokenValidController implements /is-token-valid HTTP endpoint.

func (*IsTokenValidController) Handle

func (c *IsTokenValidController) Handle(ctx vatel.Context) error

Handle implements github.com/axkit/vatel Handler interface.

func (*IsTokenValidController) Result

func (c *IsTokenValidController) Result() interface{}

Result implements github.com/axkit/vatel Resulter interface.

type RefreshController

type RefreshController struct {
	// contains filtered or unexported fields
}

RefreshController implements /refresh-token HTTP endpoint.

func (*RefreshController) Handle

func (a *RefreshController) Handle(ctx vatel.Context) error

Handle implements github.com/axkit/vatel Handler interface.

func (*RefreshController) Input

func (a *RefreshController) Input() interface{}

Input implements github.com/axkit/vatel Inputer interface.

func (*RefreshController) Result

func (a *RefreshController) Result() interface{}

Result implements github.com/axkit/vatel Resulter interface.

type RefreshToken

type RefreshToken struct {
	jwt.Payload
	UserID int `json:"user"`
}

type RoleStorer

type RoleStorer interface {
	IsRoleExist(roleID int) bool
	RolePermissions(roleID int) ([]string, bitset.BitSet)
}

RoleStorer is an interface what wraps methods IsRoleExist and RolePermissions.

IsRoleExist returns true if role is roleID is exists.

RolePermissions returns array of permissions and BitSet permission representation.

type SignInController

type SignInController struct {
	// contains filtered or unexported fields
}

SignInController implements sign in HTTP endpoint.

func (*SignInController) Handle

func (c *SignInController) Handle(ctx vatel.Context) error

Handle implements github.com/axkit/vatel Handler interface.

func (*SignInController) Input

func (c *SignInController) Input() interface{}

Input returns reference to incoming struct.

func (*SignInController) Result

func (c *SignInController) Result() interface{}

Result returns reference to sucessfull output.

type Token

type Token struct {
	jwt.Payload
	App ApplicationPayload `json:"app"`
}

Token implements interface axkit/vatel Tokener.

func (*Token) ApplicationPayload

func (t *Token) ApplicationPayload() vatel.TokenPayloader

func (*Token) JSON

func (t *Token) JSON() []byte

func (*Token) SystemPayload

func (t *Token) SystemPayload() map[string]interface{}

SystemPayload returns JWT system attributes related to standard.

type TokenSet

type TokenSet struct {
	Access             string   `json:"access_token"`
	Refresh            string   `json:"refresh_token,omitempty"`
	AllowedPermissions []string `json:"allowed_permissions,omitempty"`
}

TokenSet describes response on successfull sign in and refresh token requests.

type UserStorer

type UserStorer interface {
	UserByCredentials(login, password string) (Userer, error)
	UserByID(userID int) (Userer, error)
}

UserStorer is an interface what wraps metods UserByCridentials and UserByID.

UserByCredentials returns a user (object implementing interface Userer) if user with login and password is found.

UserByID returns a user (object implementing interface Userer) identified by userID.

type Userer

type Userer interface {
	UserID() int
	UserLogin() string
	UserRole() int
	UserLocked() bool
}

Userer is an interface what wraps access methods to User's attributes.

Jump to

Keyboard shortcuts

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