Platform-Agnostic Security Tokens implementation in GO (Golang)

Overview

Golang implementation of PASETO: Platform-Agnostic Security Tokens

License GoDoc Build Status Coverage Status Go Report Card

This is a 100% compatible pure Go (Golang) implementation of PASETO tokens.

PASETO is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.

Contents

What is PASETO?

PASETO (Platform-Agnostic SEcurity TOkens) is a specification and reference implementation for secure stateless tokens.

Key Differences between PASETO and JWT

Unlike JSON Web Tokens (JWT), which gives developers more than enough rope with which to hang themselves, PASETO only allows secure operations. JWT gives you "algorithm agility", PASETO gives you "versioned protocols". It's incredibly unlikely that you'll be able to use PASETO in an insecure way.

Caution: Neither JWT nor PASETO were designed for stateless session management. PASETO is suitable for tamper-proof cookies, but cannot prevent replay attacks by itself.

PASETO

PASETO Example 1

v2.local.QAxIpVe-ECVNI1z4xQbm_qQYomyT3h8FtV8bxkz8pBJWkT8f7HtlOpbroPDEZUKop_vaglyp76CzYy375cHmKCW8e1CCkV0Lflu4GTDyXMqQdpZMM1E6OaoQW27gaRSvWBrR3IgbFIa0AkuUFw.UGFyYWdvbiBJbml0aWF0aXZlIEVudGVycHJpc2Vz

This decodes to:

  • Version: v2
  • Purpose: local (shared-key authenticated encryption)
  • Payload (hex-encoded):
    400c48a557be10254d235cf8c506e6fea418a26c93de1f05b55f1bc64cfca412
    56913f1fec7b653a96eba0f0c46542a8a7fbda825ca9efa0b3632dfbe5c1e628
    25bc7b5082915d0b7e5bb81930f25cca9076964c33513a39aa105b6ee06914af
    581ad1dc881b1486b4024b9417
    
    • Nonce: 400c48a557be10254d235cf8c506e6fea418a26c93de1f05
    • Authentication tag: 6914af581ad1dc881b1486b4024b9417
  • Decrypted Payload:
    {
      "data": "this is a signed message",
      "exp": "2039-01-01T00:00:00+00:00"
    }
    • Key used in this example (hex-encoded):
      707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f  
      
  • Footer:
    Paragon Initiative Enterprises
    

PASETO Example 2

v2.public.eyJleHAiOiIyMDM5LTAxLTAxVDAwOjAwOjAwKzAwOjAwIiwiZGF0YSI6InRoaXMgaXMgYSBzaWduZWQgbWVzc2FnZSJ91gC7-jCWsN3mv4uJaZxZp0btLJgcyVwL-svJD7f4IHyGteKe3HTLjHYTGHI1MtCqJ-ESDLNoE7otkIzamFskCA

This decodes to:

  • Version: v2
  • Purpose: public (public-key digital signature)
  • Payload:
    {
      "data": "this is a signed message",
      "exp": "2039-01-01T00:00:00+00:00"
    }
  • Signature (hex-encoded):
    d600bbfa3096b0dde6bf8b89699c59a746ed2c981cc95c0bfacbc90fb7f8207c
    86b5e29edc74cb8c761318723532d0aa27e1120cb36813ba2d908cda985b2408
    
  • Public key (hex-encoded):
    11324397f535562178d53ff538e49d5a162242970556b4edd950c87c7d86648a
    

To learn what each version means, please see this page in the documentation.

JWT

An example JWT (taken from JWT.io) might look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

This decodes to:

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Body:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Signature:

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

Motivation

As you can see, with JWT, you get to specify an alg header. There are a lot of options to choose from (including none).

There have been ways to exploit JWT libraries by replacing RS256 with HS256 and using the known public key as the HMAC-SHA256 key, thereby allowing arbitrary token forgery.

With PASETO, your options are version and a purpose. There are two possible values for purpose:

  • local -- shared-key encryption (symmetric-key, AEAD)
  • public -- public-key digital signatures (asymmetric-key)

PASETO only allows you to use authenticated modes.

Regardless of the purpose selected, the header (and an optional footer, which is always cleartext but base64url-encoded) is included in the signature or authentication tag.

Installation

To install the library use the following command:

$ go get -u github.com/o1egl/paseto

Usage

This library contains a predefined JsonToken struct for using as payload, but you are free to use any data types and structs you want.

During the encoding process, a payload of type string and []byte is used without transformation. For other data types, the library encodes the payload to JSON.

Create token using symmetric key (local mode):

symmetricKey := []byte("YELLOW SUBMARINE, BLACK WIZARDRY") // Must be 32 bytes
now := time.Now()
exp := now.Add(24 * time.Hour)
nbt := now

jsonToken := paseto.JSONToken{
        Audience:   "test",
        Issuer:     "test_service",
        Jti:        "123",
        Subject:    "test_subject",
        IssuedAt:   now,
        Expiration: exp,
        NotBefore:  nbt,
        }
// Add custom claim    to the token    
jsonToken.Set("data", "this is a signed message")
footer := "some footer"

// Encrypt data
token, err := paseto.Encrypt(symmetricKey, jsonToken, footer)
// token = "v2.local.E42A2iMY9SaZVzt-WkCi45_aebky4vbSUJsfG45OcanamwXwieieMjSjUkgsyZzlbYt82miN1xD-X0zEIhLK_RhWUPLZc9nC0shmkkkHS5Exj2zTpdNWhrC5KJRyUrI0cupc5qrctuREFLAvdCgwZBjh1QSgBX74V631fzl1IErGBgnt2LV1aij5W3hw9cXv4gtm_jSwsfee9HZcCE0sgUgAvklJCDO__8v_fTY7i_Regp5ZPa7h0X0m3yf0n4OXY9PRplunUpD9uEsXJ_MTF5gSFR3qE29eCHbJtRt0FFl81x-GCsQ9H9701TzEjGehCC6Bhw.c29tZSBmb290ZXI"

// Decrypt data
var newJsonToken paseto.JSONToken
var newFooter string
err := paseto.Decrypt(token, symmetricKey, &newJsonToken, &newFooter)

Create token using asymetric key (public mode):

b, _ := hex.DecodeString("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
privateKey := ed25519.PrivateKey(b)

b, _ = hex.DecodeString("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
publicKey := ed25519.PublicKey(b)

// or create a new keypair 
// publicKey, privateKey, err := ed25519.GenerateKey(nil)

jsonToken := paseto.JSONToken{
        Expiration: time.Now().Add(24 * time.Hour),
        }
        
// Add custom claim    to the token    
jsonToken.Set("data", "this is a signed message")
footer := "some footer"

// Sign data
token, err := paseto.Sign(privateKey, jsonToken, footer)
// token = "v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOC0wMy0xMlQxOTowODo1NCswMTowMCJ9Ojv0uXlUNXSFhR88KXb568LheLRdeGy2oILR3uyOM_-b7r7i_fX8aljFYUiF-MRr5IRHMBcWPtM0fmn9SOd6Aw.c29tZSBmb290ZXI"

// Verify data
var newJsonToken paseto.JSONToken
var newFooter string
err := paseto.Verify(token, publicKey, &newJsonToken, &newFooter)

Use Parse() function to parse all supported token versions:

IMPORTANT: Version 1 of the protocol is deprecated

b, err := hex.DecodeString("2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b43415145417878636e47724e4f6136426c41523458707050640d0a746146576946386f7279746c4b534d6a66446831314c687956627a4335416967556b706a457274394d7649482f46384d444a72324f39486b36594b454b574b6f0d0a72333566364b6853303679357a714f722b7a4e34312b39626a52365633322b527345776d5a737a3038375258764e41334e687242633264593647736e57336c5a0d0a34356f5341564a755639553667335a334a574138355972362b6350776134793755632f56726f6d7a674679627355656e33476f724254626a783142384f514a440d0a73652f4b6b6855433655693358384264514f473974523455454775742f6c39703970732b3661474d4c57694357495a54615456784d4f75653133596b777038740d0a3148467635747a6872493055635948687638464a6b315a6435386759464158634e797975737834346e6a6152594b595948646e6b4f6a486e33416b534c4d306b0d0a6c774944415141420d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d")
block, _ := pem.Decode(b)
rsaPubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
v1PublicKey := rsaPubInterface.(*rsa.PublicKey)

b, _ = hex.DecodeString("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
v2PublicKey := ed25519.PublicKey(b)


var payload JSONToken
var footer string
version, err := paseto.Parse(token, &payload, &footer, symmetricKey, map[paseto.Version]crypto.PublicKey{paseto.V1: v1PublicKey, paseto.V2: v2PublicKey})

For more information see *_test.go files.

Benchmarks

MacBook Pro (15-inch, 2018) CPU: 2,6 GHz Intel Core i7 RAM: 32 GB 2400 MHz DDR4 OS: macOS 10.14.6 GO: 1.13.7

$ go test -bench . -benchmem

Benchmark_V2_JSONToken_Encrypt-12         137578              8532 ns/op            4186 B/op         59 allocs/op
Benchmark_V2_JSONToken_Decrypt-12         139309              7970 ns/op            2048 B/op         63 allocs/op
Benchmark_V2_JSONToken_Sign-12             21598             55817 ns/op            4426 B/op         60 allocs/op
Benchmark_V2_JSONToken_Verify-12            8772            132142 ns/op            2528 B/op         64 allocs/op
Benchmark_V2_String_Encrypt-12            544958              2051 ns/op            1176 B/op         23 allocs/op
Benchmark_V2_String_Decrypt-12           1000000              1054 ns/op             568 B/op         18 allocs/op
Benchmark_V2_String_Sign-12                25144             47645 ns/op            1144 B/op         23 allocs/op
Benchmark_V2_String_Verify-12               9408            125524 ns/op             744 B/op         18 allocs/op

Supported PASETO Versions

Version 2

Version 2 (the recommended version by the specification) is fully supported.

Version 1

Version 1 (the compatibility version) is fully supported.

Issues
  • example on readme fails, can't unmarshal token data to the given type of value.

    example on readme fails, can't unmarshal token data to the given type of value.

    When running the example in the README, the local mode decryption step fails with error "can't unmarshal token data to the given type of value". Right now the library seems unusable.

    opened by liminalitythree 4
  • Changes for 1.13

    Changes for 1.13

    Removed dependency on github.com/pkg/errors. Updated golang.org/x/crypto/ed25519 to crypto/ed25519. Updated test files.

    These are api breaking changes, error checking and ed25519 code dependent on this version of paseto will need to be updated.

    opened by sharonjl 3
  • Should decryption and verification operations mandate that footer matches an expected value?

    Should decryption and verification operations mandate that footer matches an expected value?

    In the Paseto documentation, there are two conflicting stances on how one should use the token's footer:

    In the former case, the receiver knows and will tolerate only one footer. In the latter case, the receiver only knows the schema of the footer, but can't know its value; rather, the receiver needs to read the value to know how to proceed.

    This library doesn't take either approach to heart: neither the decryption nor verification functions accept an expected footer value to match, nor is there a means to extract a footer first to guide the rest of the decryption or verification process.

    What is the author's take on the role of the token footer?

    opened by seh 2
  • Can't extend JSONToken

    Can't extend JSONToken

    I want to add my own claim to the JSONToken. I tried to use

    type MyToken struct {
    	paseto.JSONToken
    	Groups []string `json:"groups"`
    }
    

    but it doesn't work (field does not appear in my token). There is JSONToken.Set, but it only works with strings.

    opened by ikorolev93 2
  • how to get version 2.0.0

    how to get version 2.0.0

    Hi, try to install via command go get -u github.com/o1egl/paseto, but I can only get version 1.0.0, using go get -u github.com/o1egl/[email protected], it said invalid version: unknown revision v2.0.0. Am I the only one who run into this? Thanks.

    opened by allanwakes 1
  • Feature: custom error type ErrTokenExpiredError

    Feature: custom error type ErrTokenExpiredError

    This changes the token_validator to return a ErrTokenExpiredError instead of ErrTokenValidationError. This allows us to check for token expiration which differs from a regular token error. (e.g. trigger a refresh).

    opened by hazcod 1
  • Bind Keys to Version and Purpose

    Bind Keys to Version and Purpose

    https://github.com/o1egl/paseto/blob/f1000e3be0ce1d221c08cebbe13e184414a092f6/v2.go#L78

    https://github.com/o1egl/paseto/blob/f1000e3be0ce1d221c08cebbe13e184414a092f6/v2.go#L138

    See https://github.com/paseto-standard/paseto-spec/blob/master/docs/02-Implementation-Guide/03-Algorithm-Lucidity.md

    Right now, byte arrays are accepted by this API. There's no mechanism to prevent a user from using a v2 public key as a v2 local key.

    opened by paragonie-security 0
  • Assymetric woes

    Assymetric woes

    Symmetric/local keys work great, however...

            v2 := paseto.NewV2()
    	_, privateKey, err := ed25519.GenerateKey(nil)
    
    	jsonToken := paseto.JSONToken{
    		Expiration: time.Now().Add(24 * time.Hour),
    		Subject:    "Foo",
    		Audience:   "Bar",
    		Issuer:     "This guy",
    		IssuedAt:   time.Now(),
    	}
    
    	// Add custom claim    to the token
    	jsonToken.Set("data", "this is a signed message")
    	footer := "some footer"
    
    	// Sign data
    	token, err := v2.Sign(privateKey, &jsonToken, &footer)
    

    Gives a err of incorrect private key type. What am I doing wrong here?

    opened by delaneyj 3
Releases(v2.0.0)
  • v2.0.0(Jan 18, 2020)

    • Added ability to store/load values of any type as claim in JSONToken
    • New top level Encrypt(), Decrypt(), Sign() and Verify() functions that use V2 as default protocol
    Source code(tar.gz)
    Source code(zip)
Owner
Oleg Lobanov
Oleg Lobanov
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.8k Sep 14, 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 941 Sep 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.9k Sep 24, 2021
The Single Sign-On Multi-Factor portal for web apps

Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for your applications

Authelia 9.4k Sep 22, 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 108 Sep 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 Aug 6, 2021
A standalone, specification-compliant, OAuth2 server written in Golang.

Go OAuth2 Server This service implements OAuth 2.0 specification. Excerpts from the specification are included in this README file to describe differe

Richard Knop 1.8k Sep 16, 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 264 Sep 12, 2021
vault-plugin-auth-usertotp is an auth method plugin for HashiCorp Vault

vault-plugin-auth-usertotp is an auth method plugin for HashiCorp Vault. Create user accounts, add TOTP tokens (user supplied pin + totp), and have peace of mind using 2FA.

Mike McRill 6 Jul 5, 2021
A collection of authentication Go packages related to OIDC, JWKs and Distributed Claims.

cap (collection of authentication packages) provides a collection of related packages which enable support for OIDC, JWT Verification and Distributed Claims.

HashiCorp 297 Sep 19, 2021
ZITADEL - Identity Experience Platform

What Is ZITADEL ZITADEL is a "Cloud Native Identity and Access Management" solution built for the cloud era. ZITADEL uses a modern software stack cons

CAOS 216 Sep 19, 2021
🍍Jeff provides the simplest way to manage web sessions in Go.

jeff A tool for managing login sessions in Go. Motivation I was looking for a simple session management wrapper for Go and from what I could tell ther

Alan Braithwaite 231 Sep 16, 2021
Small Lambda function which performs a Aws:Sts:AssumeRole based on the presented JWT-Token

About This implements a AWS Lambda handler which takes a JWT-Token, validates it and then performs a Aws:Sts:AssumeRole based on preconfigured rules.

AOE 4 Aug 25, 2021
Certificate authority and access plane for SSH, Kubernetes, web applications, and databases

Teleport is an identity-aware, multi-protocol access proxy which understands SSH, HTTPS, Kubernetes API, MySQL and PostgreSQL wire protocols.

Teleport 10k Sep 15, 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.5k Sep 21, 2021
GLAuth 1.1k Sep 25, 2021
Fast, secure and efficient secure cookie encoder/decoder

Encode and Decode secure cookies This package provides functions to encode and decode secure cookie values. A secure cookie has its value ciphered and

Christophe Meessen 53 Sep 2, 2021
sso, aka S.S.Octopus, aka octoboi, is a single sign-on solution for securing internal services

sso See our launch blog post for more information! Please take the SSO Community Survey to let us know how we're doing, and to help us plan our roadma

BuzzFeed 2.8k Sep 19, 2021
Time-Based One-Time Password (TOTP) and HMAC-Based One-Time Password (HOTP) library for Go.

otpgo HMAC-Based and Time-Based One-Time Password (HOTP and TOTP) library for Go. Implements RFC 4226 and RFC 6238. Contents Supported Operations Read

Jose Torres 23 Aug 27, 2021