jwt

package module
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2020 License: Apache-2.0 Imports: 9 Imported by: 0

README

jwt package

Golang implementation of JSON Web Tokens (JWT)

GoDoc

This library is based on github.com/square/go-jose package and it's jwt subpackage (version 2 currently)

  1. Install
$ go get -u github.com/karantin2020/jwt
  1. Import as library:
import "github.com/karantin2020/jwt"

Examples

Signed JWT

type TestClaims struct {
    Scope []string `json:"scope,omitempty"`
    jwt.StandardClaims
}
key1 := []byte("1234567890123456")
cl := TestClaims{
    Scope: []string{"read:repo", "write:settings"},
    StandardClaims: jwt.StandardClaims{
        Subject: "subject",
        Issuer:  "issuer",
    },
}
got, err := jwt.NewWithClaims(cl, &jwt.SignOpt{
    Algorithm: jose.HS256,
    Key:       key1,
})
if err != nil {
    panic("error = " + err.Error())
}
fmt.Printf("got: %s\n", got)
tok, err := jwt.ParseSigned(got)
if err != nil {
    panic("parse error = " + err.Error())
}
destCl := TestClaims{}
err = tok.Claims(key1, &destCl)
if err != nil {
    panic("parse error = " + err.Error())
}
fmt.Printf("got: %#v\n", destCl)

Signed and encrypted JWT

type TestClaims struct {
    Scope []string `json:"scope,omitempty"`
    jwt.StandardClaims
}
key1 := []byte("1234567890123456")
key2 := []byte("3214567890123459")
cl := TestClaims{
    Scope: []string{"read:repo", "write:settings"},
    StandardClaims: jwt.StandardClaims{
        Subject: "subject",
        Issuer:  "issuer",
    },
}
got, err := jwt.NewWithClaims(cl, &jwt.SignOpt{
    Algorithm: jose.HS256,
    Key:       key1,
}, &jwt.EncOpt{
    ContEnc: jose.A128GCM,
    Rcpt: jose.Recipient{
        Algorithm: jose.A256GCMKW,
        Key:       key2,
    },
})
if err != nil {
    panic("error = " + err.Error())
}
fmt.Printf("got: %s\n", got)
tok, err := jwt.ParseSignedAndEncrypted(got)
if err != nil {
    panic("parse error = " + err.Error())
}
nested, err := tok.Decrypt(key2)
if err != nil {
    panic(err)
}
destCl := TestClaims{}
err = nested.Claims(key1, &destCl)
if err != nil {
    panic("parse error = " + err.Error())
}
fmt.Printf("got: %#v\n", destCl)

Documentation

Overview

Package jwt provides an implementation of the JSON Web Token standard.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrExpired = errors.New("square/go-jose/jwt: validation failed, token is expired (exp)")

ErrExpired indicates that token is used after expiry time indicated in exp claim.

View Source
var ErrInvalidAudience = errors.New("square/go-jose/jwt: validation failed, invalid audience claim (aud)")

ErrInvalidAudience indicated invalid aud claim.

View Source
var ErrInvalidClaims = errors.New("square/go-jose/jwt: expected claims to be value convertible into JSON object")

ErrInvalidClaims indicates that given claims have invalid type.

View Source
var ErrInvalidContentType = errors.New("square/go-jose/jwt: expected content type to be JWT (cty header)")

ErrInvalidContentType indicates that token requires JWT cty header.

View Source
var ErrInvalidID = errors.New("square/go-jose/jwt: validation failed, invalid ID claim (jti)")

ErrInvalidID indicates invalid jti claim.

View Source
var ErrInvalidIssuer = errors.New("square/go-jose/jwt: validation failed, invalid issuer claim (iss)")

ErrInvalidIssuer indicates invalid iss claim.

View Source
var ErrInvalidSubject = errors.New("square/go-jose/jwt: validation failed, invalid subject claim (sub)")

ErrInvalidSubject indicates invalid sub claim.

View Source
var ErrIssuedInTheFuture = errors.New("square/go-jose/jwt: validation field, token issued in the future (iat)")

ErrIssuedInTheFuture indicates that the iat field is in the future.

View Source
var ErrNilClaims = errors.New("passed nil claims")

ErrNilClaims represents nil value claims error

View Source
var ErrNilOptions = errors.New("passed nil options")

ErrNilOptions represents nil options error

View Source
var ErrNoneAlgorithm = errors.New("None algorithm is used in jwt header")

ErrNoneAlgorithm describes error if jwt has alg==none

View Source
var ErrNotValidYet = errors.New("square/go-jose/jwt: validation failed, token not valid yet (nbf)")

ErrNotValidYet indicates that token is used before time indicated in nbf claim.

View Source
var ErrUnmarshalAudience = errors.New("square/go-jose/jwt: expected string or array value to unmarshal to Audience")

ErrUnmarshalAudience indicates that aud claim could not be unmarshalled.

View Source
var ErrUnmarshalNumericDate = errors.New("square/go-jose/jwt: expected number value to unmarshal NumericDate")

ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled.

Functions

func ClientLeeway added in v0.1.3

func ClientLeeway()

ClientLeeway set leeway < 0 for client validating

func NewWithClaims

func NewWithClaims(c Claims, s *SignOpt, e ...*EncOpt) (string, error)

NewWithClaims creates builder for signed or signed-then-encrypted tokens

Example (Signed)
package main

import (
	"fmt"
	"time"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

func main() {
	type TestClaims struct {
		Scope []string `json:"scope,omitempty"`
		jwt.StandardClaims
	}
	key1 := []byte("1234567890123456")
	now := jwt.NewNumericDate(time.Now())
	after := jwt.NewNumericDate(now.Time().Add(time.Hour * 1))
	cl := TestClaims{
		Scope: []string{"read:repo", "write:settings"},
		StandardClaims: jwt.StandardClaims{
			Subject:  "subject",
			Issuer:   "issuer",
			ID:       "unknown_id_for_test",
			Audience: jwt.Audience{"one", "two"},
			IssuedAt: now,
			Expiry:   after,
		},
	}
	got, err := jwt.NewWithClaims(cl, &jwt.SignOpt{
		Algorithm: jose.HS256,
		Key:       key1,
	})
	if err != nil {
		panic("ExampleNewWithClaimsSigned() error = " + err.Error())
	}
	fmt.Printf("got: %s\n", got)
	tok, err := jwt.ParseSigned(got)
	if err != nil {
		panic("ExampleNewWithClaimsSigned() parse error = " + err.Error())
	}
	destCl := TestClaims{}
	err = tok.Claims(key1, &destCl)
	if err != nil {
		panic("ExampleNewWithClaimsSigned() parse error = " + err.Error())
	}
	fmt.Printf("got: %#v\n", destCl)
	expected := jwt.Expected{}.WithTime(now.Time())
	jwt.ServerLeeway()
	err = destCl.Validate(expected)
	if err != nil {
		panic("ExampleNewWithClaimsSigned() parse error = " + err.Error())
	}
	fmt.Printf("server validation: ok\n")
	expected = jwt.Expected{}.WithTime(after.Time().Add(time.Minute * -1))
	jwt.ClientLeeway()
	err = destCl.Validate(expected)
	if err != nil {
		panic("ExampleNewWithClaimsSigned() parse error = " + err.Error())
	}
	fmt.Printf("client validation: ok\n")
}
Output:

Example (SignedAndEncrypted)
package main

import (
	"fmt"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

func main() {
	type TestClaims struct {
		Scope []string `json:"scope,omitempty"`
		jwt.StandardClaims
	}
	key1 := []byte("1234567890123456")
	key2 := []byte("3214567890123459")
	cl := TestClaims{
		Scope: []string{"read:repo", "write:settings"},
		StandardClaims: jwt.StandardClaims{
			Subject: "subject",
			Issuer:  "issuer",
		},
	}
	got, err := jwt.NewWithClaims(cl, &jwt.SignOpt{
		Algorithm: jose.HS256,
		Key:       key1,
	}, &jwt.EncOpt{
		ContEnc: jose.A128GCM,
		Rcpt: jose.Recipient{
			Algorithm: jose.A256GCMKW,
			Key:       key2,
		},
	})
	if err != nil {
		panic("ExampleNewWithClaimsSignedAndEncrypted() error = " + err.Error())
	}
	fmt.Printf("got: %s\n", got)
	tok, err := jwt.ParseSignedAndEncrypted(got)
	if err != nil {
		panic("ExampleNewWithClaimsSignedAndEncrypted() parse error = " + err.Error())
	}
	nested, err := tok.Decrypt(key2)
	if err != nil {
		panic(err)
	}
	destCl := TestClaims{}
	err = nested.Claims(key1, &destCl)
	if err != nil {
		panic("ExampleNewWithClaimsSignedAndEncrypted() parse error = " + err.Error())
	}
	fmt.Printf("got: %#v\n", destCl)
}
Output:

func ServerLeeway added in v0.1.3

func ServerLeeway()

ServerLeeway set leeway > 0 for server validating

Types

type Audience

type Audience []string

Audience represents the recipients that the token is intended for.

func (Audience) Contains

func (s Audience) Contains(v string) bool

Contains checks whether a given string is included in the Audience

func (Audience) MarshalJSON

func (s Audience) MarshalJSON() ([]byte, error)

MarshalJSON converts audience to json representation.

func (*Audience) UnmarshalJSON

func (s *Audience) UnmarshalJSON(b []byte) error

UnmarshalJSON reads an audience from its JSON representation.

type Builder

type Builder interface {
	// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
	// into single JSON object. If you are passing private claims, make sure to set
	// struct field tags to specify the name for the JSON key to be used when
	// serializing.
	Claims(i interface{}) Builder
	// Token builds a JSONWebToken from provided data.
	Token() (*JSONWebToken, error)
	// FullSerialize serializes a token using the JWS/JWE JSON Serialization format.
	FullSerialize() (string, error)
	// CompactSerialize serializes a token using the compact serialization format.
	CompactSerialize() (string, error)
}

Builder is a utility for making JSON Web Tokens. Calls can be chained, and errors are accumulated until the final call to CompactSerialize/FullSerialize.

func Encrypted

func Encrypted(enc jose.Encrypter) Builder

Encrypted creates builder for encrypted tokens.

Example
package main

import (
	"fmt"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

var sharedEncryptionKey = []byte("itsa16bytesecret")

func main() {
	enc, err := jose.NewEncrypter(
		jose.A128GCM,
		jose.Recipient{Algorithm: jose.DIRECT, Key: sharedEncryptionKey},
		(&jose.EncrypterOptions{}).WithType("JWT"),
	)
	if err != nil {
		panic(err)
	}

	cl := jwt.StandardClaims{
		Subject: "subject",
		Issuer:  "issuer",
	}
	raw, err := jwt.Encrypted(enc).Claims(cl).CompactSerialize()
	if err != nil {
		panic(err)
	}

	fmt.Println(raw)
}
Output:

func Signed

func Signed(sig jose.Signer) Builder

Signed creates builder for signed tokens.

Example
package main

import (
	"fmt"
	"time"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

func main() {
	key := []byte("secret")
	sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT"))
	if err != nil {
		panic(err)
	}

	cl := jwt.StandardClaims{
		Subject:   "subject",
		Issuer:    "issuer",
		NotBefore: jwt.NewNumericDate(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)),
		Audience:  jwt.Audience{"leela", "fry"},
	}
	raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
	if err != nil {
		panic(err)
	}

	fmt.Println(raw)
}
Output:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibGVlbGEiLCJmcnkiXSwiaXNzIjoiaXNzdWVyIiwibmJmIjoxNDUxNjA2NDAwLCJzdWIiOiJzdWJqZWN0In0.4PgCj0VO-uG_cb1mNA38NjJyp0N-NdGIDLoYelEkciw
Example (MultipleClaims)
package main

import (
	"fmt"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

var sharedKey = []byte("secret")

var signer, _ = jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: sharedKey}, &jose.SignerOptions{})

func main() {
	c := &jwt.StandardClaims{
		Subject: "subject",
		Issuer:  "issuer",
	}
	c2 := struct {
		Scopes []string
	}{
		[]string{"foo", "bar"},
	}
	raw, err := jwt.Signed(signer).Claims(c).Claims(c2).CompactSerialize()
	if err != nil {
		panic(err)
	}

	fmt.Println(raw)
}
Output:

eyJhbGciOiJIUzI1NiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sImlzcyI6Imlzc3VlciIsInN1YiI6InN1YmplY3QifQ.esKOIsmwkudr_gnfnB4SngxIr-7pspd5XzG3PImfQ6Y
Example (PrivateClaims)
package main

import (
	"fmt"
	"time"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

func main() {
	key := []byte("secret")
	sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT"))
	if err != nil {
		panic(err)
	}

	cl := jwt.StandardClaims{
		Subject:   "subject",
		Issuer:    "issuer",
		NotBefore: jwt.NewNumericDate(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)),
		Audience:  jwt.Audience{"leela", "fry"},
	}

	// When setting private claims, make sure to add struct tags
	// to specify how to serialize the field. The naming behavior
	// should match the encoding/json package otherwise.
	privateCl := struct {
		CustomClaim string `json:"custom"`
	}{
		"custom claim value",
	}

	raw, err := jwt.Signed(sig).Claims(cl).Claims(privateCl).CompactSerialize()
	if err != nil {
		panic(err)
	}

	fmt.Println(raw)
}
Output:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibGVlbGEiLCJmcnkiXSwiY3VzdG9tIjoiY3VzdG9tIGNsYWltIHZhbHVlIiwiaXNzIjoiaXNzdWVyIiwibmJmIjoxNDUxNjA2NDAwLCJzdWIiOiJzdWJqZWN0In0.knXH3ReNJToS5XI7BMCkk80ugpCup3tOy53xq-ga47o

type Claims

type Claims interface {
	Validate(e Expected) error
}

Claims represents jwt claims interface

type EncOpt

type EncOpt struct {
	ContEnc jose.ContentEncryption
	Rcpt    jose.Recipient
}

EncOpt represents encryption options

type Expected

type Expected struct {
	// Issuer matches the "iss" claim exactly.
	Issuer string
	// Subject matches the "sub" claim exactly.
	Subject string
	// Audience matches the values in "aud" claim, regardless of their order.
	Audience Audience
	// ID matches the "jti" claim exactly.
	ID string
	// Time matches the "exp", "nbf" and "iat" claims with leeway.
	Time time.Time
}

Expected defines values used for protected claims validation. If field has zero value then validation is skipped.

func (Expected) WithTime

func (e Expected) WithTime(t time.Time) Expected

WithTime copies expectations with new time.

type JSONWebToken

type JSONWebToken struct {
	Headers []jose.Header
	// contains filtered or unexported fields
}

JSONWebToken represents a JSON Web Token (as specified in RFC7519).

func ParseEncrypted

func ParseEncrypted(s string) (*JSONWebToken, error)

ParseEncrypted parses token from JWE form.

Example
package main

import (
	"fmt"

	jwt "github.com/karantin2020/jwt"
)

func main() {
	key := []byte("itsa16bytesecret")
	raw := `eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..jg45D9nmr6-8awml.z-zglLlEw9MVkYHi-Znd9bSwc-oRGbqKzf9WjXqZxno.kqji2DiZHZmh-1bLF6ARPw`
	tok, err := jwt.ParseEncrypted(raw)
	if err != nil {
		panic(err)
	}

	out := jwt.StandardClaims{}
	if err := tok.Claims(key, &out); err != nil {
		panic(err)
	}
	fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}
Output:

iss: issuer, sub: subject

func ParseSigned

func ParseSigned(s string) (*JSONWebToken, error)

ParseSigned parses token from JWS form.

Example
package main

import (
	"fmt"

	jwt "github.com/karantin2020/jwt"
)

var sharedKey = []byte("secret")

func main() {
	raw := `eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0In0.gpHyA1B1H6X4a4Edm9wo7D3X2v3aLSDBDG2_5BzXYe0`
	tok, err := jwt.ParseSigned(raw)
	if err != nil {
		panic(err)
	}

	out := jwt.StandardClaims{}
	if err := tok.Claims(sharedKey, &out); err != nil {
		panic(err)
	}
	fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}
Output:

iss: issuer, sub: subject

func (*JSONWebToken) Claims

func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error

Claims deserializes a JSONWebToken into dest using the provided key.

Example (Map)
package main

import (
	"fmt"

	jwt "github.com/karantin2020/jwt"
)

var sharedKey = []byte("secret")

func main() {
	raw := `eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0In0.gpHyA1B1H6X4a4Edm9wo7D3X2v3aLSDBDG2_5BzXYe0`
	tok, err := jwt.ParseSigned(raw)
	if err != nil {
		panic(err)
	}

	out := make(map[string]interface{})
	if err := tok.Claims(sharedKey, &out); err != nil {
		panic(err)
	}

	fmt.Printf("iss: %s, sub: %s\n", out["iss"], out["sub"])
}
Output:

iss: issuer, sub: subject
Example (Multiple)
package main

import (
	"fmt"
	"strings"

	jwt "github.com/karantin2020/jwt"
)

var sharedKey = []byte("secret")

func main() {
	raw := `eyJhbGciOiJIUzI1NiJ9.eyJTY29wZXMiOlsiZm9vIiwiYmFyIl0sImlzcyI6Imlzc3VlciIsInN1YiI6InN1YmplY3QifQ.esKOIsmwkudr_gnfnB4SngxIr-7pspd5XzG3PImfQ6Y`
	tok, err := jwt.ParseSigned(raw)
	if err != nil {
		panic(err)
	}

	out := jwt.StandardClaims{}
	out2 := struct {
		Scopes []string
	}{}
	if err := tok.Claims(sharedKey, &out, &out2); err != nil {
		panic(err)
	}
	fmt.Printf("iss: %s, sub: %s, scopes: %s\n", out.Issuer, out.Subject, strings.Join(out2.Scopes, ","))
}
Output:

iss: issuer, sub: subject, scopes: foo,bar

func (*JSONWebToken) UnsafeClaimsWithoutVerification

func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error

UnsafeClaimsWithoutVerification deserializes the claims of a JSONWebToken into the dests. For signed JWTs, the claims are not verified. This function won't work for encrypted JWTs.

type MapClaims

type MapClaims map[string]interface{}

MapClaims represents claim values in form of map[string]interface{}

func (MapClaims) Validate

func (m MapClaims) Validate(e Expected) error

Validate checks claims in a token against expected values

type NestedBuilder

type NestedBuilder interface {
	// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
	// into single JSON object. If you are passing private claims, make sure to set
	// struct field tags to specify the name for the JSON key to be used when
	// serializing.
	Claims(i interface{}) NestedBuilder
	// Token builds a NestedJSONWebToken from provided data.
	Token() (*NestedJSONWebToken, error)
	// FullSerialize serializes a token using the JSON Serialization format.
	FullSerialize() (string, error)
	// CompactSerialize serializes a token using the compact serialization format.
	CompactSerialize() (string, error)
}

NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens. Calls can be chained, and errors are accumulated until final call to CompactSerialize/FullSerialize.

func SignedAndEncrypted

func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder

SignedAndEncrypted creates builder for signed-then-encrypted tokens. ErrInvalidContentType will be returned if encrypter doesn't have JWT content type.

Example
package main

import (
	"fmt"

	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"

	jwt "github.com/karantin2020/jwt"
	jose "gopkg.in/square/go-jose.v2"
)

var sharedEncryptionKey = []byte("itsa16bytesecret")

func main() {
	enc, err := jose.NewEncrypter(
		jose.A128GCM,
		jose.Recipient{
			Algorithm: jose.DIRECT,
			Key:       sharedEncryptionKey,
		},
		(&jose.EncrypterOptions{}).WithType("JWT").WithContentType("JWT"))
	if err != nil {
		panic(err)
	}

	cl := jwt.StandardClaims{
		Subject: "subject",
		Issuer:  "issuer",
	}
	raw, err := jwt.SignedAndEncrypted(rsaSigner, enc).Claims(cl).CompactSerialize()
	if err != nil {
		panic(err)
	}

	fmt.Println(raw)
}

func mustUnmarshalRSA(data string) *rsa.PrivateKey {
	block, _ := pem.Decode([]byte(data))
	if block == nil {
		panic("failed to decode PEM data")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		panic("failed to parse RSA key: " + err.Error())
	}
	if key, ok := key.(*rsa.PrivateKey); ok {
		return key
	}
	panic("key is not of type *rsa.PrivateKey")
}

func mustMakeSigner(alg jose.SignatureAlgorithm, k interface{}) jose.Signer {
	sig, err := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: k}, nil)
	if err != nil {
		panic("failed to create signer:" + err.Error())
	}

	return sig
}

var rsaPrivKey = mustUnmarshalRSA(`-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIHBvDHAr7jh8h
xaqBCl11fjI9YZtdC5b3HtXTXZW3c2dIOImNUjffT8POP6p5OpzivmC1om7iOyuZ
3nJjC9LT3zqqs3f2i5d4mImxEuqG6uWdryFfkp0uIv5VkjVO+iQWd6pDAPGP7r1Z
foXCleyCtmyNH4JSkJneNPOk/4BxO8vcvRnCMT/Gv81IT6H+OQ6OovWOuJr8RX9t
1wuCjC9ezZxeI9ONffhiO5FMrVh5H9LJTl3dPOVa4aEcOvgd45hBmvxAyXqf8daE
6Kl2O7vQ4uwgnSTVXYIIjCjbepuersApIMGx/XPSgiU1K3Xtah/TBvep+S3VlwPc
q/QH25S9AgMBAAECggEAe+y8XKYfPw4SxY1uPB+5JSwT3ON3nbWxtjSIYy9Pqp5z
Vcx9kuFZ7JevQSk4X38m7VzM8282kC/ono+d8yy9Uayq3k/qeOqV0X9Vti1qxEbw
ECkG1/MqGApfy4qSLOjINInDDV+mOWa2KJgsKgdCwuhKbVMYGB2ozG2qfYIlfvlY
vLcBEpGWmswJHNmkcjTtGFIyJgPbsI6ndkkOeQbqQKAaadXtG1xUzH+vIvqaUl/l
AkNf+p4qhPkHsoAWXf1qu9cYa2T8T+mEo79AwlgVC6awXQWNRTiyClDJC7cu6NBy
ZHXCLFMbalzWF9qeI2OPaFX2x3IBWrbyDxcJ4TSdQQKBgQD/Fp/uQonMBh1h4Vi4
HlxZdqSOArTitXValdLFGVJ23MngTGV/St4WH6eRp4ICfPyldsfcv6MZpNwNm1Rn
lB5Gtpqpby1dsrOSfvVbY7U3vpLnd8+hJ/lT5zCYt5Eor46N6iWRkYWzNe4PixiF
z1puGUvFCbZdeeACVrPLmW3JKQKBgQDI0y9WTf8ezKPbtap4UEE6yBf49ftohVGz
p4iD6Ng1uqePwKahwoVXKOc179CjGGtW/UUBORAoKRmxdHajHq6LJgsBxpaARz21
COPy99BUyp9ER5P8vYn63lC7Cpd/K7uyMjaz1DAzYBZIeVZHIw8O9wuGNJKjRFy9
SZyD3V0ddQKBgFMdohrWH2QVEfnUnT3Q1rJn0BJdm2bLTWOosbZ7G72TD0xAWEnz
sQ1wXv88n0YER6X6YADziEdQykq8s/HT91F/KkHO8e83zP8M0xFmGaQCOoelKEgQ
aFMIX3NDTM7+9OoUwwz9Z50PE3SJFAJ1n7eEEoYvNfabQXxBl+/dHEKRAoGAPEvU
EaiXacrtg8EWrssB2sFLGU/ZrTciIbuybFCT4gXp22pvXXAHEvVP/kzDqsRhLhwb
BNP6OuSkNziNikpjA5pngZ/7fgZly54gusmW/m5bxWdsUl0iOXVYbeAvPlqGH2me
LP4Pfs1hw17S/cbT9Z1NE31jbavP4HFikeD73SUCgYEArQfuudml6ei7XZ1Emjq8
jZiD+fX6e6BD/ISatVnuyZmGj9wPFsEhY2BpLiAMQHMDIvH9nlKzsFvjkTPB86qG
jCh3D67Os8eSBk5uRC6iW3Fc4DXvB5EFS0W9/15Sl+V5vXAcrNMpYS82OTSMG2Gt
b9Ym/nxaqyTu0PxajXkKm5Q=
-----END PRIVATE KEY-----`)

var rsaSigner = mustMakeSigner(jose.RS256, rsaPrivKey)
Output:

type NestedJSONWebToken

type NestedJSONWebToken struct {
	Headers []jose.Header
	// contains filtered or unexported fields
}

NestedJSONWebToken represents a JSON Web Token (as specified in RFC7519).

func ParseSignedAndEncrypted

func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error)

ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form.

Example
package main

import (
	"fmt"

	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"

	jwt "github.com/karantin2020/jwt"
)

var sharedEncryptionKey = []byte("itsa16bytesecret")

func main() {
	raw := `eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwiY3R5IjoiSldUIn0..-keV-9YpsxotBEHw.yC9SHWgnkjykgJqXZGlzYC5Wg_EdWKO5TgfqeqsWWJYw7fX9zXQE3NtXmA3nAiUrYOr3H2s0AgTeAhTNbELLEHQu0blfRaPa_uKOAgFgmhJwbGe2iFLn9J0U72wk56318nI-pTLCV8FijoGpXvAxQlaKrPLKkl9yDQimPhb7UiDwLWYkJeoayciAXhR5f40E8ORGjCz8oawXRvjDaSjgRElUwy4kMGzvJy_difemEh4lfMSIwUNVEqJkEYaalRttSymMYuV6NvBVU0N0Jb6omdM4tW961OySB4KPWCWH9UJUX0XSEcqbW9WLxpg3ftx5R7xNiCnaVaCx_gJZfXJ9yFLqztIrKh2N05zHM0tddSOwCOnq7_1rJtaVz0nTXjSjf1RrVaxJya59p3K-e41QutiGFiJGzXG-L2OyLETIaVSU3ptvaCz4IxCF3GzeCvOgaICvXkpBY1-bv-fk1ilyjmcTDnLp2KivWIxcnoQmpN9xj06ZjagdG09AHUhS5WixADAg8mIdGcanNblALecnCWG-otjM9Kw.RZoaHtSgnzOin2od3D9tnA`
	tok, err := jwt.ParseSignedAndEncrypted(raw)
	if err != nil {
		panic(err)
	}

	nested, err := tok.Decrypt(sharedEncryptionKey)
	if err != nil {
		panic(err)
	}

	out := jwt.StandardClaims{}
	if err := nested.Claims(&rsaPrivKey.PublicKey, &out); err != nil {
		panic(err)
	}

	fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}

func mustUnmarshalRSA(data string) *rsa.PrivateKey {
	block, _ := pem.Decode([]byte(data))
	if block == nil {
		panic("failed to decode PEM data")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		panic("failed to parse RSA key: " + err.Error())
	}
	if key, ok := key.(*rsa.PrivateKey); ok {
		return key
	}
	panic("key is not of type *rsa.PrivateKey")
}

var rsaPrivKey = mustUnmarshalRSA(`-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIHBvDHAr7jh8h
xaqBCl11fjI9YZtdC5b3HtXTXZW3c2dIOImNUjffT8POP6p5OpzivmC1om7iOyuZ
3nJjC9LT3zqqs3f2i5d4mImxEuqG6uWdryFfkp0uIv5VkjVO+iQWd6pDAPGP7r1Z
foXCleyCtmyNH4JSkJneNPOk/4BxO8vcvRnCMT/Gv81IT6H+OQ6OovWOuJr8RX9t
1wuCjC9ezZxeI9ONffhiO5FMrVh5H9LJTl3dPOVa4aEcOvgd45hBmvxAyXqf8daE
6Kl2O7vQ4uwgnSTVXYIIjCjbepuersApIMGx/XPSgiU1K3Xtah/TBvep+S3VlwPc
q/QH25S9AgMBAAECggEAe+y8XKYfPw4SxY1uPB+5JSwT3ON3nbWxtjSIYy9Pqp5z
Vcx9kuFZ7JevQSk4X38m7VzM8282kC/ono+d8yy9Uayq3k/qeOqV0X9Vti1qxEbw
ECkG1/MqGApfy4qSLOjINInDDV+mOWa2KJgsKgdCwuhKbVMYGB2ozG2qfYIlfvlY
vLcBEpGWmswJHNmkcjTtGFIyJgPbsI6ndkkOeQbqQKAaadXtG1xUzH+vIvqaUl/l
AkNf+p4qhPkHsoAWXf1qu9cYa2T8T+mEo79AwlgVC6awXQWNRTiyClDJC7cu6NBy
ZHXCLFMbalzWF9qeI2OPaFX2x3IBWrbyDxcJ4TSdQQKBgQD/Fp/uQonMBh1h4Vi4
HlxZdqSOArTitXValdLFGVJ23MngTGV/St4WH6eRp4ICfPyldsfcv6MZpNwNm1Rn
lB5Gtpqpby1dsrOSfvVbY7U3vpLnd8+hJ/lT5zCYt5Eor46N6iWRkYWzNe4PixiF
z1puGUvFCbZdeeACVrPLmW3JKQKBgQDI0y9WTf8ezKPbtap4UEE6yBf49ftohVGz
p4iD6Ng1uqePwKahwoVXKOc179CjGGtW/UUBORAoKRmxdHajHq6LJgsBxpaARz21
COPy99BUyp9ER5P8vYn63lC7Cpd/K7uyMjaz1DAzYBZIeVZHIw8O9wuGNJKjRFy9
SZyD3V0ddQKBgFMdohrWH2QVEfnUnT3Q1rJn0BJdm2bLTWOosbZ7G72TD0xAWEnz
sQ1wXv88n0YER6X6YADziEdQykq8s/HT91F/KkHO8e83zP8M0xFmGaQCOoelKEgQ
aFMIX3NDTM7+9OoUwwz9Z50PE3SJFAJ1n7eEEoYvNfabQXxBl+/dHEKRAoGAPEvU
EaiXacrtg8EWrssB2sFLGU/ZrTciIbuybFCT4gXp22pvXXAHEvVP/kzDqsRhLhwb
BNP6OuSkNziNikpjA5pngZ/7fgZly54gusmW/m5bxWdsUl0iOXVYbeAvPlqGH2me
LP4Pfs1hw17S/cbT9Z1NE31jbavP4HFikeD73SUCgYEArQfuudml6ei7XZ1Emjq8
jZiD+fX6e6BD/ISatVnuyZmGj9wPFsEhY2BpLiAMQHMDIvH9nlKzsFvjkTPB86qG
jCh3D67Os8eSBk5uRC6iW3Fc4DXvB5EFS0W9/15Sl+V5vXAcrNMpYS82OTSMG2Gt
b9Ym/nxaqyTu0PxajXkKm5Q=
-----END PRIVATE KEY-----`)
Output:

iss: issuer, sub: subject

func (*NestedJSONWebToken) Decrypt

func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error)

Decrypt decrypts encrypted jwt

type NumericDate

type NumericDate int64

NumericDate represents date and time as the number of seconds since the epoch, ignoring leap seconds. Non-integer values can be represented in the serialized format, but we round to the nearest second. See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2

func NewNumericDate

func NewNumericDate(t time.Time) *NumericDate

NewNumericDate constructs NumericDate from time.Time value.

func (NumericDate) MarshalJSON

func (n NumericDate) MarshalJSON() ([]byte, error)

MarshalJSON serializes the given NumericDate into its JSON representation.

func (*NumericDate) Time

func (n *NumericDate) Time() time.Time

Time returns time.Time representation of NumericDate.

func (*NumericDate) UnmarshalJSON

func (n *NumericDate) UnmarshalJSON(b []byte) error

UnmarshalJSON reads a date from its JSON representation.

type SignOpt

type SignOpt = jose.SigningKey

SignOpt represents signing options

type StandardClaims

type StandardClaims struct {
	Issuer    string       `json:"iss,omitempty"`
	Subject   string       `json:"sub,omitempty"`
	Audience  Audience     `json:"aud,omitempty"`
	Expiry    *NumericDate `json:"exp,omitempty"`
	NotBefore *NumericDate `json:"nbf,omitempty"`
	IssuedAt  *NumericDate `json:"iat,omitempty"`
	ID        string       `json:"jti,omitempty"`
}

StandardClaims represents public claim values (as specified in RFC 7519).

func (StandardClaims) Validate

func (c StandardClaims) Validate(e Expected) error

Validate checks claims in a token against expected values. A default leeway value of one minute is used to compare time values.

The default leeway will cause the token to be deemed valid until one minute after the expiration time. If you're a server application that wants to give an extra minute to client tokens, use this function. If you're a client application wondering if the server will accept your token, use ValidateWithLeeway with a leeway <=0, otherwise this function might make you think a token is valid when it is not.

func (StandardClaims) ValidateWithLeeway

func (c StandardClaims) ValidateWithLeeway(e Expected, leeway time.Duration) error

ValidateWithLeeway checks claims in a token against expected values. A custom leeway may be specified for comparing time values. You may pass a zero value to check time values with no leeway, but you should not that numeric date values are rounded to the nearest second and sub-second precision is not supported.

The leeway gives some extra time to the token from the server's point of view. That is, if the token is expired, ValidateWithLeeway will still accept the token for 'leeway' amount of time. This fails if you're using this function to check if a server will accept your token, because it will think the token is valid even after it expires. So if you're a client validating if the token is valid to be submitted to a server, use leeway <=0, if you're a server validation a token, use leeway >=0.

Jump to

Keyboard shortcuts

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